-
Notifications
You must be signed in to change notification settings - Fork 720
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support for extracting attachments from OneNote section files
Includes rudimentary support for getting slices from FMap's and for interacting with libclamav's context structure. For now will use a Cisco-Talos org fork of the onenote_parser until the feature to read open a onenote section from a slice (instead of from a filepath) is added to the upstream.
Showing
22 changed files
with
912 additions
and
349 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
* Rust equivalent of libclamav's scanners.c module | ||
* | ||
* Copyright (C) 2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
* | ||
* Authors: Micah Snyder | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
* MA 02110-1301, USA. | ||
*/ | ||
|
||
use std::{convert::TryInto, path::PathBuf, slice}; | ||
|
||
use crate::{fmap::FMap, sys::cli_ctx, util::str_from_ptr}; | ||
|
||
/// Error enumerates all possible errors returned by this library. | ||
#[derive(thiserror::Error, Debug)] | ||
pub enum Error { | ||
#[error("Invalid format")] | ||
Format, | ||
|
||
#[error("Invalid NULL pointer: {0}")] | ||
NullPointer(&'static str), | ||
|
||
#[error("{0} parameter is NULL")] | ||
NullParam(&'static str), | ||
|
||
#[error("No more files to extract")] | ||
NoMoreFiles, | ||
|
||
#[error("Invalid FMap: {0}")] | ||
BadMap(#[from] crate::fmap::Error), | ||
|
||
#[error("String not UTF8: {0}")] | ||
Utf8(#[from] std::str::Utf8Error), | ||
} | ||
|
||
/// Get the ctx.sub_filepath as an Option<'str> | ||
/// | ||
/// # Safety | ||
/// | ||
/// Must be a valid ctx pointer. | ||
pub unsafe fn sub_filepath(ctx: *mut cli_ctx) -> Result<Option<PathBuf>, Error> { | ||
if ctx.is_null() { | ||
return Err(Error::NullPointer("ctx")); | ||
} | ||
|
||
Ok(str_from_ptr(unsafe { *ctx }.sub_filepath) | ||
.map_err(Error::Utf8)? | ||
.map(PathBuf::from)) | ||
} | ||
|
||
/// Get the ctx.target_filepath as an Option<'str> | ||
/// | ||
/// # Safety | ||
/// | ||
/// Must be a valid ctx pointer. | ||
pub unsafe fn target_filepath(ctx: *mut cli_ctx) -> Result<Option<PathBuf>, Error> { | ||
if ctx.is_null() { | ||
return Err(Error::NullPointer("ctx")); | ||
} | ||
|
||
Ok(str_from_ptr(unsafe { *ctx }.target_filepath) | ||
.map_err(Error::Utf8)? | ||
.map(PathBuf::from)) | ||
} | ||
|
||
/// Get the fmap for the current layer. | ||
/// | ||
/// # Safety | ||
/// | ||
/// Must be a valid ctx pointer. | ||
pub unsafe fn current_fmap(ctx: *mut cli_ctx) -> Result<FMap, Error> { | ||
if ctx.is_null() { | ||
return Err(Error::NullPointer("ctx")); | ||
} | ||
|
||
let recursion_stack_size = unsafe { *ctx }.recursion_stack_size as usize; | ||
let recursion_level = unsafe { *ctx }.recursion_level as usize; | ||
|
||
let recursion_stack = | ||
unsafe { slice::from_raw_parts((*ctx).recursion_stack, recursion_stack_size) }; | ||
|
||
let current_level = recursion_stack[recursion_level]; | ||
|
||
current_level.fmap.try_into().map_err(Error::BadMap) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
* Rust interface for libclamav FMap module | ||
* | ||
* Copyright (C) 2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
* | ||
* Authors: Micah Snyder | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
* MA 02110-1301, USA. | ||
*/ | ||
|
||
use std::convert::TryFrom; | ||
|
||
use log::{debug, error}; | ||
|
||
use crate::{sys, util::str_from_ptr}; | ||
|
||
/// Error enumerates all possible errors returned by this library. | ||
#[derive(thiserror::Error, Debug)] | ||
pub enum Error { | ||
#[error("Invalid parameter: {0}")] | ||
InvalidParameter(String), | ||
|
||
#[error("{0} parmeter is NULL")] | ||
NullParam(&'static str), | ||
|
||
#[error("Offset {0} and length {1} not contained in FMap of size {2}")] | ||
NotContained(usize, usize, usize), | ||
|
||
#[error("FMap pointer not initialized: {0}")] | ||
UninitializedPtr(&'static str), | ||
|
||
#[error("Attempted to create Rust FMap interface from NULL pointer")] | ||
Null, | ||
} | ||
|
||
#[derive(PartialEq, Eq, Hash, Debug)] | ||
pub struct FMap { | ||
fmap_ptr: *mut sys::cl_fmap_t, | ||
} | ||
|
||
impl TryFrom<*mut sys::cl_fmap_t> for FMap { | ||
type Error = Error; | ||
|
||
fn try_from(value: *mut sys::cl_fmap_t) -> Result<Self, Self::Error> { | ||
if value.is_null() { | ||
return Err(Error::Null); | ||
} | ||
|
||
Ok(FMap { fmap_ptr: value }) | ||
} | ||
} | ||
|
||
impl<'a> FMap { | ||
/// Simple wrapper around C FMAP module's fmap.need() method. | ||
pub fn need_off(&'a self, at: usize, len: usize) -> Result<&'a [u8], Error> { | ||
// Get the need() method function pointer from the fmap. | ||
let need_fn = match unsafe { *self.fmap_ptr }.need { | ||
Some(ptr) => ptr, | ||
None => return Err(Error::UninitializedPtr("need()")), | ||
}; | ||
|
||
let ptr: *const u8 = unsafe { need_fn(self.fmap_ptr, at, len, 1) } as *const u8; | ||
|
||
if ptr.is_null() { | ||
let fmap_size = unsafe { *self.fmap_ptr }.len; | ||
debug!( | ||
"need_off at {:?} len {:?} for fmap size {:?} returned NULL", | ||
at, len, fmap_size | ||
); | ||
return Err(Error::NotContained(at, len, fmap_size)); | ||
} | ||
|
||
let slice: &[u8] = unsafe { std::slice::from_raw_parts(ptr, len) }; | ||
|
||
Ok(slice) | ||
} | ||
|
||
pub fn len(&self) -> usize { | ||
unsafe { (*self.fmap_ptr).len } | ||
} | ||
|
||
pub fn is_empty(&self) -> bool { | ||
unsafe { (*self.fmap_ptr).len == 0 } | ||
} | ||
|
||
pub fn name(&self) -> &'static str { | ||
unsafe { | ||
str_from_ptr((*self.fmap_ptr).name) | ||
.unwrap_or(Some("<invalid-utf8>")) | ||
.unwrap_or("<unnamed>") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,285 @@ | ||
/* | ||
* Onenote document parser to extract embedded files. | ||
* | ||
* Copyright (C) 2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
* | ||
* Authors: Micah Snyder | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
* MA 02110-1301, USA. | ||
*/ | ||
|
||
use std::{ | ||
convert::TryInto, | ||
mem, panic, | ||
path::{Path, PathBuf}, | ||
}; | ||
|
||
use hex_literal::hex; | ||
use log::{debug, error}; | ||
use onenote_parser; | ||
|
||
/// Error enumerates all possible errors returned by this library. | ||
#[derive(thiserror::Error, Debug)] | ||
pub enum Error { | ||
#[error("Invalid format")] | ||
Format, | ||
|
||
#[error("Invalid parameter: {0}")] | ||
InvalidParameter(String), | ||
|
||
#[error("Failed to open file: {0}, {1}")] | ||
FailedToOpen(PathBuf, String), | ||
|
||
#[error("Failed to get size for file: {0}")] | ||
FailedToGetFileSize(PathBuf), | ||
|
||
#[error("{0} parameter is NULL")] | ||
NullParam(&'static str), | ||
|
||
#[error("No more files to extract")] | ||
NoMoreFiles, | ||
|
||
#[error("Unable to parse OneNote file")] | ||
Parse, | ||
|
||
#[error("Failed to parse OneNote file due to a panic in the onenote_parser library")] | ||
OneNoteParserPanic, | ||
} | ||
|
||
fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> { | ||
haystack | ||
.windows(needle.len()) | ||
.position(|window| window == needle) | ||
} | ||
|
||
/// Struct representing a file extracted from a OneNote document. | ||
/// This has the option of providing a file name, if one was found when extracting the file. | ||
pub struct ExtractedFile { | ||
pub name: Option<String>, | ||
pub data: Vec<u8>, | ||
} | ||
|
||
/// Struct used for a file handle for our OneNote parser. | ||
/// This struct is used to keep track of state for our iterator to work through the document extracting each file. | ||
/// There are three different ways we keep track of state depending on the file format and the way in which the file was opened. | ||
#[derive(Default)] | ||
pub struct OneNote<'a> { | ||
embedded_files: Vec<ExtractedFile>, | ||
remaining_vec: Option<Vec<u8>>, | ||
remaining: Option<&'a [u8]>, | ||
} | ||
|
||
// https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-onestore/8806fd18-6735-4874-b111-227b83eaac26 | ||
#[repr(packed)] | ||
#[allow(dead_code)] | ||
struct FileDataHeader { | ||
guid_header: [u8; 16], | ||
cb_length: u64, | ||
unused: u32, | ||
reserved: u64, | ||
} | ||
const SIZE_OF_FILE_DATA_HEADER: usize = mem::size_of::<FileDataHeader>(); | ||
|
||
// Hex sequence identifying the start of a file data store object. | ||
const FILE_DATA_STORE_OBJECT: &[u8] = &hex!("e716e3bd65261145a4c48d4d0b7a9eac"); | ||
|
||
// Hex sequence identifying the start of a OneNote file. | ||
const ONE_MAGIC: &[u8] = &hex!("e4525c7b8cd8a74daeb15378d02996d3"); | ||
|
||
impl<'a> OneNote<'a> { | ||
/// Open a OneNote document given a slice bytes. | ||
pub fn from_bytes(data: &'a [u8], filename: &Path) -> Result<OneNote<'a>, Error> { | ||
debug!( | ||
"Inspecting OneNote file for attachments from in-memory buffer of size {}-bytes named {}\n", | ||
data.len(), filename.to_string_lossy() | ||
); | ||
|
||
fn parse_section_buffer(data: &[u8], filename: &Path) -> Result<Vec<ExtractedFile>, Error> { | ||
let mut embedded_files: Vec<ExtractedFile> = vec![]; | ||
let mut parser = onenote_parser::Parser::new(); | ||
|
||
if let Ok(section) = parser.parse_section_buffer(data, filename) { | ||
// file appears to be OneStore 2.8 `.one` file. | ||
section.page_series().iter().for_each(|page_series| { | ||
page_series.pages().iter().for_each(|page| { | ||
page.contents().iter().for_each(|page_content| { | ||
if let Some(page_outline) = page_content.outline() { | ||
page_outline.items().iter().for_each(|outline_item| { | ||
outline_item.element().iter().for_each(|&outline_element| { | ||
outline_element.contents().iter().for_each(|content| { | ||
if let Some(embedded_file) = content.embedded_file() { | ||
let data = embedded_file.data(); | ||
let name = embedded_file.filename(); | ||
|
||
// If name is empty, set to None. | ||
let name = if name.is_empty() { | ||
debug!("Found unnamed attached file of size {}-bytes", data.len()); | ||
None | ||
} else { | ||
debug!("Found attached file '{}' of size {}-bytes", name, data.len()); | ||
Some(name.to_string()) | ||
}; | ||
|
||
embedded_files.push(ExtractedFile { | ||
name, | ||
data: data.to_vec(), | ||
}); | ||
} | ||
}); | ||
}); | ||
}); | ||
} | ||
}); | ||
}); | ||
}); | ||
} else { | ||
return Err(Error::Parse); | ||
} | ||
|
||
Ok(embedded_files) | ||
} | ||
|
||
// Try to parse the section buffer using the onenote_parser crate. | ||
// Attempt to catch panics in case the parser encounter unexpected issues. | ||
let result_result = panic::catch_unwind(|| -> Result<Vec<ExtractedFile>, Error> { | ||
parse_section_buffer(data, filename) | ||
}); | ||
|
||
// Check if it panicked. If no panic, grab the parse result. | ||
let result = result_result.map_err(|_| Error::OneNoteParserPanic)?; | ||
|
||
if let Ok(embedded_files) = result { | ||
// Successfully parsed the OneNote file with the onenote_parser crate. | ||
Ok(OneNote { | ||
embedded_files, | ||
..Default::default() | ||
}) | ||
} else { | ||
debug!("Unable to parse OneNote file with onenote_parser crate. Trying a different method known to work with older office 2010 OneNote files to extract attachments."); | ||
|
||
let embedded_files: Vec<ExtractedFile> = vec![]; | ||
|
||
// Verify that the OneNote document file magic is correct. | ||
// We don't check this for the onenote_parser crate because it does this for us, and may add support for newer OneNote file formats in the future. | ||
let file_magic = data.get(0..16).ok_or(Error::Format)?; | ||
if file_magic != ONE_MAGIC { | ||
return Err(Error::Format); | ||
} | ||
|
||
Ok(OneNote { | ||
embedded_files, | ||
remaining: Some(data), | ||
..Default::default() | ||
}) | ||
} | ||
} | ||
|
||
/// Open a OneNote document given the document was provided as a slice of bytes. | ||
pub fn next_file(&mut self) -> Option<ExtractedFile> { | ||
debug!("Looking to extract file from OneNote section..."); | ||
|
||
let mut file_data: Option<Vec<u8>> = None; | ||
|
||
let remaining = if let Some(remaining_in) = self.remaining { | ||
let remaining = if let Some(pos) = find_bytes(remaining_in, FILE_DATA_STORE_OBJECT) { | ||
let (_, remaining) = remaining_in.split_at(pos); | ||
// Found file data store object. | ||
remaining | ||
} else { | ||
return None; | ||
}; | ||
|
||
let data_length = if let Some(x) = remaining.get(16..20) { | ||
u32::from_le_bytes(x.try_into().unwrap()) as u64 | ||
} else { | ||
return None; | ||
}; | ||
|
||
let data: &[u8] = remaining | ||
.get(SIZE_OF_FILE_DATA_HEADER..SIZE_OF_FILE_DATA_HEADER + data_length as usize)?; | ||
|
||
file_data = Some(data.to_vec()); | ||
|
||
Some(&remaining[SIZE_OF_FILE_DATA_HEADER + (data_length as usize)..remaining.len()]) | ||
} else { | ||
None | ||
}; | ||
|
||
self.remaining = remaining; | ||
|
||
file_data.map(|data| ExtractedFile { data, name: None }) | ||
} | ||
|
||
/// Get the next file from the OneNote document using the method required for when we've read the file into a Vec. | ||
pub fn next_file_vec(&mut self) -> Option<ExtractedFile> { | ||
debug!("Looking to extract file from OneNote section..."); | ||
|
||
let mut file_data: Option<Vec<u8>> = None; | ||
|
||
self.remaining_vec = if let Some(ref remaining_vec) = self.remaining_vec { | ||
let remaining = if let Some(pos) = find_bytes(remaining_vec, FILE_DATA_STORE_OBJECT) { | ||
let (_, remaining) = remaining_vec.split_at(pos); | ||
// Found file data store object. | ||
remaining | ||
} else { | ||
return None; | ||
}; | ||
|
||
let data_length = if let Some(x) = remaining.get(16..20) { | ||
u32::from_le_bytes(x.try_into().unwrap()) as u64 | ||
} else { | ||
return None; | ||
}; | ||
|
||
let data: &[u8] = remaining | ||
.get(SIZE_OF_FILE_DATA_HEADER..SIZE_OF_FILE_DATA_HEADER + data_length as usize)?; | ||
|
||
file_data = Some(data.to_vec()); | ||
|
||
Some(Vec::from( | ||
&remaining[SIZE_OF_FILE_DATA_HEADER + (data_length as usize)..remaining.len()], | ||
)) | ||
} else { | ||
None | ||
}; | ||
|
||
file_data.map(|data| ExtractedFile { data, name: None }) | ||
} | ||
|
||
/// Get the next file from the OneNote document using the method required for the onenote_parser crate. | ||
pub fn next_file_parser(&mut self) -> Option<ExtractedFile> { | ||
self.embedded_files.pop() | ||
} | ||
} | ||
|
||
impl<'a> Iterator for OneNote<'a> { | ||
type Item = ExtractedFile; | ||
|
||
fn next(&mut self) -> Option<ExtractedFile> { | ||
// Find the next embedded file | ||
if self.remaining.is_some() { | ||
// Data stored in a slice. | ||
self.next_file() | ||
} else if self.remaining_vec.is_some() { | ||
// Data stored in a Vec. | ||
self.next_file_vec() | ||
} else if !self.embedded_files.is_empty() { | ||
// Data stored in a Vec. | ||
self.next_file_parser() | ||
} else { | ||
None | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/* | ||
* Rust equivalent of libclamav's scanners.c module | ||
* | ||
* Copyright (C) 2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
* | ||
* Authors: Micah Snyder | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
* MA 02110-1301, USA. | ||
*/ | ||
|
||
use std::{ | ||
ffi::{c_char, CString}, | ||
path::Path, | ||
ptr::null_mut, | ||
}; | ||
|
||
use libc::c_void; | ||
use log::{debug, error, warn}; | ||
|
||
use crate::{ | ||
ctx, | ||
onenote::OneNote, | ||
sys::{cl_error_t, cl_error_t_CL_ERROR, cl_error_t_CL_SUCCESS, cli_ctx, cli_magic_scan_buff}, | ||
}; | ||
|
||
/// Rust wrapper of libclamav's cli_magic_scan_buff() function. | ||
/// Use magic sigs to identify the file type and then scan it. | ||
fn magic_scan(ctx: *mut cli_ctx, buf: &[u8], name: Option<String>) -> cl_error_t { | ||
let ptr = buf.as_ptr(); | ||
let len = buf.len(); | ||
|
||
match &name { | ||
Some(name) => debug!("Scanning {}-byte file named {}.", len, name), | ||
None => debug!("Scanning {}-byte unnamed file.", len), | ||
} | ||
|
||
// Convert name to a C string. | ||
let name = match name { | ||
Some(name) => name, | ||
None => String::from(""), | ||
}; | ||
|
||
let name_ptr: *mut c_char = match CString::new(name) { | ||
Ok(name_cstr) => { | ||
// into_raw() so name_cstr doesn't get dropped and | ||
// we don't do an unsafe deref of the pointer. | ||
name_cstr.into_raw() | ||
} | ||
Err(_) => null_mut(), | ||
}; | ||
|
||
let ret = unsafe { cli_magic_scan_buff(ptr as *const c_void, len, ctx, name_ptr, 0) }; | ||
|
||
if ret != cl_error_t_CL_SUCCESS { | ||
debug!("cli_magic_scan_buff returned error: {}", ret); | ||
} | ||
|
||
// Okay now safe to drop the name CString. | ||
let _ = unsafe { CString::from_raw(name_ptr) }; | ||
|
||
ret | ||
} | ||
|
||
/// Scan a OneNote file for attachments | ||
/// | ||
/// # Safety | ||
/// | ||
/// Must be a valid ctx pointer. | ||
#[no_mangle] | ||
pub unsafe extern "C" fn scan_onenote(ctx: *mut cli_ctx) -> cl_error_t { | ||
let fmap = match ctx::current_fmap(ctx) { | ||
Ok(fmap) => fmap, | ||
Err(e) => { | ||
warn!("Error getting FMap from ctx: {e}"); | ||
return cl_error_t_CL_ERROR; | ||
} | ||
}; | ||
|
||
let file_bytes = match fmap.need_off(0, fmap.len()) { | ||
Ok(bytes) => bytes, | ||
Err(err) => { | ||
error!( | ||
"Failed to get file bytes for fmap of size {}: {err}", | ||
fmap.len() | ||
); | ||
return cl_error_t_CL_ERROR; | ||
} | ||
}; | ||
|
||
let one = match OneNote::from_bytes(file_bytes, Path::new(fmap.name())) { | ||
Ok(x) => x, | ||
Err(err) => { | ||
error!("Failed to parse OneNote file: {}", err.to_string()); | ||
return cl_error_t_CL_ERROR; | ||
} | ||
}; | ||
|
||
let mut scan_result = cl_error_t_CL_SUCCESS; | ||
|
||
one.into_iter().all(|attachment| { | ||
debug!( | ||
"Extracted {}-byte attachment with name: {:?}", | ||
attachment.data.len(), | ||
attachment.name | ||
); | ||
|
||
let ret = magic_scan(ctx, &attachment.data, attachment.name); | ||
if ret != cl_error_t_CL_SUCCESS { | ||
scan_result = ret; | ||
return false; | ||
} | ||
|
||
true | ||
}); | ||
|
||
scan_result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters