Skip to content

Commit ee7ec8f

Browse files
authored
Merge pull request #720 from arnaldo2792/fix-whippet-defaults
Fix whippet defaults and wildcard replacements
2 parents 71a9aea + 6fda99e commit ee7ec8f

File tree

5 files changed

+210
-73
lines changed

5 files changed

+210
-73
lines changed

sources/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sources/whippet/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@ zvariant = { workspace = true }
2424

2525
[build-dependencies]
2626
generate-readme.workspace = true
27+
28+
[dev-dependencies]
29+
test-case.workspace = true

sources/whippet/src/dbus_policy.rs

Lines changed: 118 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121
//! expected by dbus-broker, ensuring compatibility with the broker's policy engine.
2222
2323
use crate::error::{self, Result};
24-
use crate::policy::{Context, MessageType, Policy, Rule};
24+
use crate::policy::{Context, Policy, Rule};
2525
use serde::Serialize;
2626
use snafu::ResultExt;
2727
use zvariant::{Type, Value as ZVariantValue};
2828

29+
const DEFAULT_MAX_FDS: u64 = u64::MAX;
30+
2931
/// Top-level policy structure that matches launcher's Dbus format. It is crucial that
3032
/// the order of the fields remains like it is, otherwise the broker rejects the policy.
3133
///
@@ -52,8 +54,8 @@ pub struct PolicyBatch {
5254
pub(crate) connect_verdict: bool,
5355
pub(crate) connect_priority: u64,
5456
pub(crate) own_rules: Vec<OwnRecord>,
55-
pub(crate) send_rules: Vec<SendReceiveRecord>,
56-
pub(crate) recv_rules: Vec<SendReceiveRecord>,
57+
pub(crate) send_rules: Vec<SendRecord>,
58+
pub(crate) recv_rules: Vec<ReceiveRecord>,
5759
}
5860

5961
/// Represents an Own Record in the actual dbus policy
@@ -67,23 +69,85 @@ pub struct OwnRecord {
6769
pub name: String,
6870
}
6971

70-
/// Represents a Send Record in the actual dbus policy
72+
/// Represents a Send/Receive Record in the actual dbus policy
7173
/// See:
7274
/// https://github.com/bus1/dbus-broker/blob/b0db0890d1254477cf832e5f9f0a798360c80fd9/src/launch/policy.c#L877
73-
#[derive(Debug, Type, Serialize, Clone, ZVariantValue, Default)]
74-
pub struct SendReceiveRecord {
75-
pub verdict: bool,
76-
pub priority: u64,
77-
pub name: String,
78-
pub path: String,
79-
pub interface: String,
80-
pub member: String,
81-
pub record_type: MessageType,
82-
pub broadcast: u32,
83-
pub min_fds: u64,
84-
pub max_fds: u64,
75+
macro_rules! impl_record {
76+
($record_type:ident, $rule_variant:path, [$(($struct_field:ident, $rule_field:ident)),*]) => {
77+
#[derive(Debug, Type, Serialize, Clone, ZVariantValue)]
78+
pub struct $record_type {
79+
pub verdict: bool,
80+
pub priority: u64,
81+
pub name: String,
82+
pub path: String,
83+
pub interface: String,
84+
pub member: String,
85+
pub record_type: crate::policy::MessageType,
86+
pub broadcast: u32,
87+
pub min_fds: u64,
88+
pub max_fds: u64,
89+
}
90+
91+
impl Default for $record_type {
92+
fn default() -> Self {
93+
Self {
94+
verdict: Default::default(),
95+
priority: Default::default(),
96+
name: Default::default(),
97+
path: Default::default(),
98+
interface: Default::default(),
99+
member: Default::default(),
100+
record_type: Default::default(),
101+
broadcast: Default::default(),
102+
min_fds: Default::default(),
103+
max_fds: crate::dbus_policy::DEFAULT_MAX_FDS,
104+
}
105+
}
106+
}
107+
108+
impl TryFrom<&Rule> for $record_type {
109+
type Error = crate::error::Error;
110+
111+
fn try_from(rule: &Rule) -> Result<Self> {
112+
match rule {
113+
$rule_variant {
114+
allow,
115+
priority,
116+
$($rule_field,)*
117+
..
118+
} => Ok(Self {
119+
verdict: *allow,
120+
priority: *priority,
121+
name: rule.name()?,
122+
interface: rule.interface()?,
123+
member: rule.member()?,
124+
path: rule.path()?,
125+
$($struct_field: $rule_field.clone(),)*
126+
..Self::default()
127+
}),
128+
_ => error::RuleToRecordSnafu {
129+
rule_type: format!("{rule:?}"),
130+
record_type: stringify!($record_type).to_string(),
131+
}
132+
.fail(),
133+
}
134+
}
135+
}
136+
};
85137
}
86138

139+
impl_record!(
140+
SendRecord,
141+
Rule::Send,
142+
[(broadcast, send_broadcast), (record_type, send_type)]
143+
);
144+
145+
impl_record!(
146+
ReceiveRecord,
147+
Rule::Receive,
148+
[(broadcast, receive_broadcast), (record_type, receive_type)]
149+
);
150+
87151
impl DbusPolicy {
88152
/// Builds a new DbusPolicy object using "system" as the only supported bus_type
89153
fn new() -> Self {
@@ -221,63 +285,6 @@ impl TryFrom<&Rule> for OwnRecord {
221285
}
222286
}
223287

224-
impl TryFrom<&Rule> for SendReceiveRecord {
225-
type Error = crate::error::Error;
226-
227-
fn try_from(rule: &Rule) -> Result<Self> {
228-
match rule {
229-
Rule::Send {
230-
allow,
231-
send_destination,
232-
send_path,
233-
send_interface,
234-
send_member,
235-
send_type,
236-
send_broadcast,
237-
priority,
238-
..
239-
} => Ok(SendReceiveRecord {
240-
verdict: *allow,
241-
name: send_destination.clone(),
242-
path: send_path.clone(),
243-
interface: send_interface.clone(),
244-
member: send_member.clone(),
245-
record_type: *send_type,
246-
broadcast: *send_broadcast,
247-
priority: *priority,
248-
..SendReceiveRecord::default()
249-
}),
250-
251-
Rule::Receive {
252-
receive_sender,
253-
receive_path,
254-
receive_interface,
255-
receive_member,
256-
receive_type,
257-
receive_broadcast,
258-
allow,
259-
priority,
260-
..
261-
} => Ok(SendReceiveRecord {
262-
verdict: *allow,
263-
name: receive_sender.clone(),
264-
path: receive_path.clone(),
265-
interface: receive_interface.clone(),
266-
member: receive_member.clone(),
267-
record_type: *receive_type,
268-
broadcast: *receive_broadcast,
269-
priority: *priority,
270-
..SendReceiveRecord::default()
271-
}),
272-
_ => error::RuleToRecordSnafu {
273-
rule_type: format!("{rule:?}"),
274-
record_type: "SendRecord".to_string(),
275-
}
276-
.fail(),
277-
}
278-
}
279-
}
280-
281288
#[cfg(test)]
282289
mod tests {
283290
use super::*;
@@ -438,4 +445,42 @@ mod tests {
438445
let dbus_policy: DbusPolicy = policy.try_into().unwrap();
439446
assert_eq!(dbus_policy.uid_entries[1].1.send_rules.len(), 2);
440447
}
448+
449+
#[test]
450+
fn test_wildcards_replaced_with_empty_string() {
451+
let config_str = r#"
452+
[default]
453+
rules = [
454+
{ allow = true, send_interface = "*", send_destination = "*", send_path = "*", send_member = "*" },
455+
]
456+
"#;
457+
let mut policy: Policy = toml::from_str(config_str).unwrap();
458+
policy.set_rule_priorities(&mut 0u64);
459+
policy.prepare().unwrap();
460+
461+
let dbus_policy: DbusPolicy = policy.try_into().unwrap();
462+
assert_eq!(dbus_policy.uid_entries[0].1.send_rules[0].interface, "");
463+
assert_eq!(dbus_policy.uid_entries[0].1.send_rules[0].name, "");
464+
assert_eq!(dbus_policy.uid_entries[0].1.send_rules[0].member, "");
465+
assert_eq!(dbus_policy.uid_entries[0].1.send_rules[0].path, "");
466+
}
467+
468+
#[test]
469+
fn test_max_fds_are_always_the_default() {
470+
let config_str = r#"
471+
[default]
472+
rules = [
473+
{ allow = true, send_interface = "*", send_destination = "*", send_path = "*", send_member = "*" },
474+
]
475+
"#;
476+
let mut policy: Policy = toml::from_str(config_str).unwrap();
477+
policy.set_rule_priorities(&mut 0u64);
478+
policy.prepare().unwrap();
479+
480+
let dbus_policy: DbusPolicy = policy.try_into().unwrap();
481+
assert_eq!(
482+
dbus_policy.uid_entries[0].1.send_rules[0].max_fds,
483+
DEFAULT_MAX_FDS
484+
);
485+
}
441486
}

sources/whippet/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,7 @@ pub enum Error {
133133
uid: String,
134134
source: std::num::ParseIntError,
135135
},
136+
137+
#[snafu(display("Can't get property '{property}' from rule '{rule_type}'"))]
138+
InvalidPropertyForRule { property: String, rule_type: String },
136139
}

sources/whippet/src/policy.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,31 @@ fn find_priority_threshold(rules: &[Rule]) -> u64 {
465465
.unwrap_or_default()
466466
}
467467

468+
/// Macro to generate rule methods that extract fields and replace wildcards with empty strings
469+
/// See: https://github.com/bus1/dbus-broker/blob/e3324b3736fd40d95e7943fca6e485013d15d643/src/launch/policy.c#L97
470+
macro_rules! impl_wildcard_getter {
471+
($method_name:ident, ($($rule_type:ident => $field:ident),*)) => {
472+
pub(crate) fn $method_name(&self) -> Result<String> {
473+
match self {
474+
$(
475+
Self::$rule_type { $field, .. } => {
476+
if $field == "*" {
477+
Ok("".to_string())
478+
} else {
479+
Ok($field.to_owned())
480+
}
481+
}
482+
)*
483+
_ => error::InvalidPropertyForRuleSnafu {
484+
property: stringify!($method_name).to_string(),
485+
rule_type: format!("{self:?}"),
486+
}
487+
.fail(),
488+
}
489+
}
490+
};
491+
}
492+
468493
impl Rule {
469494
/// Sets the priority of the rule
470495
fn set_priority(&mut self, new_priority: u64) {
@@ -489,6 +514,11 @@ impl Rule {
489514
fn is_connect(&self) -> bool {
490515
matches!(self, Self::ConnectUser { .. })
491516
}
517+
518+
impl_wildcard_getter!(interface, (Send => send_interface, Receive => receive_interface));
519+
impl_wildcard_getter!(name, (Send => send_destination, Receive => receive_sender));
520+
impl_wildcard_getter!(member, (Send => send_member, Receive => receive_member));
521+
impl_wildcard_getter!(path, (Send => send_path, Receive => receive_path));
492522
}
493523

494524
impl Context {
@@ -551,6 +581,7 @@ impl UsernameResolver for PasswdUsernameResolver {
551581
#[cfg(test)]
552582
mod tests {
553583
use super::*;
584+
use test_case::test_case;
554585

555586
const ALICE_USER: u32 = 1;
556587
const BOB_USER: u32 = 2;
@@ -920,4 +951,58 @@ mod tests {
920951
clean_connect_rules(4, &mut rules);
921952
assert!(rules.is_empty());
922953
}
954+
955+
#[test_case(Rule::Send {
956+
allow: false,
957+
priority: u64::default(),
958+
send_broadcast: u32::default(),
959+
send_destination: "*".to_string(),
960+
send_interface: "*".to_string(),
961+
send_member: "*".to_string(),
962+
send_path: "*".to_string(),
963+
send_type: MessageType::default(),
964+
}; "with a send rule wildcards are replaced with empty strings")]
965+
#[test_case(Rule::Receive {
966+
allow: false,
967+
priority: u64::default(),
968+
receive_broadcast: u32::default(),
969+
receive_interface: "*".to_string(),
970+
receive_member: "*".to_string(),
971+
receive_path: "*".to_string(),
972+
receive_sender: "*".to_string(),
973+
receive_type: MessageType::default(),
974+
}; "with a receive rule wildcards are replaced with empty strings")]
975+
fn rules_with_wildcards(rule: Rule) {
976+
assert_eq!(rule.name().unwrap(), "");
977+
assert_eq!(rule.interface().unwrap(), "");
978+
assert_eq!(rule.member().unwrap(), "");
979+
assert_eq!(rule.path().unwrap(), "");
980+
}
981+
982+
#[test_case(Rule::Send {
983+
allow: false,
984+
priority: u64::default(),
985+
send_broadcast: u32::default(),
986+
send_destination: "name".to_string(),
987+
send_interface: "interface".to_string(),
988+
send_member: "member".to_string(),
989+
send_path: "path".to_string(),
990+
send_type: MessageType::default(),
991+
}; "with a send rule the original value is returned")]
992+
#[test_case(Rule::Receive {
993+
allow: false,
994+
priority: u64::default(),
995+
receive_broadcast: u32::default(),
996+
receive_interface: "interface".to_string(),
997+
receive_member: "member".to_string(),
998+
receive_path: "path".to_string(),
999+
receive_sender: "name".to_string(),
1000+
receive_type: MessageType::default(),
1001+
}; "with a receive rule the original value is returned")]
1002+
fn rules_without_wildcards(rule: Rule) {
1003+
assert_eq!(rule.name().unwrap(), "name");
1004+
assert_eq!(rule.interface().unwrap(), "interface");
1005+
assert_eq!(rule.member().unwrap(), "member");
1006+
assert_eq!(rule.path().unwrap(), "path");
1007+
}
9231008
}

0 commit comments

Comments
 (0)