From b6f273d1968e31e85ebbcd2772603164d9a7007b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 26 Jun 2023 02:32:35 +0200 Subject: [PATCH 1/7] =?UTF-8?q?=E2=9E=95=20Add=20quick-xml=20dependency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bus config parsing requires XML. --- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + 2 files changed, 12 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index f2c7045..00f6d7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,6 +266,7 @@ dependencies = [ "hex", "nix", "ntest", + "quick-xml", "rand", "serde", "tokio", @@ -1194,6 +1195,16 @@ dependencies = [ "prost", ] +[[package]] +name = "quick-xml" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "quote" version = "1.0.29" diff --git a/Cargo.toml b/Cargo.toml index 7c9c210..142997b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ console-subscriber = { version = "0.1.8", optional = true } hex = "0.4.3" xdg-home = "1.0.0" rand = "0.8.5" +quick-xml = { version = "0.29", features = ["serialize", "overlapped-lists"] } [target.'cfg(unix)'.dependencies] nix = "0.26.0" From 9f341cb5fc049c0a2fa8e8abfbad931d598de59c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 26 Jun 2023 02:25:39 +0200 Subject: [PATCH 2/7] =?UTF-8?q?=E2=9C=A8=20Add=20bus=20config=20parsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.rs | 392 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 393 insertions(+) create mode 100644 src/config.rs diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..7948c1b --- /dev/null +++ b/src/config.rs @@ -0,0 +1,392 @@ +use std::fmt::Write; +use std::path::{Path, PathBuf}; + +use anyhow::{bail, Context}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] +#[serde(rename = "busconfig", rename_all = "snake_case")] +pub struct BusConfig { + #[serde(rename = "$value", default)] + pub elements: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum Element { + User(String), + Type(String), + Fork, + Syslog, + KeepUmask, + Listen(String), + #[serde(rename = "pidfile")] + PIDFile(String), + #[serde(rename = "includedir")] + IncludeDir(String), + #[serde(rename = "standard_session_servicedirs")] + StandardSessionServiceDirs, + #[serde(rename = "standard_system_servicedirs")] + StandardSystemServiceDirs, + #[serde(rename = "servicedir")] + ServiceDir(String), + #[serde(rename = "servicehelper")] + ServiceHelper(String), + Auth(String), + Include(Include), + Policy(Policy), + Limit(Limit), + #[serde(rename = "selinux")] + SELinux(SELinux), + #[serde(rename = "apparmor")] + AppArmor(AppArmor), +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct Include { + #[serde(rename = "$value")] + pub content: String, + #[serde(rename = "@ignore_missing", deserialize_with = "de_yes_no", default)] + pub ignore_missing: Option, + #[serde( + rename = "@if_selinux_enabled", + deserialize_with = "de_yes_no", + default + )] + pub if_selinux_enabled: Option, + #[serde( + rename = "@selinux_root_relative", + deserialize_with = "de_yes_no", + default + )] + pub selinux_root_relative: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct Policy { + #[serde(rename = "$value")] + pub rules: Vec, + #[serde(rename = "@context")] + pub context: Option, + #[serde(rename = "@user")] + pub user: Option, + #[serde(rename = "@group")] + pub group: Option, + #[serde( + rename = "@at_console", + deserialize_with = "de_yes_no_true_false", + default + )] + pub at_console: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum PolicyContext { + Default, + Mandatory, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum Rule { + #[serde(deserialize_with = "de_allow_deny")] + Allow(AllowDeny), + Deny(AllowDeny), +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct AllowDeny { + #[serde(rename = "@send_interface")] + pub send_interface: Option, + #[serde(rename = "@send_member")] + pub send_member: Option, + #[serde(rename = "@send_error")] + pub send_error: Option, + #[serde(rename = "@send_destination")] + pub send_destination: Option, + #[serde(rename = "@send_path")] + pub send_path: Option, + #[serde(rename = "@send_type")] + pub send_type: Option, + #[serde( + rename = "@send_requested_reply", + deserialize_with = "de_true_false", + default + )] + pub send_requested_reply: Option, + #[serde( + rename = "@send_broadcast", + deserialize_with = "de_true_false", + default + )] + pub send_broadcast: Option, + #[serde(rename = "@receive_interface")] + pub receive_interface: Option, + #[serde(rename = "@receive_member")] + pub receive_member: Option, + #[serde(rename = "@receive_error")] + pub receive_error: Option, + #[serde(rename = "@receive_sender")] + pub receive_sender: Option, + #[serde(rename = "@receive_path")] + pub receive_path: Option, + #[serde(rename = "@receive_type")] + pub receive_type: Option, + #[serde( + rename = "@receive_requested_reply", + deserialize_with = "de_true_false", + default + )] + pub receive_requested_reply: Option, + #[serde(rename = "@eavesdrop", deserialize_with = "de_true_false", default)] + pub eavesdrop: Option, + #[serde(rename = "@min_fds")] + pub min_fds: Option, + #[serde(rename = "@max_fds")] + pub max_fds: Option, + #[serde(rename = "@own")] + pub own: Option, + #[serde(rename = "@own_prefix")] + pub own_prefix: Option, + #[serde(rename = "@user")] + pub user: Option, + #[serde(rename = "@group")] + pub group: Option, + #[serde(rename = "@log", deserialize_with = "de_true_false", default)] + pub log: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct Limit { + #[serde(rename = "$value", deserialize_with = "de_limit")] + pub value: usize, + #[serde(rename = "@name")] + pub name: String, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct SELinux { + #[serde(rename = "$value")] + pub associates: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(rename = "associate")] +pub struct Associate { + #[serde(rename = "@own")] + pub own: String, + #[serde(rename = "@context")] + pub context: String, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct AppArmor { + mode: AppArmorMode, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum AppArmorMode { + Required, + Enabled, + Diabled, +} + +impl BusConfig { + /// Write the XML document to writer. + pub fn to_writer(&self, writer: W) -> Result<(), anyhow::Error> { + Ok(quick_xml::se::to_writer(writer, self)?) + } + + /// Read a config from the filesystem, with support. + pub fn read

(path: P) -> Result + where + P: AsRef, + { + use std::collections::HashSet; + + fn join_paths

(parent: Option<&Path>, path: P) -> PathBuf + where + P: AsRef, + { + let path = path.as_ref(); + match parent { + Some(p) => p.join(path), + None => path.to_path_buf(), + } + } + + pub fn read_visited

( + path: P, + visited: &mut HashSet, + ) -> Result + where + P: AsRef, + { + let path: &Path = path.as_ref(); + if !visited.insert(path.to_owned()) { + bail!("File {} visited recursively", path.display()); + } + let file = std::fs::File::open(path) + .with_context(|| format!("Failed to read from {}", path.display()))?; + let reader = std::io::BufReader::new(file); + let parent = path.parent(); + let config: BusConfig = quick_xml::de::from_reader(reader)?; + let mut elements = Vec::new(); + for e in config.elements { + match e { + Element::Include(inc) => { + if inc.if_selinux_enabled.unwrap_or(false) { + // FIXME: add selinux support + continue; + } + + let p = join_paths(parent, inc.content); + match read_visited(p, visited) { + Ok(mut config) => { + elements.append(&mut config.elements); + } + Err(e) => { + if let (Some(true), Some(e)) = + (inc.ignore_missing, e.downcast_ref::()) + { + if e.kind() == std::io::ErrorKind::NotFound { + continue; + } + } + return Err(e); + } + } + } + Element::IncludeDir(inc) => { + let path = join_paths(parent, inc); + let Ok(entries) = std::fs::read_dir(path) else { + continue; + }; + for entry in entries { + let mut config = read_visited(entry?.path(), visited)?; + elements.append(&mut config.elements); + } + } + _ => elements.push(e), + } + } + Ok(BusConfig { elements }) + } + + let mut visited = HashSet::new(); + read_visited(path, &mut visited) + } +} + +impl<'a> TryFrom<&'a str> for BusConfig { + type Error = anyhow::Error; + + fn try_from(s: &'a str) -> Result { + Ok(quick_xml::de::from_str(s)?) + } +} + +impl TryFrom<&BusConfig> for String { + type Error = anyhow::Error; + + fn try_from(c: &BusConfig) -> Result { + let mut writer = String::new(); + c.to_writer(&mut writer)?; + Ok(writer) + } +} + +fn de_limit<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let v = usize::deserialize(deserializer)?; + if v > i32::MAX as _ { + Err(serde::de::Error::custom("Limit is > i32::MAX")) + } else { + Ok(v) + } +} + +fn de_allow_deny<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let v = AllowDeny::deserialize(deserializer)?; + let has_send = v.send_interface.is_some() + || v.send_member.is_some() + || v.send_error.is_some() + || v.send_destination.is_some() + || v.send_path.is_some() + || v.send_type.is_some() + || v.send_requested_reply.is_some() + || v.send_broadcast.is_some(); + + let has_recv = v.receive_interface.is_some() + || v.receive_member.is_some() + || v.receive_error.is_some() + || v.receive_sender.is_some() + || v.receive_path.is_some() + || v.receive_type.is_some() + || v.receive_requested_reply.is_some(); + + if !has_send + && !has_recv + && v.eavesdrop.is_none() + && v.min_fds.is_none() + && v.max_fds.is_none() + && v.own.is_none() + && v.own_prefix.is_none() + && v.user.is_none() + && v.group.is_none() + && v.log.is_none() + { + return Err(serde::de::Error::custom("No limit attribute set")); + } + + if has_send && has_recv { + return Err(serde::de::Error::custom( + "send and receive attributes cannot be combined", + )); + } + + Ok(v) +} + +fn de_yes_no<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let v = String::deserialize(deserializer)?; + match v.as_str() { + "yes" => Ok(Some(true)), + "no" => Ok(Some(false)), + _ => Err(serde::de::Error::custom("Invalid boolean")), + } +} + +fn de_true_false<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let v = String::deserialize(deserializer)?; + match v.as_str() { + "true" => Ok(Some(true)), + "false" => Ok(Some(false)), + _ => Err(serde::de::Error::custom("Invalid boolean")), + } +} + +fn de_yes_no_true_false<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let v = String::deserialize(deserializer)?; + match v.as_str() { + "yes" | "true" => Ok(Some(true)), + "no" | "false" => Ok(Some(false)), + _ => Err(serde::de::Error::custom("Invalid boolean")), + } +} diff --git a/src/lib.rs b/src/lib.rs index ace8594..8a4564c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod bus; +pub mod config; pub mod fdo; pub mod name_registry; pub mod peer; From 29f371d554f0f91b7a97f31e6d7d34d7acdf015f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 4 Jul 2023 15:46:19 +0200 Subject: [PATCH 3/7] =?UTF-8?q?=E2=9C=A8=20config:=20add=20a=20few=20acces?= =?UTF-8?q?sors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.rs | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/src/config.rs b/src/config.rs index 7948c1b..fe6411d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -11,6 +11,164 @@ pub struct BusConfig { pub elements: Vec, } +impl BusConfig { + /// The well-known type of the message bus. Currently known values are + /// "system" and "session". + /// + /// Note: the last wins. + pub fn r#type(&self) -> Option<&str> { + self.elements.iter().rev().find_map(|e| match e { + Element::Type(e) => Some(e.as_str()), + _ => None, + }) + } + + /// The user account the daemon should run as, as either a username or a + /// UID. + /// + /// Note: the last wins. + pub fn user(&self) -> Option<&str> { + self.elements.iter().rev().find_map(|e| match e { + Element::User(e) => Some(e.as_str()), + _ => None, + }) + } + + /// The bus daemon becomes a real daemon (forks into the background, etc.) + pub fn fork(&self) -> bool { + self.elements.iter().any(|e| matches!(e, Element::Fork)) + } + + /// The bus daemon keeps its original umask when forking. + pub fn keep_umask(&self) -> bool { + self.elements + .iter() + .any(|e| matches!(e, Element::KeepUmask)) + } + + /// The bus daemon will log to syslog. + pub fn syslog(&self) -> bool { + self.elements.iter().any(|e| matches!(e, Element::Syslog)) + } + + /// The bus daemon will write its pid to the specified file. + /// + /// Note: the last wins. + pub fn pidfile(&self) -> Option<&str> { + self.elements.iter().rev().find_map(|e| match e { + Element::PIDFile(e) => Some(e.as_str()), + _ => None, + }) + } + + /// Addresses that the bus should listen on. + pub fn listen_addresses(&self) -> Vec<&str> { + self.elements + .iter() + .filter_map(|e| match e { + Element::Listen(e) => Some(e.as_str()), + _ => None, + }) + .collect() + } + + /// Add an address that the bus should listen on. + pub fn add_listen_address(&mut self, address: String) { + self.elements.push(Element::Listen(address)); + } + + /// Permitted authorization mechanisms. + pub fn auths(&self) -> Vec<&str> { + self.elements + .iter() + .filter_map(|e| match e { + Element::Auth(e) => Some(e.as_str()), + _ => None, + }) + .collect() + } + + /// Directory to search for .service files. + pub fn service_dirs(&self) -> Vec<&str> { + self.elements + .iter() + .filter_map(|e| match e { + Element::ServiceDir(e) => Some(e.as_str()), + _ => None, + }) + .collect() + } + + /// Requests a standard set of session service directories. + pub fn standard_session_service_dirs(&self) -> bool { + self.elements + .iter() + .any(|e| matches!(e, Element::StandardSessionServiceDirs)) + } + + /// Requests a standard set of system service directories. + pub fn standard_system_service_dirs(&self) -> bool { + self.elements + .iter() + .any(|e| matches!(e, Element::StandardSystemServiceDirs)) + } + + /// Specifies the setuid helper that is used to launch system daemons with + /// an alternate user. + /// + /// Note: the last wins. + pub fn service_helper(&self) -> Option<&str> { + self.elements.iter().rev().find_map(|e| match e { + Element::ServiceHelper(e) => Some(e.as_str()), + _ => None, + }) + } + + /// Resource limits. + pub fn limits(&self) -> Vec<&Limit> { + self.elements + .iter() + .filter_map(|e| match e { + Element::Limit(e) => Some(e), + _ => None, + }) + .collect() + } + + /// Security policies. + pub fn policies(&self) -> Vec<&Policy> { + self.elements + .iter() + .filter_map(|e| match e { + Element::Policy(e) => Some(e), + _ => None, + }) + .collect() + } + + /// SELinux settings. + pub fn selinux(&self) -> Vec<&SELinux> { + self.elements + .iter() + .filter_map(|e| match e { + Element::SELinux(e) => Some(e), + _ => None, + }) + .collect() + } + + /// AppArmor settings. + pub fn apparmor(&self) -> Vec<&AppArmor> { + self.elements + .iter() + .filter_map(|e| match e { + Element::AppArmor(e) => Some(e), + _ => None, + }) + .collect() + } +} + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] #[serde(rename_all = "snake_case")] pub enum Element { From 9dd7dcdc6c4c785349b2210e5536c5e394a8887b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 26 Jun 2023 02:35:00 +0200 Subject: [PATCH 4/7] =?UTF-8?q?=E2=9C=85=20tests:=20add=20config=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests are from dbus reference implementation. The schema/dtd is a based on a mix of what is accepted by dbus and dbus-broker. Some tests are left with FIXME, as they may not fit in the config parser itself, but rather in the runtime handling of options. dbus has extra tests which may require installation to run (*.conf.in). --- tests/config.rs | 300 ++++++++++++++++++ .../apparmor-bad-attribute.conf | 4 + .../apparmor-bad-mode.conf | 4 + .../invalid-config-files/bad-attribute-2.conf | 3 + .../invalid-config-files/bad-attribute.conf | 4 + .../invalid-config-files/bad-element.conf | 4 + .../data/invalid-config-files/bad-limit.conf | 4 + .../invalid-config-files/badselinux-1.conf | 10 + .../invalid-config-files/badselinux-2.conf | 10 + .../data/invalid-config-files/circular-1.conf | 4 + .../data/invalid-config-files/circular-2.conf | 4 + .../data/invalid-config-files/circular-3.conf | 4 + .../double-attribute.conf | 4 + .../invalid-config-files/impossible-send.conf | 6 + .../invalid-config-files/limit-no-name.conf | 4 + .../invalid-config-files/ludicrous-limit.conf | 5 + .../invalid-config-files/negative-limit.conf | 4 + .../non-numeric-limit.conf | 4 + .../invalid-config-files/not-well-formed.conf | 5 + .../policy-bad-at-console.conf | 4 + .../policy-bad-attribute.conf | 4 + .../policy-bad-context.conf | 4 + .../policy-bad-rule-attribute.conf | 6 + .../policy-contradiction.conf | 4 + .../policy-member-no-path.conf | 6 + .../invalid-config-files/policy-mixed.conf | 6 + .../policy-no-attributes.conf | 4 + .../policy-no-rule-attribute.conf | 6 + .../send-and-receive.conf | 6 + .../invalid-config-files/truncated-file.conf | 9 + .../invalid-config-files/unknown-limit.conf | 4 + tests/data/valid-config-files/basic.conf | 32 ++ .../valid-config-files/basic.d/basic.conf | 13 + .../valid-config-files/check-own-rules.conf | 14 + tests/data/valid-config-files/entities.conf | 14 + .../listen-unix-runtime.conf | 11 + tests/data/valid-config-files/many-rules.conf | 57 ++++ tests/data/valid-config-files/minimal.conf | 3 + tests/data/valid-config-files/session.conf | 80 +++++ .../standard-session-dirs.conf | 8 + 40 files changed, 682 insertions(+) create mode 100644 tests/config.rs create mode 100644 tests/data/invalid-config-files/apparmor-bad-attribute.conf create mode 100644 tests/data/invalid-config-files/apparmor-bad-mode.conf create mode 100644 tests/data/invalid-config-files/bad-attribute-2.conf create mode 100644 tests/data/invalid-config-files/bad-attribute.conf create mode 100644 tests/data/invalid-config-files/bad-element.conf create mode 100644 tests/data/invalid-config-files/bad-limit.conf create mode 100644 tests/data/invalid-config-files/badselinux-1.conf create mode 100644 tests/data/invalid-config-files/badselinux-2.conf create mode 100644 tests/data/invalid-config-files/circular-1.conf create mode 100644 tests/data/invalid-config-files/circular-2.conf create mode 100644 tests/data/invalid-config-files/circular-3.conf create mode 100644 tests/data/invalid-config-files/double-attribute.conf create mode 100644 tests/data/invalid-config-files/impossible-send.conf create mode 100644 tests/data/invalid-config-files/limit-no-name.conf create mode 100644 tests/data/invalid-config-files/ludicrous-limit.conf create mode 100644 tests/data/invalid-config-files/negative-limit.conf create mode 100644 tests/data/invalid-config-files/non-numeric-limit.conf create mode 100644 tests/data/invalid-config-files/not-well-formed.conf create mode 100644 tests/data/invalid-config-files/policy-bad-at-console.conf create mode 100644 tests/data/invalid-config-files/policy-bad-attribute.conf create mode 100644 tests/data/invalid-config-files/policy-bad-context.conf create mode 100644 tests/data/invalid-config-files/policy-bad-rule-attribute.conf create mode 100644 tests/data/invalid-config-files/policy-contradiction.conf create mode 100644 tests/data/invalid-config-files/policy-member-no-path.conf create mode 100644 tests/data/invalid-config-files/policy-mixed.conf create mode 100644 tests/data/invalid-config-files/policy-no-attributes.conf create mode 100644 tests/data/invalid-config-files/policy-no-rule-attribute.conf create mode 100644 tests/data/invalid-config-files/send-and-receive.conf create mode 100644 tests/data/invalid-config-files/truncated-file.conf create mode 100644 tests/data/invalid-config-files/unknown-limit.conf create mode 100644 tests/data/valid-config-files/basic.conf create mode 100644 tests/data/valid-config-files/basic.d/basic.conf create mode 100644 tests/data/valid-config-files/check-own-rules.conf create mode 100644 tests/data/valid-config-files/entities.conf create mode 100644 tests/data/valid-config-files/listen-unix-runtime.conf create mode 100644 tests/data/valid-config-files/many-rules.conf create mode 100644 tests/data/valid-config-files/minimal.conf create mode 100644 tests/data/valid-config-files/session.conf create mode 100644 tests/data/valid-config-files/standard-session-dirs.conf diff --git a/tests/config.rs b/tests/config.rs new file mode 100644 index 0000000..32b4cda --- /dev/null +++ b/tests/config.rs @@ -0,0 +1,300 @@ +use busd::config::{BusConfig, Element}; +use std::error::Error; +use std::path::PathBuf; + +#[test] +fn test_se() -> Result<(), Box> { + let mut elements = vec![]; + elements.push(Element::User("foo".into())); + let c = BusConfig { elements }; + let _config = String::try_from(&c); + Ok(()) +} + +#[test] +fn test_de() -> Result<(), Box> { + let input = r##" + + + foo + unix:path=/foo/bar + unix:path=/foo/bar2 + +"##; + let _config = BusConfig::try_from(input)?; + Ok(()) +} + +#[test] +fn valid_basic() -> Result<(), Box> { + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests/data/valid-config-files/basic.conf"); + let config = BusConfig::read(path)?; + let n_listen = config + .elements + .iter() + .filter(|e| matches!(e, Element::Listen(_))) + .count(); + assert_eq!(n_listen, 4); + Ok(()) +} + +#[test] +fn valid_check_own_rules() -> Result<(), Box> { + let input = include_str!("data/valid-config-files/check-own-rules.conf"); + let _config = BusConfig::try_from(input)?; + Ok(()) +} + +#[test] +fn valid_entities() -> Result<(), Box> { + let input = include_str!("data/valid-config-files/entities.conf"); + let _config = BusConfig::try_from(input)?; + Ok(()) +} + +#[test] +fn valid_listen_unix_runtime() -> Result<(), Box> { + let input = include_str!("data/valid-config-files/listen-unix-runtime.conf"); + let _config = BusConfig::try_from(input)?; + Ok(()) +} + +#[test] +fn valid_many_rules() -> Result<(), Box> { + let input = include_str!("data/valid-config-files/many-rules.conf"); + let _config = BusConfig::try_from(input)?; + Ok(()) +} + +#[test] +fn valid_minimal() -> Result<(), Box> { + let input = include_str!("data/valid-config-files/minimal.conf"); + let _config = BusConfig::try_from(input)?; + Ok(()) +} + +#[test] +fn valid_session() -> Result<(), Box> { + let input = include_str!("data/valid-config-files/session.conf"); + let _config = BusConfig::try_from(input)?; + Ok(()) +} + +#[test] +fn valid_standard_session_dirs() -> Result<(), Box> { + let input = include_str!("data/valid-config-files/standard-session-dirs.conf"); + let _config = BusConfig::try_from(input)?; + Ok(()) +} + +#[test] +fn invalid_apparmor_bad_attribute() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/apparmor-bad-attribute.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_bad_attribute() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/bad-attribute.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +// FIXME: how to #[serde(deny_unknown_fields)] +#[test] +#[ignore] +fn invalid_bad_attribute_2() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/bad-attribute-2.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_bad_element() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/bad-element.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_bad_limit() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/bad-limit.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_badselinux_1() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/badselinux-1.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_badselinux_2() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/badselinux-2.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_circular_1() -> Result<(), Box> { + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests/data/invalid-config-files/circular-1.conf"); + assert!(BusConfig::read(path).is_err()); + Ok(()) +} + +#[test] +fn invalid_circular_2() -> Result<(), Box> { + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("tests/data/invalid-config-files/circular-2.conf"); + assert!(BusConfig::read(path).is_err()); + Ok(()) +} + +#[test] +fn invalid_double_attribute() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/double-attribute.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +// FIXME: needs some semantic knowledge? +#[ignore] +#[test] +fn invalid_impossible_send() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/impossible-send.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_limit_no_name() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/limit-no-name.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_ludicrous_limit() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/ludicrous-limit.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_negative_limit() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/negative-limit.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_non_numeric_limit() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/non-numeric-limit.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_not_well_formed() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/not-well-formed.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_policy_bad_at_console() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/policy-bad-at-console.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_policy_bad_attribute() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/policy-bad-attribute.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_policy_bad_context() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/policy-bad-context.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +// FIXME: how to #[serde(deny_unknown_fields)] +#[ignore] +#[test] +fn invalid_policy_bad_rule_attribute() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/policy-bad-rule-attribute.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_policy_contradiction() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/policy-contradiction.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +// FIXME: needs some semantic knowledge? +#[ignore] +#[test] +fn invalid_policy_member_no_path() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/policy-member-no-path.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +// FIXME: needs some semantic knowledge? +#[ignore] +#[test] +fn invalid_policy_mixed() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/policy-mixed.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_policy_no_attributes() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/policy-no-attributes.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_policy_no_rule_attribute() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/policy-no-rule-attribute.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_send_and_receive() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/send-and-receive.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +#[test] +fn invalid_truncated_file() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/truncated-file.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} + +// FIXME: needs some semantic knowledge? +#[ignore] +#[test] +fn invalid_unknown_limit() -> Result<(), Box> { + let input = include_str!("data/invalid-config-files/unknown-limit.conf"); + assert!(BusConfig::try_from(input).is_err()); + Ok(()) +} diff --git a/tests/data/invalid-config-files/apparmor-bad-attribute.conf b/tests/data/invalid-config-files/apparmor-bad-attribute.conf new file mode 100644 index 0000000..16a10e8 --- /dev/null +++ b/tests/data/invalid-config-files/apparmor-bad-attribute.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + + diff --git a/tests/data/invalid-config-files/apparmor-bad-mode.conf b/tests/data/invalid-config-files/apparmor-bad-mode.conf new file mode 100644 index 0000000..2398ee5 --- /dev/null +++ b/tests/data/invalid-config-files/apparmor-bad-mode.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + + diff --git a/tests/data/invalid-config-files/bad-attribute-2.conf b/tests/data/invalid-config-files/bad-attribute-2.conf new file mode 100644 index 0000000..cc430f5 --- /dev/null +++ b/tests/data/invalid-config-files/bad-attribute-2.conf @@ -0,0 +1,3 @@ + + tcp:port=1234 + diff --git a/tests/data/invalid-config-files/bad-attribute.conf b/tests/data/invalid-config-files/bad-attribute.conf new file mode 100644 index 0000000..993dd3c --- /dev/null +++ b/tests/data/invalid-config-files/bad-attribute.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + 5000 + diff --git a/tests/data/invalid-config-files/bad-element.conf b/tests/data/invalid-config-files/bad-element.conf new file mode 100644 index 0000000..77067eb --- /dev/null +++ b/tests/data/invalid-config-files/bad-element.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + + diff --git a/tests/data/invalid-config-files/bad-limit.conf b/tests/data/invalid-config-files/bad-limit.conf new file mode 100644 index 0000000..9b7b317 --- /dev/null +++ b/tests/data/invalid-config-files/bad-limit.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + as many as you like + diff --git a/tests/data/invalid-config-files/badselinux-1.conf b/tests/data/invalid-config-files/badselinux-1.conf new file mode 100644 index 0000000..4852ded --- /dev/null +++ b/tests/data/invalid-config-files/badselinux-1.conf @@ -0,0 +1,10 @@ + + + mybususer + unix:path=/foo/bar + tcp:port=1234 + basic.d + /usr/share/foo + blah + diff --git a/tests/data/invalid-config-files/badselinux-2.conf b/tests/data/invalid-config-files/badselinux-2.conf new file mode 100644 index 0000000..ac3b95c --- /dev/null +++ b/tests/data/invalid-config-files/badselinux-2.conf @@ -0,0 +1,10 @@ + + + mybususer + unix:path=/foo/bar + tcp:port=1234 + basic.d + /usr/share/foo + blah + diff --git a/tests/data/invalid-config-files/circular-1.conf b/tests/data/invalid-config-files/circular-1.conf new file mode 100644 index 0000000..faa895a --- /dev/null +++ b/tests/data/invalid-config-files/circular-1.conf @@ -0,0 +1,4 @@ + + +circular-1.conf + \ No newline at end of file diff --git a/tests/data/invalid-config-files/circular-2.conf b/tests/data/invalid-config-files/circular-2.conf new file mode 100644 index 0000000..46a7e78 --- /dev/null +++ b/tests/data/invalid-config-files/circular-2.conf @@ -0,0 +1,4 @@ + + +circular-3.conf + \ No newline at end of file diff --git a/tests/data/invalid-config-files/circular-3.conf b/tests/data/invalid-config-files/circular-3.conf new file mode 100644 index 0000000..87e354d --- /dev/null +++ b/tests/data/invalid-config-files/circular-3.conf @@ -0,0 +1,4 @@ + + +circular-2.conf + \ No newline at end of file diff --git a/tests/data/invalid-config-files/double-attribute.conf b/tests/data/invalid-config-files/double-attribute.conf new file mode 100644 index 0000000..514fda4 --- /dev/null +++ b/tests/data/invalid-config-files/double-attribute.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + 5000 + diff --git a/tests/data/invalid-config-files/impossible-send.conf b/tests/data/invalid-config-files/impossible-send.conf new file mode 100644 index 0000000..136d747 --- /dev/null +++ b/tests/data/invalid-config-files/impossible-send.conf @@ -0,0 +1,6 @@ + + unix:path=/foo + + + + diff --git a/tests/data/invalid-config-files/limit-no-name.conf b/tests/data/invalid-config-files/limit-no-name.conf new file mode 100644 index 0000000..390a4af --- /dev/null +++ b/tests/data/invalid-config-files/limit-no-name.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + + diff --git a/tests/data/invalid-config-files/ludicrous-limit.conf b/tests/data/invalid-config-files/ludicrous-limit.conf new file mode 100644 index 0000000..d78f0b5 --- /dev/null +++ b/tests/data/invalid-config-files/ludicrous-limit.conf @@ -0,0 +1,5 @@ + + tcp:port=1234 + + 3123123123 + diff --git a/tests/data/invalid-config-files/negative-limit.conf b/tests/data/invalid-config-files/negative-limit.conf new file mode 100644 index 0000000..de10d1a --- /dev/null +++ b/tests/data/invalid-config-files/negative-limit.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + -1 + diff --git a/tests/data/invalid-config-files/non-numeric-limit.conf b/tests/data/invalid-config-files/non-numeric-limit.conf new file mode 100644 index 0000000..99b4429 --- /dev/null +++ b/tests/data/invalid-config-files/non-numeric-limit.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + bees + diff --git a/tests/data/invalid-config-files/not-well-formed.conf b/tests/data/invalid-config-files/not-well-formed.conf new file mode 100644 index 0000000..9e59cbc --- /dev/null +++ b/tests/data/invalid-config-files/not-well-formed.conf @@ -0,0 +1,5 @@ + + + mybususer + diff --git a/tests/data/invalid-config-files/policy-bad-at-console.conf b/tests/data/invalid-config-files/policy-bad-at-console.conf new file mode 100644 index 0000000..1994cb9 --- /dev/null +++ b/tests/data/invalid-config-files/policy-bad-at-console.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + + diff --git a/tests/data/invalid-config-files/policy-bad-attribute.conf b/tests/data/invalid-config-files/policy-bad-attribute.conf new file mode 100644 index 0000000..03baeb0 --- /dev/null +++ b/tests/data/invalid-config-files/policy-bad-attribute.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + + diff --git a/tests/data/invalid-config-files/policy-bad-context.conf b/tests/data/invalid-config-files/policy-bad-context.conf new file mode 100644 index 0000000..18ac25e --- /dev/null +++ b/tests/data/invalid-config-files/policy-bad-context.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + + diff --git a/tests/data/invalid-config-files/policy-bad-rule-attribute.conf b/tests/data/invalid-config-files/policy-bad-rule-attribute.conf new file mode 100644 index 0000000..b28a7b3 --- /dev/null +++ b/tests/data/invalid-config-files/policy-bad-rule-attribute.conf @@ -0,0 +1,6 @@ + + tcp:port=1234 + + + + diff --git a/tests/data/invalid-config-files/policy-contradiction.conf b/tests/data/invalid-config-files/policy-contradiction.conf new file mode 100644 index 0000000..88bd73f --- /dev/null +++ b/tests/data/invalid-config-files/policy-contradiction.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + + diff --git a/tests/data/invalid-config-files/policy-member-no-path.conf b/tests/data/invalid-config-files/policy-member-no-path.conf new file mode 100644 index 0000000..9e6c89f --- /dev/null +++ b/tests/data/invalid-config-files/policy-member-no-path.conf @@ -0,0 +1,6 @@ + + tcp:port=1234 + + + + diff --git a/tests/data/invalid-config-files/policy-mixed.conf b/tests/data/invalid-config-files/policy-mixed.conf new file mode 100644 index 0000000..5549f23 --- /dev/null +++ b/tests/data/invalid-config-files/policy-mixed.conf @@ -0,0 +1,6 @@ + + tcp:port=1234 + + + + diff --git a/tests/data/invalid-config-files/policy-no-attributes.conf b/tests/data/invalid-config-files/policy-no-attributes.conf new file mode 100644 index 0000000..d10c496 --- /dev/null +++ b/tests/data/invalid-config-files/policy-no-attributes.conf @@ -0,0 +1,4 @@ + + tcp:port=1234 + + diff --git a/tests/data/invalid-config-files/policy-no-rule-attribute.conf b/tests/data/invalid-config-files/policy-no-rule-attribute.conf new file mode 100644 index 0000000..08ae437 --- /dev/null +++ b/tests/data/invalid-config-files/policy-no-rule-attribute.conf @@ -0,0 +1,6 @@ + + tcp:port=1234 + + + + diff --git a/tests/data/invalid-config-files/send-and-receive.conf b/tests/data/invalid-config-files/send-and-receive.conf new file mode 100644 index 0000000..f5b1a31 --- /dev/null +++ b/tests/data/invalid-config-files/send-and-receive.conf @@ -0,0 +1,6 @@ + + unix:path=/foo + + + + diff --git a/tests/data/invalid-config-files/truncated-file.conf b/tests/data/invalid-config-files/truncated-file.conf new file mode 100644 index 0000000..e8d6088 --- /dev/null +++ b/tests/data/invalid-config-files/truncated-file.conf @@ -0,0 +1,9 @@ + + + mybususer + unix:path=/foo/bar + tcp:port=1234 + basic.d + /usr/share/foo + 5000 + diff --git a/tests/data/valid-config-files/basic.conf b/tests/data/valid-config-files/basic.conf new file mode 100644 index 0000000..78cf7dd --- /dev/null +++ b/tests/data/valid-config-files/basic.conf @@ -0,0 +1,32 @@ + + + mybususer + unix:path=/foo/bar + tcp:port=1234 + basic.d + /usr/share/foo + nonexistent.conf + + + + + 5000 + 5000 + 300 + 5000 + 6000 + 50 + 80 + 64 + 64 + 256 + + + + + + + diff --git a/tests/data/valid-config-files/basic.d/basic.conf b/tests/data/valid-config-files/basic.d/basic.conf new file mode 100644 index 0000000..d109d71 --- /dev/null +++ b/tests/data/valid-config-files/basic.d/basic.conf @@ -0,0 +1,13 @@ + + + mybususer + unix:path=/foo/bar + tcp:port=1234 + basic.d + /usr/share/foo + nonexistent.conf + + + + diff --git a/tests/data/valid-config-files/check-own-rules.conf b/tests/data/valid-config-files/check-own-rules.conf new file mode 100644 index 0000000..bc2f415 --- /dev/null +++ b/tests/data/valid-config-files/check-own-rules.conf @@ -0,0 +1,14 @@ + + + mybususer + unix:path=/foo/bar + tcp:port=1234 + /usr/share/foo + + + + + + + diff --git a/tests/data/valid-config-files/entities.conf b/tests/data/valid-config-files/entities.conf new file mode 100644 index 0000000..3d3cea7 --- /dev/null +++ b/tests/data/valid-config-files/entities.conf @@ -0,0 +1,14 @@ + + + + mybususer + unix:path=/foo/<bar> + tcp:port=1234 + basic.d + /usr/&share/foo + nonexistent.confn + + + + diff --git a/tests/data/valid-config-files/listen-unix-runtime.conf b/tests/data/valid-config-files/listen-unix-runtime.conf new file mode 100644 index 0000000..169de2c --- /dev/null +++ b/tests/data/valid-config-files/listen-unix-runtime.conf @@ -0,0 +1,11 @@ + + + session + unix:runtime=yes + + + + + + diff --git a/tests/data/valid-config-files/many-rules.conf b/tests/data/valid-config-files/many-rules.conf new file mode 100644 index 0000000..df9a994 --- /dev/null +++ b/tests/data/valid-config-files/many-rules.conf @@ -0,0 +1,57 @@ + + + mybususer + unix:path=/foo/bar + tcp:port=1234 + basic.d + + /usr/share/foo + nonexistent.conf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5000 + 5000 + 300 + 5000 + 6000 + 50 + 80 + 64 + 64 + 256 + 512 + + diff --git a/tests/data/valid-config-files/minimal.conf b/tests/data/valid-config-files/minimal.conf new file mode 100644 index 0000000..3fd2d34 --- /dev/null +++ b/tests/data/valid-config-files/minimal.conf @@ -0,0 +1,3 @@ + + tcp:port=1234 + diff --git a/tests/data/valid-config-files/session.conf b/tests/data/valid-config-files/session.conf new file mode 100644 index 0000000..750f5d0 --- /dev/null +++ b/tests/data/valid-config-files/session.conf @@ -0,0 +1,80 @@ + + + + + + session + + + + + tcp:host=localhost + + + + + + + + + + + + + + + + + ../../etc/dbus-1/session.conf + + + session.d + + ../../etc/dbus-1/session.d + + + ../../etc/dbus-1/session-local.conf + + contexts/dbus_contexts + + + + + 1000000000 + 250000000 + 1000000000 + 250000000 + 1000000000 + + 120000 + 240000 + 150000 + 100000 + 10000 + 100000 + 10000 + 50000 + 50000 + 50000 + + diff --git a/tests/data/valid-config-files/standard-session-dirs.conf b/tests/data/valid-config-files/standard-session-dirs.conf new file mode 100644 index 0000000..f62dc87 --- /dev/null +++ b/tests/data/valid-config-files/standard-session-dirs.conf @@ -0,0 +1,8 @@ + + + + unix:path=/foo + session + + From f21c7912aad5c0d10290d147da7fdbf6d047177e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 26 Jun 2023 10:17:04 +0200 Subject: [PATCH 5/7] Bump clap to 4.3 As we need require=false on the following patch. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 142997b..751dda1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ path = "src/bin/busd.rs" #zbus = { git = "https://github.com/dbus2/zbus/", features = ["tokio"], default-features = false } zbus = { version = "3.14.1", features = ["tokio"], default-features = false } tokio = { version = "1.19.2", features = ["macros", "rt-multi-thread", "signal", "tracing", "fs" ] } -clap = { version = "4.0.18", features = ["derive"] } +clap = { version = "4.3", features = ["derive"] } tracing = "0.1.34" tracing-subscriber = { version = "0.3.11", features = ["env-filter" , "fmt", "ansi"], default-features = false, optional = true } anyhow = "1.0.58" From f8678b3feec232accf3e5b9270e58bb773350cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 26 Jun 2023 10:19:31 +0200 Subject: [PATCH 6/7] Introduce --config-file parsing Atm, we simply handle the last address! --- src/bin/busd.rs | 34 ++++++++++++++---- src/bus/mod.rs | 80 ++++++++++++++++++++++------------------- src/config.rs | 6 ++-- tests/config.rs | 3 +- tests/fdo.rs | 4 +-- tests/greet.rs | 4 +-- tests/multiple_conns.rs | 4 +-- 7 files changed, 79 insertions(+), 56 deletions(-) diff --git a/src/bin/busd.rs b/src/bin/busd.rs index 404395b..574d289 100644 --- a/src/bin/busd.rs +++ b/src/bin/busd.rs @@ -3,10 +3,10 @@ extern crate busd; #[cfg(unix)] use std::{fs::File, io::Write, os::fd::FromRawFd}; -use busd::bus; +use busd::{bus, config::BusConfig}; use anyhow::Result; -use clap::{Parser, ValueEnum}; +use clap::{Args, Parser, ValueEnum}; #[cfg(unix)] use tokio::{select, signal::unix::SignalKind}; use tracing::error; @@ -16,7 +16,10 @@ use tracing::{info, warn}; /// A simple D-Bus broker. #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] -struct Args { +struct BusdArgs { + #[command(flatten)] + config: ConfigArg, + /// The address to listen on. #[clap(short = 'a', long, value_parser)] address: Option, @@ -43,6 +46,14 @@ struct Args { ready_fd: Option, } +#[derive(Args, Debug)] +#[group(required = false, multiple = false)] +struct ConfigArg { + /// The configuration file. + #[clap(long, value_parser)] + config_file: Option, +} + #[derive(Copy, Clone, Debug, ValueEnum)] enum AuthMechanism { /// This is the recommended authentication mechanism on platforms where credentials can be @@ -70,10 +81,21 @@ impl From for zbus::AuthMechanism { async fn main() -> Result<()> { busd::tracing_subscriber::init(); - let args = Args::parse(); + let args = BusdArgs::parse(); + + let mut config = BusConfig::default(); + if let Some(file) = args.config.config_file { + config = BusConfig::read(file)?; + } + + if let Some(address) = args.address { + config.add_listen_address(address); + } - let mut bus = - bus::Bus::for_address(args.address.as_deref(), args.auth_mechanism.into()).await?; + // FIXME: we don't support multiple atm + let listen_addresses = config.listen_addresses(); + let address = listen_addresses.last().unwrap(); + let mut bus = bus::Bus::for_address(address, args.auth_mechanism.into()).await?; #[cfg(unix)] if let Some(fd) = args.ready_fd { diff --git a/src/bus/mod.rs b/src/bus/mod.rs index 27bd733..c93e1d8 100644 --- a/src/bus/mod.rs +++ b/src/bus/mod.rs @@ -5,12 +5,9 @@ use futures_util::{ future::{select, Either}, pin_mut, try_join, TryFutureExt, }; -use std::{cell::OnceCell, str::FromStr, sync::Arc}; #[cfg(unix)] -use std::{ - env, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; +use std::{cell::OnceCell, sync::Arc}; #[cfg(unix)] use tokio::fs::remove_file; use tokio::{spawn, task::JoinHandle}; @@ -51,24 +48,53 @@ enum Listener { } impl Bus { - pub async fn for_address(address: Option<&str>, auth_mechanism: AuthMechanism) -> Result { - let address = match address { - Some(address) => address.to_string(), - None => default_address(), - }; - let address = Address::from_str(&address)?; - let listener = match &address { + pub async fn for_address(address: &str, auth_mechanism: AuthMechanism) -> Result { + let address = address.try_into()?; + + let (listener, address) = match &address { #[cfg(unix)] Address::Unix(path) => { let path = Path::new(&path); info!("Listening on {}.", path.display()); - Self::unix_stream(path).await + Self::unix_stream(path).await.map(|l| (l, address)) + } + #[cfg(unix)] + Address::UnixDir(dir) => { + let dir = Path::new(&dir); + let mut n = 0; + loop { + n += 1; + let path = dir.join(format!("dbus-{}", n)); + + let Result::Ok(l) = Self::unix_stream(&path).await else { + continue; + }; + info!("Listening on {}.", path.display()); + break Ok((l, Address::Unix(path.into()))); + } + } + #[cfg(unix)] + Address::UnixTmpDir(dir) => { + let mut s = std::ffi::OsString::from("\0"); + s.push(dir); + let dir = Path::new(&s); + let mut n = 0; + loop { + n += 1; + let path = dir.join(format!("dbus-{}", n)); + + let Result::Ok(l) = Self::unix_stream(&path).await else { + continue; + }; + info!("Listening on abstract {}.", path.display()); + break Ok((l, Address::Unix(path.into()))); + } } - Address::Tcp(address) => { - info!("Listening on `{}:{}`.", address.host(), address.port()); + Address::Tcp(tcp) => { + info!("Listening on `{}:{}`.", tcp.host(), tcp.port()); - Self::tcp_stream(address).await + Self::tcp_stream(tcp).await.map(|l| (l, address)) } Address::NonceTcp { .. } => bail!("`nonce-tcp` transport is not supported (yet)."), Address::Autolaunch(_) => bail!("`autolaunch` transport is not supported (yet)."), @@ -142,6 +168,7 @@ impl Bus { #[cfg(unix)] async fn unix_stream(socket_path: &Path) -> Result { let socket_path = socket_path.to_path_buf(); + let _ = remove_file(&socket_path).await; let listener = Listener::Unix { listener: tokio::net::UnixListener::bind(&socket_path)?, socket_path, @@ -243,24 +270,3 @@ impl Bus { } } } - -#[cfg(unix)] -fn default_address() -> String { - let runtime_dir = env::var("XDG_RUNTIME_DIR") - .as_ref() - .map(|s| Path::new(s).to_path_buf()) - .ok() - .unwrap_or_else(|| { - Path::new("/run") - .join("user") - .join(format!("{}", nix::unistd::Uid::current())) - }); - let path = runtime_dir.join("busd-session"); - - format!("unix:path={}", path.display()) -} - -#[cfg(not(unix))] -fn default_address() -> String { - "tcp:host=127.0.0.1,port=4242".to_string() -} diff --git a/src/config.rs b/src/config.rs index fe6411d..4471c6d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,7 @@ -use std::fmt::Write; -use std::path::{Path, PathBuf}; +use std::{ + fmt::Write, + path::{Path, PathBuf}, +}; use anyhow::{bail, Context}; use serde::{Deserialize, Serialize}; diff --git a/tests/config.rs b/tests/config.rs index 32b4cda..b1edf7e 100644 --- a/tests/config.rs +++ b/tests/config.rs @@ -1,6 +1,5 @@ use busd::config::{BusConfig, Element}; -use std::error::Error; -use std::path::PathBuf; +use std::{error::Error, path::PathBuf}; #[test] fn test_se() -> Result<(), Box> { diff --git a/tests/fdo.rs b/tests/fdo.rs index be35b11..bd4497c 100644 --- a/tests/fdo.rs +++ b/tests/fdo.rs @@ -38,9 +38,7 @@ async fn name_ownership_changes() { } async fn name_ownership_changes_(address: &str, auth_mechanism: AuthMechanism) { - let mut bus = Bus::for_address(Some(address), auth_mechanism) - .await - .unwrap(); + let mut bus = Bus::for_address(address, auth_mechanism).await.unwrap(); let (tx, rx) = tokio::sync::oneshot::channel(); let handle = tokio::spawn(async move { diff --git a/tests/greet.rs b/tests/greet.rs index 51ca8b0..3a9e594 100644 --- a/tests/greet.rs +++ b/tests/greet.rs @@ -40,9 +40,7 @@ async fn greet() { } async fn greet_(socket_addr: &str, auth_mechanism: AuthMechanism) { - let mut bus = Bus::for_address(Some(socket_addr), auth_mechanism) - .await - .unwrap(); + let mut bus = Bus::for_address(socket_addr, auth_mechanism).await.unwrap(); let (tx, mut rx) = channel(1); let handle = tokio::spawn(async move { diff --git a/tests/multiple_conns.rs b/tests/multiple_conns.rs index a6759a5..dfcea21 100644 --- a/tests/multiple_conns.rs +++ b/tests/multiple_conns.rs @@ -32,9 +32,7 @@ async fn multi_conenct() { } async fn multi_conenct_(socket_addr: &str, auth_mechanism: AuthMechanism) { - let mut bus = Bus::for_address(Some(socket_addr), auth_mechanism) - .await - .unwrap(); + let mut bus = Bus::for_address(socket_addr, auth_mechanism).await.unwrap(); let (tx, rx) = channel(); let handle = tokio::spawn(async move { From 81d493dc532187f9b542201538e5cfa2e6b3bd27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 26 Jun 2023 10:25:41 +0200 Subject: [PATCH 7/7] Add --system and --session --- src/bin/busd.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/bin/busd.rs b/src/bin/busd.rs index 574d289..b81a2b4 100644 --- a/src/bin/busd.rs +++ b/src/bin/busd.rs @@ -52,6 +52,14 @@ struct ConfigArg { /// The configuration file. #[clap(long, value_parser)] config_file: Option, + + /// Use the standard configuration file for the per-login-session message bus. + #[clap(long, value_parser)] + session: bool, + + /// Use the standard configuration file for the system-wide message bus. + #[clap(long, value_parser)] + system: bool, } #[derive(Copy, Clone, Debug, ValueEnum)] @@ -81,7 +89,19 @@ impl From for zbus::AuthMechanism { async fn main() -> Result<()> { busd::tracing_subscriber::init(); - let args = BusdArgs::parse(); + let mut args = BusdArgs::parse(); + + // let's make --session the default + if !args.config.session && !args.config.system && args.config.config_file.is_none() { + args.config.session = true; + } + // FIXME: make default config configurable or OS dependant + if args.config.session { + args.config.config_file = Some("/usr/share/dbus-1/session.conf".into()); + } + if args.config.system { + args.config.config_file = Some("/usr/share/dbus-1/system.conf".into()); + } let mut config = BusConfig::default(); if let Some(file) = args.config.config_file {