use std::{borrow::Cow, collections::HashMap};
use bon::Builder;
use capctl::CapSet;
use log::debug;
use nix::unistd::{Group, User};
use rar_common::{
database::{
actor::{DGroupType, DGroups, DUserType},
score::{CapsMin, SetgidMin, SetuidMin, TaskScore},
structs::{SCapabilities, SetBehavior},
},
util::capabilities_are_exploitable,
};
use serde::{
Deserialize,
de::{DeserializeSeed, IgnoredAny},
};
use serde_json::Value;
use crate::Cli;
pub(super) struct CredFinderDeserializerReturn<'a> {
pub(super) cli: &'a Cli,
}
#[derive(Debug, PartialEq, Eq, Default, Builder)]
pub struct CredData<'a> {
pub setuid: Option<DUserType<'a>>,
pub setgroups: Option<DGroups<'a>>,
pub caps: Option<CapSet>,
#[builder(default)]
pub extra_values: HashMap<Cow<'a, str>, Value>,
}
#[derive(Debug, PartialEq, Eq, Default, Clone, Builder)]
pub struct CredOwnedData {
pub setuid: Option<User>,
pub setgroups: Option<Vec<Group>>,
pub caps: Option<CapSet>,
#[builder(default)]
pub extra_values: HashMap<String, Value>,
}
#[derive(Debug)]
pub(super) struct CredResult<'a> {
pub(super) cred: CredData<'a>,
pub(super) score: TaskScore,
pub(super) ok: bool,
}
impl<'de: 'a, 'a> DeserializeSeed<'de> for CredFinderDeserializerReturn<'a> {
type Value = CredResult<'a>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
enum Field<'a> {
#[serde(alias = "u")]
Setuid,
#[serde(alias = "g", alias = "setgroups")]
Setgid,
#[serde(alias = "c", alias = "capabilities")]
Caps,
#[serde(untagged, borrow)]
Other(Cow<'a, str>),
}
struct CredFinderVisitor<'a> {
cli: &'a Cli,
}
fn get_caps_min(caps: CapSet) -> CapsMin {
if caps.is_empty() {
CapsMin::NoCaps
} else if caps == !CapSet::empty() {
CapsMin::CapsAll
} else if capabilities_are_exploitable(caps) {
CapsMin::CapsAdmin(caps.size())
} else {
CapsMin::CapsNoAdmin(caps.size())
}
}
impl<'de: 'a, 'a> serde::de::Visitor<'de> for CredFinderVisitor<'a> {
type Value = CredResult<'a>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("Cred structure")
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut setuid = None;
let mut setgroups = None;
let mut caps = None;
let mut score = TaskScore::default();
let mut ok = true;
let mut extra_values = HashMap::new();
while let Some(key) = map.next_key()? {
match key {
Field::Setuid => {
debug!("CredFinderVisitor: setuid");
let (user, setuser_min, user_ok) =
map.next_value_seed(SetUserDeserializerReturn { cli: self.cli })?;
setuid = user;
score.setuser_min.uid = setuser_min;
if !user_ok {
ok = false;
}
}
Field::Setgid => {
debug!("CredFinderVisitor: setgid");
let (groups, setuser_min, groups_ok) =
map.next_value_seed(SetGroupsDeserializerReturn { cli: self.cli })?;
setgroups = groups;
score.setuser_min.gid = setuser_min;
if !groups_ok {
ok = false;
}
}
Field::Caps => {
debug!("CredFinderVisitor: capabilities");
let scaps: SCapabilities = map.next_value()?;
let capset = scaps.to_capset();
score.caps_min = get_caps_min(capset);
caps = Some(capset);
}
Field::Other(n) => {
debug!("CredFinderVisitor: unknown {n}");
let v: Value = map.next_value()?;
extra_values.insert(n, v);
}
}
}
debug!("CredFinderVisitor: end");
Ok(CredResult {
cred: CredData {
setuid,
setgroups,
caps,
extra_values,
},
score,
ok,
})
}
}
const FIELDS: &[&str] = &["setuid", "setgroups", "capabilities", "0", "1", "2"];
deserializer.deserialize_struct("Cred", FIELDS, CredFinderVisitor { cli: self.cli })
}
}
struct SetGroupsDeserializerReturn<'a> {
cli: &'a Cli,
}
impl<'de: 'a, 'a> DeserializeSeed<'de> for SetGroupsDeserializerReturn<'a> {
type Value = (Option<DGroups<'a>>, Option<SetgidMin>, bool);
#[allow(clippy::too_many_lines)]
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
#[repr(u8)]
enum Field {
#[serde(alias = "d")]
Default,
#[serde(alias = "f")]
Fallback,
#[serde(alias = "a")]
Add,
#[serde(alias = "s", alias = "sub")]
Del,
}
struct SGroupsChooserVisitor<'a> {
cli: &'a Cli,
}
impl<'de: 'a, 'a> serde::de::Visitor<'de> for SGroupsChooserVisitor<'a> {
type Value = (Option<DGroups<'a>>, Option<SetgidMin>, bool);
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("SGroups structure")
}
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
debug!("SGroupsChooserVisitor: visit_borrowed_str");
let group: DGroupType<'_> = v
.parse::<u32>()
.map_or_else(|_| v.into(), std::convert::Into::into);
let score = Some(SetgidMin::from(&group));
let ok = true;
if let Some(y) = &self.cli.opt_filter.as_ref().and_then(|x| x.group.as_ref())
&& y.len() == 1
&& y[0]
!= group
.fetch_id()
.ok_or_else(|| serde::de::Error::custom("Group does not exist"))?
{
return Ok((None, None, false));
}
Ok((Some(DGroups::Single(group)), score, ok))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
debug!("SGroupsChooserVisitor: visit_str");
self.visit_string(v.to_string())
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
debug!("SGroupsChooserVisitor: visit_string");
let group: DGroupType<'_> = v
.parse::<u32>()
.map_or_else(|_| Cow::<str>::from(v).into(), std::convert::Into::into);
let score = Some(SetgidMin::from(&group));
let ok = true;
if let Some(y) = &self.cli.opt_filter.as_ref().and_then(|x| x.group.as_ref())
&& y.len() == 1
&& y[0]
!= group
.fetch_id()
.ok_or_else(|| serde::de::Error::custom("Group does not exist"))?
{
return Ok((None, None, false));
}
Ok((Some(DGroups::Single(group)), score, ok))
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
debug!("SGroupsChooserVisitor: visit_u64");
let group: DGroupType<'_> = <DGroupType<'_>>::from(
u32::try_from(v).map_err(|_| serde::de::Error::custom("Group id too large"))?,
);
let score = Some(SetgidMin::from(&group));
let ok = true;
if let Some(y) = &self.cli.opt_filter.as_ref().and_then(|x| x.group.as_ref())
&& y.len() == 1
&& y[0]
!= group
.fetch_id()
.ok_or_else(|| serde::de::Error::custom("Group does not exist"))?
{
return Ok((None, None, false));
}
Ok((Some(DGroups::Single(group)), score, ok))
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
debug!("SGroupsChooserVisitor: visit_seq");
let mut groups = None;
let mut score = None;
let mut ok = false;
let filter = self.cli.opt_filter.as_ref().and_then(|x| x.group.as_ref());
while let Some(group) = seq.next_element::<DGroups>()? {
if let Some(u) = filter {
let parsed_ids: Vec<u32> =
(&group).try_into().map_err(serde::de::Error::custom)?;
if *u == parsed_ids {
ok = true;
groups = Some(group.clone());
score.replace((&group).into());
while seq.next_element::<IgnoredAny>()?.is_some() {}
break;
}
} else {
groups = Some(group.clone());
ok = true;
score.replace((&group).into());
}
}
Ok((groups, score, ok))
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut groups = None;
let mut score = None;
let mut ok = false;
let filter = self.cli.opt_filter.as_ref().and_then(|x| x.group.as_ref());
'fields: while let Some(key) = map.next_key()? {
match key {
Field::Default => {
debug!("SGroupsChooserVisitor: default");
let default = map.next_value::<SetBehavior>()?;
if default.is_all() {
ok = true;
}
}
Field::Fallback => {
debug!("SGroupsChooserVisitor: fallback");
let value = map.next_value::<DGroups>()?;
if let Some(u) = filter {
let parsed_ids: Vec<u32> =
(&value).try_into().map_err(serde::de::Error::custom)?;
if *u == parsed_ids {
ok = true;
groups = Some(value.clone());
score.replace((&value).into());
}
} else {
groups = Some(value.clone());
ok = true;
score.replace((&value).into());
}
}
Field::Add => {
debug!("SGroupsChooserVisitor: add");
if let Some(filter) = filter {
let add = map.next_value::<Cow<'_, [DGroups]>>()?;
for group in add.iter() {
let v: Vec<u32> =
group.try_into().map_err(serde::de::Error::custom)?;
if v == *filter {
ok = true;
groups = Some(group.to_owned());
score.replace(group.into());
while map.next_entry::<IgnoredAny, IgnoredAny>()?.is_some()
{
}
break;
}
}
} else {
map.next_value::<IgnoredAny>()?;
}
}
Field::Del => {
debug!("SGroupsChooserVisitor: del");
if let Some(u) = filter {
for group in map.next_value::<Cow<'_, [DGroups]>>()?.iter() {
if let Ok(v) = TryInto::<Vec<u32>>::try_into(group) {
if v == *u {
while map
.next_entry::<IgnoredAny, IgnoredAny>()?
.is_some()
{
}
ok = false;
groups = None;
score = None;
break 'fields;
}
} else {
return Err(serde::de::Error::custom("Invalid group"));
}
}
} else {
map.next_value::<IgnoredAny>()?;
}
}
}
}
Ok((groups, score, ok))
}
}
deserializer.deserialize_any(SGroupsChooserVisitor { cli: self.cli })
}
}
struct SetUserDeserializerReturn<'a> {
cli: &'a Cli,
}
impl<'de: 'a, 'a> DeserializeSeed<'de> for SetUserDeserializerReturn<'a> {
type Value = (Option<DUserType<'a>>, Option<SetuidMin>, bool);
#[allow(clippy::too_many_lines)]
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
#[repr(u8)]
enum Field {
#[serde(alias = "d")]
Default,
#[serde(alias = "f")]
Fallback,
#[serde(alias = "a")]
Add,
#[serde(alias = "s", alias = "sub")]
Del,
}
struct SetUserVisitor<'a> {
cli: &'a Cli,
}
impl<'de: 'a, 'a> serde::de::Visitor<'de> for SetUserVisitor<'a> {
type Value = (Option<DUserType<'a>>, Option<SetuidMin>, bool);
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("SUser structure")
}
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
debug!("SetUserVisitor: visit_borrowed_str");
let user = v
.parse::<u32>()
.map_or_else(|_| DUserType::from(v), DUserType::from);
let score = Some(SetuidMin::from(&user));
let ok = true;
if let Some(y) = &self.cli.opt_filter.as_ref().and_then(|x| x.user)
&& *y
!= user
.fetch_id()
.ok_or_else(|| serde::de::Error::custom("User does not exist"))?
{
return Ok((None, None, false));
}
Ok((Some(user), score, ok))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
debug!("SetUserVisitor: visit_str");
self.visit_string(v.to_string())
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
debug!("SetUserVisitor: visit_string");
let user = v
.parse::<u32>()
.map_or_else(|_| DUserType::from(v), DUserType::from);
let score = Some(SetuidMin::from(&user));
let ok = true;
if let Some(y) = &self.cli.opt_filter.as_ref().and_then(|x| x.user)
&& *y
!= user
.fetch_id()
.ok_or_else(|| serde::de::Error::custom("User does not exist"))?
{
return Ok((None, None, false));
}
Ok((Some(user), score, ok))
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
debug!("SetUserVisitor: visit_i64");
let user = DUserType::from(
u32::try_from(v).map_err(|_| serde::de::Error::custom("User id too large"))?,
);
let score = Some(SetuidMin::from(&user));
let ok = true;
if let Some(y) = &self.cli.opt_filter.as_ref().and_then(|x| x.user)
&& *y
!= user
.fetch_id()
.ok_or_else(|| serde::de::Error::custom("User does not exist"))?
{
return Ok((None, None, false));
}
Ok((Some(user), score, ok))
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut user = None;
let mut score = None;
let mut ok = false;
let filter = self.cli.opt_filter.as_ref().and_then(|x| x.user.as_ref());
'fields: while let Some(key) = map.next_key()? {
match key {
Field::Default => {
debug!("SUserChooserVisitor: default");
let default = map.next_value::<SetBehavior>()?;
if default.is_all() {
ok = true;
}
}
Field::Fallback => {
debug!("SUserChooserVisitor: fallback");
let value = map.next_value::<DUserType>()?;
if let Some(u) = filter {
let userid = value.fetch_id().ok_or_else(|| {
serde::de::Error::custom("User does not exist")
})?;
if u == &userid {
score.replace((&value).into());
user = Some(value);
ok = true;
}
} else {
ok = true;
score.replace((&value).into());
user = Some(value);
}
}
Field::Add => {
debug!("SUserChooserVisitor: add");
if let Some(filter) = filter {
let users = map.next_value::<Cow<'_, [DUserType]>>()?;
for user_item in users.iter() {
let user_id = user_item.fetch_id().ok_or_else(|| {
serde::de::Error::custom("User does not exist")
})?;
if user_id == *filter {
ok = true;
user = Some(user_item.to_owned());
score.replace(user_item.into());
break;
}
}
} else {
map.next_value::<IgnoredAny>()?;
}
}
Field::Del => {
debug!("SUserChooserVisitor: del");
if let Some(u) = filter {
let users = map.next_value::<Cow<'_, [DUserType]>>()?;
for user_item in users.iter() {
let user_id = user_item.fetch_id().ok_or_else(|| {
serde::de::Error::custom("User does not exist")
})?;
if user_id == *u {
while map.next_entry::<IgnoredAny, IgnoredAny>()?.is_some()
{
}
score = None;
user = None;
ok = false;
break 'fields;
}
}
} else {
map.next_value::<IgnoredAny>()?;
}
}
}
}
Ok((user, score, ok))
}
}
deserializer.deserialize_any(SetUserVisitor { cli: self.cli })
}
}
#[cfg(test)]
mod test {
use crate::finder::de::tests::{get_non_root_gid, get_non_root_uid};
use super::*;
use capctl::Cap;
use rar_common::database::{
FilterMatcher,
actor::{DGroupType, SGroupType},
score::{SetgidMin, SetuidMin},
};
use test_log::test;
#[test]
fn test_setuserdeserializerreturn() {
let json =
r#"{"default": "none", "fallback": "user1", "add": ["user2"], "del": ["user3"]}"#;
let cli = Cli::builder().build();
let deserializer = SetUserDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok());
let (user, score, ok) = result.unwrap();
assert!(ok);
let user1 = DUserType::from("user1");
assert_eq!(score, Some(SetuidMin::from(&user1)));
assert_eq!(user, Some(user1));
}
#[test]
fn test_setuserdeserializerreturn_filter() {
let uid1 = get_non_root_uid(0).unwrap();
let uid2 = get_non_root_uid(1).unwrap();
let json = format!(
r#"{{"default": "none", "fallback": "root", "add": [{uid1}], "del": [{uid2}]}}"#
);
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().user(uid1).unwrap().build())
.build();
let deserializer = SetUserDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(&json));
assert!(result.is_ok());
let (user, score, ok) = result.unwrap();
assert!(ok);
let user1 = DUserType::from(uid1);
assert_eq!(score, Some(SetuidMin::from(&user1)));
assert_eq!(user, Some(user1));
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().user("root").unwrap().build())
.build();
let deserializer = SetUserDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(&json));
assert!(result.is_ok());
let (user, score, ok) = result.unwrap();
assert!(ok);
let user1 = DUserType::from("root");
assert_eq!(score, Some(SetuidMin::from(&user1)));
assert_eq!(user, Some(user1));
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().user(uid2).unwrap().build())
.build();
let deserializer = SetUserDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(&json));
assert!(result.is_ok());
let (user, score, ok) = result.unwrap();
assert!(!ok);
assert_eq!(score, None);
assert_eq!(user, None);
let json = "\"root\"";
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().user("root").unwrap().build())
.build();
let deserializer = SetUserDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok(), "Failed to deserialize: {result:?}");
let (user, score, ok) = result.unwrap();
assert!(ok);
let user1 = DUserType::from("root");
assert_eq!(score, Some(SetuidMin::from(&user1)));
assert_eq!(user, Some(user1));
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().user(uid1).unwrap().build())
.build();
let deserializer = SetUserDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok());
let (user, score, ok) = result.unwrap();
assert!(!ok);
assert_eq!(score, None);
assert_eq!(user, None);
let json = "0";
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().user(uid1).unwrap().build())
.build();
let deserializer = SetUserDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok());
let (user, score, ok) = result.unwrap();
assert!(!ok);
assert_eq!(score, None);
assert_eq!(user, None);
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().user("root").unwrap().build())
.build();
let deserializer = SetUserDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok());
let (user, score, ok) = result.unwrap();
assert!(ok);
let user1 = DUserType::from(0);
assert_eq!(score, Some(SetuidMin::from(&user1)));
assert_eq!(user, Some(user1));
}
#[test]
fn test_no_fallback() {
let json = r#"{"default": "all"}"#;
let cli = Cli::builder().build();
let deserializer = SetUserDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok());
let (user, score, ok) = result.unwrap();
assert!(ok);
assert_eq!(score, None);
assert_eq!(user, None);
}
#[test]
fn test_setgroupsdeserializerreturn() {
let json = r#"{"default": "none", "fallback": [1, 2], "add": [[3, 4]], "del": [[5, 6]]}"#;
let cli = Cli::builder().build();
let deserializer = SetGroupsDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok());
let (groups, score, ok) = result.unwrap();
assert!(ok);
let groups1 = DGroups::from(vec![1.into(), 2.into()]);
assert_eq!(score, Some((&groups1).into()));
assert_eq!(groups, Some(groups1));
}
#[test]
#[allow(clippy::too_many_lines)]
fn test_setgroupsdeserializerreturn_filter() {
let gid1 = get_non_root_gid(0).unwrap();
let gid2 = get_non_root_gid(1).unwrap();
let json = format!(
r#"{{"default": "none", "fallback": ["root"], "add": [[{gid1}]], "del": [[{gid2}]]}}"#
);
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().group("root").unwrap().build())
.build();
let deserializer = SetGroupsDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(&json));
assert!(result.is_ok());
let (groups, score, ok) = result.unwrap();
assert!(ok);
let groups1 = DGroups::Single("root".into());
assert_eq!(score, Some((&groups1).into()));
assert_eq!(groups, Some(groups1));
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().group(gid1).unwrap().build())
.build();
let deserializer = SetGroupsDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(&json));
assert!(result.is_ok());
let (groups, score, ok) = result.unwrap();
assert!(ok);
let groups1 = DGroups::Single(gid1.into());
assert_eq!(score, Some((&groups1).into()));
assert_eq!(groups, Some(groups1));
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().group(gid2).unwrap().build())
.build();
let deserializer = SetGroupsDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(&json));
assert!(result.is_ok());
let (groups, score, ok) = result.unwrap();
assert!(!ok);
assert_eq!(score, None);
assert_eq!(groups, None);
let json = "\"root\"";
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().group("root").unwrap().build())
.build();
let deserializer = SetGroupsDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok(), "Failed to deserialize: {result:?}");
let (groups, score, ok) = result.unwrap();
assert!(ok);
let groups1 = DGroups::Single("root".into());
assert_eq!(score, Some((&groups1).into()));
assert_eq!(groups, Some(groups1));
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().group(gid1).unwrap().build())
.build();
let deserializer = SetGroupsDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok());
let (groups, score, ok) = result.unwrap();
assert!(!ok);
assert_eq!(score, None);
assert_eq!(groups, None);
let json = "0";
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().group(gid1).unwrap().build())
.build();
let deserializer = SetGroupsDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok());
let (groups, score, ok) = result.unwrap();
assert!(!ok);
assert_eq!(score, None);
assert_eq!(groups, None);
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().group("root").unwrap().build())
.build();
let deserializer = SetGroupsDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok());
let (groups, score, ok) = result.unwrap();
assert!(ok);
let groups1 = DGroups::Single(0.into());
assert_eq!(score, Some((&groups1).into()));
assert_eq!(groups, Some(groups1));
let json = "[[\"root\", 1]]";
let cli = Cli::builder()
.opt_filter(
FilterMatcher::builder()
.group(vec!["root".into(), Into::<SGroupType>::into(1)])
.unwrap()
.build(),
)
.build();
let deserializer = SetGroupsDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok(), "Failed to deserialize: {result:?}");
let (groups, score, ok) = result.unwrap();
assert!(ok);
let groups1 = DGroups::from(vec!["root".into(), 1.into()]);
assert_eq!(score, Some((&groups1).into()));
assert_eq!(groups, Some(groups1));
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().build())
.build();
let deserializer = SetGroupsDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok(), "Failed to deserialize: {result:?}");
let (groups, score, ok) = result.unwrap();
assert!(ok);
let groups1 = DGroups::from(vec!["root".into(), 1.into()]);
assert_eq!(score, Some((&groups1).into()));
assert_eq!(groups, Some(groups1));
let cli = Cli::builder()
.opt_filter(FilterMatcher::builder().group(gid1).unwrap().build())
.build();
let deserializer = SetGroupsDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok());
let (groups, score, ok) = result.unwrap();
assert!(!ok);
assert_eq!(score, None);
assert_eq!(groups, None);
}
#[test]
fn test_no_fallback_groups() {
let json = r#"{"default": "all"}"#;
let cli = Cli::builder().build();
let deserializer = SetGroupsDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok());
let (groups, score, ok) = result.unwrap();
assert!(ok);
assert_eq!(score, None);
assert_eq!(groups, None);
}
#[test]
fn test_cred_deserializer() {
let json = r#"{"setuid":"root", "setgid":"root", "caps": ["CAP_SYS_ADMIN"]}"#;
let cli = Cli::builder().build();
let deserializer = CredFinderDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_ok(), "Failed to deserialize: {result:?}");
let result = result.unwrap();
assert!(result.ok);
assert_eq!(result.cred.setuid, Some("root".into()));
assert_eq!(
result.cred.setgroups,
Some(DGroups::from(vec!["root".into()]))
);
assert_eq!(
result.cred.caps,
Some(CapSet::from_iter(vec![Cap::SYS_ADMIN]))
);
assert_eq!(
result.score.setuser_min.uid,
Some(SetuidMin::from(&"root".into()))
);
assert_eq!(
result.score.setuser_min.gid,
Some(SetgidMin::from(&Into::<DGroupType<'_>>::into("root")))
);
assert_eq!(result.score.caps_min, CapsMin::CapsAdmin(1));
let uid = get_non_root_uid(0).unwrap();
let gid = get_non_root_gid(0).unwrap();
let json = format!(r#"{{"setuid":{uid}, "setgid":[[{gid}]]}}"#);
let cli = Cli::builder().build();
let deserializer = CredFinderDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(&json));
assert!(result.is_ok(), "Failed to deserialize: {result:?}");
let result = result.unwrap();
assert!(result.ok);
assert_eq!(result.cred.setuid, Some(uid.into()));
assert_eq!(result.cred.setgroups, Some(DGroups::from(vec![gid.into()])));
assert_eq!(result.cred.caps, None);
assert_eq!(
result.score.setuser_min.uid,
Some(SetuidMin::from(&uid.into()))
);
assert_eq!(
result.score.setuser_min.gid,
Some(SetgidMin::from(&Into::<DGroupType<'_>>::into(uid)))
);
assert_eq!(result.score.caps_min, CapsMin::Undefined);
let uid = get_non_root_uid(0).unwrap();
let gid = get_non_root_gid(0).unwrap();
let json = format!(r#"{{"setuid":"{uid}", "setgid":["{gid}"]}}"#);
let cli = Cli::builder().build();
let deserializer = CredFinderDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(&json));
assert!(result.is_ok(), "Failed to deserialize: {result:?}");
let result = result.unwrap();
assert!(result.ok);
assert_eq!(result.cred.setuid, Some(uid.into()));
assert_eq!(result.cred.setgroups, Some(DGroups::from(vec![gid.into()])));
assert_eq!(result.cred.caps, None);
assert_eq!(
result.score.setuser_min.uid,
Some(SetuidMin::from(&uid.into()))
);
assert_eq!(
result.score.setuser_min.gid,
Some(SetgidMin::from(&Into::<DGroupType<'_>>::into(uid)))
);
assert_eq!(result.score.caps_min, CapsMin::Undefined);
}
#[test]
fn test_cred_deserializer_invalid() {
let json = r#"{"setuid":-1, "setgid":"invalid", "caps": ["CAP_SYS_ADMIN"]}"#;
let cli = Cli::builder().build();
let deserializer = CredFinderDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_err(), "Expected error, got: {result:?}");
let json = r#"{"setuid":"invalid", "setgid":-1, "caps": ["CAP_SYS_ADMIN"]}"#;
let cli = Cli::builder().build();
let deserializer = CredFinderDeserializerReturn { cli: &cli };
let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(json));
assert!(result.is_err(), "Expected error, got: {result:?}");
}
#[test]
fn test_expecting_error() {
let seq = "[1, 2, 3]";
let float = "1.0";
let cli = Cli::builder().build();
let cred = CredFinderDeserializerReturn { cli: &cli };
let result = cred.deserialize(&mut serde_json::Deserializer::from_str(seq));
assert!(result.is_err(), "Expected error, got: {result:?}");
let setuser = SetUserDeserializerReturn { cli: &cli };
let result = setuser.deserialize(&mut serde_json::Deserializer::from_str(float));
assert!(result.is_err(), "Expected error, got: {result:?}");
let setgroups = SetGroupsDeserializerReturn { cli: &cli };
let result = setgroups.deserialize(&mut serde_json::Deserializer::from_str(float));
assert!(result.is_err(), "Expected error, got: {result:?}");
}
}