use core::marker::PhantomPinned;
use core::pin::Pin;
use core::ptr::NonNull;
use std::collections::HashSet;
use crate::elf;
use crate::elf::needed_libc::NeededLibC;
use crate::errors::{Error, Result};
pub(crate) const MARKER_GOOD: char = '+';
pub(crate) const MARKER_BAD: char = '!';
pub(crate) const MARKER_MAYBE: char = '~';
pub(crate) const MARKER_UNKNOWN: char = '?';
pub(crate) const COLOR_GOOD: termcolor::Color = termcolor::Color::Green;
pub(crate) const COLOR_BAD: termcolor::Color = termcolor::Color::Red;
pub(crate) const COLOR_UNKNOWN: termcolor::Color = termcolor::Color::Yellow;
pub(crate) trait DisplayInColorTerm {
fn display_in_color_term(&self, wc: &mut dyn termcolor::WriteColor) -> Result<()>;
}
pub(crate) struct YesNoUnknownStatus {
name: &'static str,
status: Option<bool>,
yes_is_good: bool,
}
impl YesNoUnknownStatus {
pub(crate) fn new(name: &'static str, yes_or_no: bool, yes_is_good: bool) -> Self {
Self {
name,
status: Some(yes_or_no),
yes_is_good,
}
}
pub(crate) fn unknown(name: &'static str, yes_is_good: bool) -> Self {
Self {
name,
status: None,
yes_is_good,
}
}
}
impl DisplayInColorTerm for YesNoUnknownStatus {
fn display_in_color_term(&self, wc: &mut dyn termcolor::WriteColor) -> Result<()> {
let (good_color, bad_color) = if self.yes_is_good {
(COLOR_GOOD, COLOR_BAD)
} else {
(COLOR_BAD, COLOR_GOOD)
};
let (marker, color) = match self.status {
Some(true) => (MARKER_GOOD, good_color),
Some(false) => (MARKER_BAD, bad_color),
None => (MARKER_UNKNOWN, COLOR_UNKNOWN),
};
wc.set_color(termcolor::ColorSpec::new().set_fg(Some(color)))
.map_err(|r| Error::from_io1(r, "set color", "standard output stream"))?;
write!(wc, "{}{}", marker, self.name)
.map_err(|r| Error::from_io1(r, "write", "standard output stream"))?;
wc.reset()
.map_err(|r| Error::from_io1(r, "reset", "standard output stream"))
}
}
pub(crate) enum PEControlFlowGuardLevel {
Unknown,
Unsupported,
Ineffective,
Supported,
}
impl DisplayInColorTerm for PEControlFlowGuardLevel {
fn display_in_color_term(&self, wc: &mut dyn termcolor::WriteColor) -> Result<()> {
let (marker, color) = match *self {
PEControlFlowGuardLevel::Unknown => (MARKER_UNKNOWN, COLOR_UNKNOWN),
PEControlFlowGuardLevel::Unsupported => (MARKER_BAD, COLOR_BAD),
PEControlFlowGuardLevel::Ineffective => (MARKER_MAYBE, COLOR_UNKNOWN),
PEControlFlowGuardLevel::Supported => (MARKER_GOOD, COLOR_GOOD),
};
wc.set_color(termcolor::ColorSpec::new().set_fg(Some(color)))
.map_err(|r| Error::from_io1(r, "set color", "standard output stream"))?;
write!(wc, "{marker}CONTROL-FLOW-GUARD")
.map_err(|r| Error::from_io1(r, "write", "standard output stream"))?;
wc.reset()
.map_err(|r| Error::from_io1(r, "reset", "standard output stream"))
}
}
pub(crate) enum ASLRCompatibilityLevel {
Unknown,
Unsupported,
Expensive,
SupportedLowEntropyBelow2G,
SupportedLowEntropy,
SupportedBelow2G,
Supported,
}
impl DisplayInColorTerm for ASLRCompatibilityLevel {
fn display_in_color_term(&self, wc: &mut dyn termcolor::WriteColor) -> Result<()> {
let (marker, color, text) = match *self {
ASLRCompatibilityLevel::Unknown => (MARKER_UNKNOWN, COLOR_UNKNOWN, "ASLR"),
ASLRCompatibilityLevel::Unsupported => (MARKER_BAD, COLOR_BAD, "ASLR"),
ASLRCompatibilityLevel::Expensive => (MARKER_MAYBE, COLOR_UNKNOWN, "ASLR-EXPENSIVE"),
ASLRCompatibilityLevel::SupportedLowEntropyBelow2G => {
(MARKER_MAYBE, COLOR_UNKNOWN, "ASLR-LOW-ENTROPY-LT-2GB")
}
ASLRCompatibilityLevel::SupportedLowEntropy => {
(MARKER_MAYBE, COLOR_UNKNOWN, "ASLR-LOW-ENTROPY")
}
ASLRCompatibilityLevel::SupportedBelow2G => {
(MARKER_MAYBE, COLOR_UNKNOWN, "ASLR-LT-2GB")
}
ASLRCompatibilityLevel::Supported => (MARKER_GOOD, COLOR_GOOD, "ASLR"),
};
wc.set_color(termcolor::ColorSpec::new().set_fg(Some(color)))
.map_err(|r| Error::from_io1(r, "set color", "standard output stream"))?;
write!(wc, "{marker}{text}")
.map_err(|r| Error::from_io1(r, "write", "standard output stream"))?;
wc.reset()
.map_err(|r| Error::from_io1(r, "reset", "standard output stream"))
}
}
pub(crate) struct ELFFortifySourceStatus {
libc: NeededLibC,
protected_functions: HashSet<&'static str>,
unprotected_functions: HashSet<&'static str>,
_pin: PhantomPinned,
}
impl ELFFortifySourceStatus {
pub(crate) fn new(libc: NeededLibC, elf_object: &goblin::elf::Elf) -> Result<Pin<Box<Self>>> {
let mut result = Box::pin(Self {
libc,
protected_functions: HashSet::default(),
unprotected_functions: HashSet::default(),
_pin: PhantomPinned,
});
let libc_ref: &'static NeededLibC =
unsafe { NonNull::from(&result.libc).as_ptr().as_ref().unwrap() };
let (prot_fn, unprot_fn) = elf::get_libc_functions_by_protection(elf_object, libc_ref);
unsafe { Pin::get_unchecked_mut(result.as_mut()) }.protected_functions = prot_fn;
unsafe { Pin::get_unchecked_mut(result.as_mut()) }.unprotected_functions = unprot_fn;
Ok(result)
}
fn drop_pinned(mut self: Pin<&mut Self>) {
let this = Pin::as_mut(&mut self);
let this = unsafe { Pin::get_unchecked_mut(this) };
this.protected_functions.clear();
this.unprotected_functions.clear();
}
}
impl Drop for ELFFortifySourceStatus {
fn drop(&mut self) {
unsafe { Pin::new_unchecked(self) }.drop_pinned();
}
}
impl DisplayInColorTerm for Pin<Box<ELFFortifySourceStatus>> {
fn display_in_color_term(&self, wc: &mut dyn termcolor::WriteColor) -> Result<()> {
let no_protected_functions = self.protected_functions.is_empty();
let no_unprotected_functions = self.unprotected_functions.is_empty();
let (marker, color) = match (no_protected_functions, no_unprotected_functions) {
(true, true) => (MARKER_UNKNOWN, COLOR_UNKNOWN),
(true, false) => (MARKER_BAD, COLOR_BAD),
(false, true) => (MARKER_GOOD, COLOR_GOOD),
(false, false) => (MARKER_MAYBE, COLOR_UNKNOWN),
};
let set_color_err = |r| Error::from_io1(r, "set color", "standard output stream");
wc.set_color(termcolor::ColorSpec::new().set_fg(Some(color)))
.map_err(set_color_err)?;
write!(wc, "{marker}FORTIFY-SOURCE")
.map_err(|r| Error::from_io1(r, "write", "standard output stream"))?;
wc.reset()
.map_err(|r| Error::from_io1(r, "reset", "standard output stream"))?;
write!(wc, "(").map_err(|r| Error::from_io1(r, "write", "standard output stream"))?;
wc.set_color(termcolor::ColorSpec::new().set_fg(Some(COLOR_GOOD)))
.map_err(set_color_err)?;
let mut separator = "";
for &name in &self.protected_functions {
write!(wc, "{separator}{MARKER_GOOD}{name}")
.map_err(|r| Error::from_io1(r, "write", "standard output stream"))?;
separator = ",";
}
wc.set_color(termcolor::ColorSpec::new().set_fg(Some(COLOR_BAD)))
.map_err(set_color_err)?;
for &name in &self.unprotected_functions {
write!(wc, "{separator}{MARKER_BAD}{name}")
.map_err(|r| Error::from_io1(r, "write", "standard output stream"))?;
separator = ",";
}
wc.reset()
.map_err(|r| Error::from_io1(r, "reset", "standard output stream"))?;
writeln!(wc, ")")
.map_err(|r| Error::from_io1(r, "write line", "standard output stream"))?;
Ok(())
}
}