From 84c926cb184f44c1acc79b3a27119f090f7c1890 Mon Sep 17 00:00:00 2001 From: Ron Waldon-Howe <jokeyrhyme@jokeyrhy.me> Date: Mon, 18 Nov 2024 17:45:29 +1100 Subject: [PATCH] =?UTF-8?q?=E2=9C=85=20Test=20against=20upstream's=20defau?= =?UTF-8?q?lt=20and=20example=20XMLs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/dbus2/busd/pull/159#issuecomment-2481538852 --- src/config.rs | 10 +- tests/config.rs | 386 +++++++++++++++++- .../example-session-disable-stats.conf | 17 + .../fixtures/example-system-enable-stats.conf | 17 + tests/fixtures/session.conf | 83 ++++ tests/fixtures/system.conf | 145 +++++++ 6 files changed, 653 insertions(+), 5 deletions(-) create mode 100644 tests/fixtures/example-session-disable-stats.conf create mode 100644 tests/fixtures/example-system-enable-stats.conf create mode 100644 tests/fixtures/session.conf create mode 100644 tests/fixtures/system.conf diff --git a/src/config.rs b/src/config.rs index a3e0305..3437aaa 100644 --- a/src/config.rs +++ b/src/config.rs @@ -639,10 +639,14 @@ mod tests { "#; let busconfig = Config::parse(input).expect("should parse XML input"); - let want = HashSet::from_iter([String::from("ANONYMOUS"), String::from("EXTERNAL")]); - let diff = busconfig.auth.symmetric_difference(&want); - assert_eq!(diff.count(), 0); + assert_eq!( + busconfig, + Config { + auth: HashSet::from_iter([String::from("ANONYMOUS"), String::from("EXTERNAL")]), + ..Default::default() + } + ); } #[test] diff --git a/tests/config.rs b/tests/config.rs index 25dcc92..7b6f760 100644 --- a/tests/config.rs +++ b/tests/config.rs @@ -1,6 +1,9 @@ -use std::collections::HashSet; +use std::{collections::HashSet, path::PathBuf}; -use busd::config::{Access, Config, Name, Operation, OwnOperation, Policy}; +use busd::config::{ + Access, BusType, Config, ConnectOperation, MessageType, Name, Operation, OwnOperation, Policy, + ReceiveOperation, SendOperation, +}; #[test] fn config_read_file_with_includes_ok() { @@ -53,6 +56,385 @@ fn config_read_file_with_includes_ok() { ); } +#[test] +fn config_read_file_example_session_disable_stats_conf_ok() { + let got = Config::read_file("./tests/fixtures/example-session-disable-stats.conf") + .expect("should read and parse XML input"); + + assert_eq!( + got, + Config { + policies: vec![Policy::DefaultContext(vec![( + Access::Deny, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Exact(String::from("org.freedesktop.DBus"))), + error: None, + interface: Some(String::from("org.freedesktop.DBus.Debug.Stats")), + max_fds: None, + min_fds: None, + path: None, + r#type: None + }), + ),]),], + ..Default::default() + } + ); +} + +#[test] +fn config_read_file_example_system_enable_stats_conf_ok() { + let got = Config::read_file("./tests/fixtures/example-system-enable-stats.conf") + .expect("should read and parse XML input"); + + assert_eq!( + got, + Config { + policies: vec![Policy::User( + vec![( + Access::Allow, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Exact(String::from("org.freedesktop.DBus"))), + error: None, + interface: Some(String::from("org.freedesktop.DBus.Debug.Stats")), + max_fds: None, + min_fds: None, + path: None, + r#type: None + }), + )], + String::from("USERNAME"), + ),], + ..Default::default() + } + ); +} + +#[test] +fn config_read_file_session_conf_ok() { + let mut got = Config::read_file("./tests/fixtures/session.conf") + .expect("should read and parse XML input"); + + assert!(!got.servicedirs.is_empty()); + + // nuking this to make it easier to `assert_eq!()` + got.servicedirs = vec![]; + + assert_eq!( + got, + Config { + listen: HashSet::from_iter(vec![String::from("@DBUS_SESSION_BUS_LISTEN_ADDRESS@"),]), + keep_umask: true, + policies: vec![Policy::DefaultContext(vec![ + ( + Access::Allow, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Any), + error: None, + interface: None, + max_fds: None, + min_fds: None, + path: None, + r#type: None, + }), + ), + ( + Access::Allow, + Operation::Own(OwnOperation { + own: Some(Name::Any), + }), + ), + ]),], + r#type: Some(BusType::Session), + ..Default::default() + } + ); +} + +#[test] +fn config_read_file_system_conf_ok() { + let want = Config { + auth: HashSet::from_iter(vec![String::from("EXTERNAL")]), + fork: true, + listen: HashSet::from_iter(vec![String::from("@DBUS_SYSTEM_BUS_DEFAULT_ADDRESS@")]), + pidfile: Some(PathBuf::from("@DBUS_SYSTEM_PID_FILE@")), + policies: vec![ + Policy::DefaultContext(vec![ + ( + Access::Allow, + Operation::Connect(ConnectOperation { + group: None, + user: Some(String::from("*")), + }), + ), + ( + Access::Deny, + Operation::Own(OwnOperation { + own: Some(Name::Any), + }), + ), + ( + Access::Deny, + Operation::Send(SendOperation { + broadcast: None, + destination: None, + error: None, + interface: None, + max_fds: None, + min_fds: None, + path: None, + r#type: Some(MessageType::MethodCall), + }), + ), + ( + Access::Allow, + Operation::Send(SendOperation { + broadcast: None, + destination: None, + error: None, + interface: None, + max_fds: None, + min_fds: None, + path: None, + r#type: Some(MessageType::Signal), + }), + ), + ( + Access::Allow, + Operation::Send(SendOperation { + broadcast: None, + destination: None, + error: None, + interface: None, + max_fds: None, + min_fds: None, + path: None, + r#type: Some(MessageType::MethodReturn), + }), + ), + ( + Access::Allow, + Operation::Send(SendOperation { + broadcast: None, + destination: None, + error: None, + interface: None, + max_fds: None, + min_fds: None, + path: None, + r#type: Some(MessageType::Error), + }), + ), + ( + Access::Allow, + Operation::Receive(ReceiveOperation { + error: None, + interface: None, + max_fds: None, + min_fds: None, + path: None, + sender: None, + r#type: Some(MessageType::MethodCall), + }), + ), + ( + Access::Allow, + Operation::Receive(ReceiveOperation { + error: None, + interface: None, + max_fds: None, + min_fds: None, + path: None, + sender: None, + r#type: Some(MessageType::MethodReturn), + }), + ), + ( + Access::Allow, + Operation::Receive(ReceiveOperation { + error: None, + interface: None, + max_fds: None, + min_fds: None, + path: None, + sender: None, + r#type: Some(MessageType::Error), + }), + ), + ( + Access::Allow, + Operation::Receive(ReceiveOperation { + error: None, + interface: None, + max_fds: None, + min_fds: None, + path: None, + sender: None, + r#type: Some(MessageType::Signal), + }), + ), + ( + Access::Allow, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Exact(String::from("org.freedesktop.DBus"))), + error: None, + interface: Some(String::from("org.freedesktop.DBus")), + max_fds: None, + min_fds: None, + path: None, + r#type: None, + }), + ), + ( + Access::Allow, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Exact(String::from("org.freedesktop.DBus"))), + error: None, + interface: Some(String::from("org.freedesktop.DBus.Introspectable")), + max_fds: None, + min_fds: None, + path: None, + r#type: None, + }), + ), + ( + Access::Allow, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Exact(String::from("org.freedesktop.DBus"))), + error: None, + interface: Some(String::from("org.freedesktop.DBus.Properties")), + max_fds: None, + min_fds: None, + path: None, + r#type: None, + }), + ), + ( + Access::Allow, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Exact(String::from("org.freedesktop.DBus"))), + error: None, + interface: Some(String::from("org.freedesktop.DBus.Containers1")), + max_fds: None, + min_fds: None, + path: None, + r#type: None, + }), + ), + ( + // TODO: this is a mistake, deny with send_member rule should be omitted + Access::Deny, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Exact(String::from("org.freedesktop.DBus"))), + error: None, + interface: Some(String::from("org.freedesktop.DBus")), + max_fds: None, + min_fds: None, + path: None, + r#type: None, + }), + ), + ( + Access::Deny, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Exact(String::from("org.freedesktop.DBus"))), + error: None, + interface: Some(String::from("org.freedesktop.DBus.Debug.Stats")), + max_fds: None, + min_fds: None, + path: None, + r#type: None, + }), + ), + ( + Access::Deny, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Exact(String::from("org.freedesktop.DBus"))), + error: None, + interface: Some(String::from("org.freedesktop.systemd1.Activator")), + max_fds: None, + min_fds: None, + path: None, + r#type: None, + }), + ), + ]), + Policy::User( + vec![( + Access::Allow, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Exact(String::from("org.freedesktop.DBus"))), + error: None, + interface: Some(String::from("org.freedesktop.systemd1.Activator")), + max_fds: None, + min_fds: None, + path: None, + r#type: None, + }), + )], + String::from("root"), + ), + Policy::User( + vec![( + Access::Allow, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Exact(String::from("org.freedesktop.DBus"))), + error: None, + interface: Some(String::from("org.freedesktop.DBus.Monitoring")), + max_fds: None, + min_fds: None, + path: None, + r#type: None, + }), + )], + String::from("root"), + ), + Policy::User( + vec![( + Access::Allow, + Operation::Send(SendOperation { + broadcast: None, + destination: Some(Name::Exact(String::from("org.freedesktop.DBus"))), + error: None, + interface: Some(String::from("org.freedesktop.DBus.Debug.Stats")), + max_fds: None, + min_fds: None, + path: None, + r#type: None, + }), + )], + String::from("root"), + ), + ], + servicehelper: Some(PathBuf::from("@DBUS_LIBEXECDIR@/dbus-daemon-launch-helper")), + syslog: true, + r#type: Some(BusType::System), + user: Some(String::from("@DBUS_USER@")), + ..Default::default() + }; + + let mut got = + Config::read_file("./tests/fixtures/system.conf").expect("should read and parse XML input"); + + assert!(!got.servicedirs.is_empty()); + + // nuking this to make it easier to `assert_eq!()` + got.servicedirs = vec![]; + + assert_eq!(got, want,); +} + #[should_panic] #[test] fn config_read_file_with_missing_include_err() { diff --git a/tests/fixtures/example-session-disable-stats.conf b/tests/fixtures/example-session-disable-stats.conf new file mode 100644 index 0000000..baafb2d --- /dev/null +++ b/tests/fixtures/example-session-disable-stats.conf @@ -0,0 +1,17 @@ +<!-- busd: from: https://gitlab.freedesktop.org/dbus/dbus/-/commit/776e6e0b04a14de4cafc13cc74ffb4a55a23a074 --> + +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + + <!-- If the Stats interface was enabled at compile-time, users can use it on + the session bus by default. Systems providing isolation of processes + with LSMs might want to restrict this. This can be achieved by copying + this file in @EXPANDED_SYSCONFDIR@/dbus-1/session.d/ --> + + <policy context="default"> + <deny send_destination="org.freedesktop.DBus" + send_interface="org.freedesktop.DBus.Debug.Stats"/> + </policy> + +</busconfig> diff --git a/tests/fixtures/example-system-enable-stats.conf b/tests/fixtures/example-system-enable-stats.conf new file mode 100644 index 0000000..677f923 --- /dev/null +++ b/tests/fixtures/example-system-enable-stats.conf @@ -0,0 +1,17 @@ +<!-- busd: from: https://gitlab.freedesktop.org/dbus/dbus/-/commit/776e6e0b04a14de4cafc13cc74ffb4a55a23a074 --> + +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + + <!-- If the Stats interface was enabled at compile-time, only root may use it. + Replace USERNAME and copy this file in @EXPANDED_SYSCONFDIR@/dbus-1/system.d/ + if you want to enable other privileged users to view statistics and + debug info --> + + <policy user="USERNAME"> + <allow send_destination="org.freedesktop.DBus" + send_interface="org.freedesktop.DBus.Debug.Stats"/> + </policy> + +</busconfig> diff --git a/tests/fixtures/session.conf b/tests/fixtures/session.conf new file mode 100644 index 0000000..9f3e553 --- /dev/null +++ b/tests/fixtures/session.conf @@ -0,0 +1,83 @@ +<!-- busd: from: https://gitlab.freedesktop.org/dbus/dbus/-/commit/776e6e0b04a14de4cafc13cc74ffb4a55a23a074 --> +<!-- busd: we've commented out any blocking includes or includedirs --> + +<!-- This configuration file controls the per-user-login-session message bus. + Add a session-local.conf and edit that rather than changing this + file directly. --> + +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <!-- Our well-known bus type, don't change this --> + <type>session</type> + + <!-- If we fork, keep the user's original umask to avoid affecting + the behavior of child processes. --> + <keep_umask/> + + <listen>@DBUS_SESSION_BUS_LISTEN_ADDRESS@</listen> + + <!-- On Unix systems, the most secure authentication mechanism is + EXTERNAL, which uses credential-passing over Unix sockets. + + This authentication mechanism is not available on Windows, + is not suitable for use with the tcp: or nonce-tcp: transports, + and will not work on obscure flavours of Unix that do not have + a supported credentials-passing mechanism. On those platforms/transports, + comment out the <auth> element to allow fallback to DBUS_COOKIE_SHA1. --> + <!-- @DBUS_SESSION_CONF_MAYBE_AUTH_EXTERNAL@ --> + + <standard_session_servicedirs /> + + <policy context="default"> + <!-- Allow everything to be sent --> + <allow send_destination="*" eavesdrop="true"/> + <!-- Allow everything to be received --> + <allow eavesdrop="true"/> + <!-- Allow anyone to own anything --> + <allow own="*"/> + </policy> + + <!-- Include legacy configuration location --> + <include ignore_missing="yes">@SYSCONFDIR_FROM_PKGDATADIR@/dbus-1/session.conf</include> + + <!-- Config files are placed here that among other things, + further restrict the above policy for specific services. --> + <!-- <includedir>session.d</includedir> --> + + <!-- <includedir>@SYSCONFDIR_FROM_PKGDATADIR@/dbus-1/session.d</includedir> --> + + <!-- This is included last so local configuration can override what's + in this standard file --> + <include ignore_missing="yes">@SYSCONFDIR_FROM_PKGDATADIR@/dbus-1/session-local.conf</include> + + <!-- <include if_selinux_enabled="yes" selinux_root_relative="yes">contexts/dbus_contexts</include> --> + + <!-- For the session bus, override the default relatively-low limits + with essentially infinite limits, since the bus is just running + as the user anyway, using up bus resources is not something we need + to worry about. In some cases, we do set the limits lower than + "all available memory" if exceeding the limit is almost certainly a bug, + having the bus enforce a limit is nicer than a huge memory leak. But the + intent is that these limits should never be hit. --> + + <!-- the memory limits are 1G instead of say 4G because they can't exceed 32-bit signed int max --> + <limit name="max_incoming_bytes">1000000000</limit> + <limit name="max_incoming_unix_fds">250000000</limit> + <limit name="max_outgoing_bytes">1000000000</limit> + <limit name="max_outgoing_unix_fds">250000000</limit> + <limit name="max_message_size">1000000000</limit> + <!-- We do not override max_message_unix_fds here since the in-kernel + limit is also relatively low --> + <limit name="service_start_timeout">120000</limit> + <limit name="auth_timeout">240000</limit> + <limit name="pending_fd_timeout">150000</limit> + <limit name="max_completed_connections">100000</limit> + <limit name="max_incomplete_connections">10000</limit> + <limit name="max_connections_per_user">100000</limit> + <limit name="max_pending_service_starts">10000</limit> + <limit name="max_names_per_connection">50000</limit> + <limit name="max_match_rules_per_connection">50000</limit> + <limit name="max_replies_per_connection">50000</limit> + +</busconfig> diff --git a/tests/fixtures/system.conf b/tests/fixtures/system.conf new file mode 100644 index 0000000..043b57c --- /dev/null +++ b/tests/fixtures/system.conf @@ -0,0 +1,145 @@ +<!-- from: https://gitlab.freedesktop.org/dbus/dbus/-/commit/776e6e0b04a14de4cafc13cc74ffb4a55a23a074 --> +<!-- busd: we've commented out any blocking includes or includedirs --> + +<!-- This configuration file controls the systemwide message bus. + Add a system-local.conf and edit that rather than changing this + file directly. --> + +<!-- Note that there are any number of ways you can hose yourself + security-wise by screwing up this file; in particular, you + probably don't want to listen on any more addresses, add any more + auth mechanisms, run as a different user, etc. --> + +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + + <!-- Our well-known bus type, do not change this --> + <type>system</type> + + <!-- Run as special user --> + <user>@DBUS_USER@</user> + + <!-- Fork into daemon mode --> + <fork/> + + <!-- We use system service launching using a helper --> + <standard_system_servicedirs/> + + <!-- This is a setuid helper that is used to launch system services --> + <servicehelper>@DBUS_LIBEXECDIR@/dbus-daemon-launch-helper</servicehelper> + + <!-- Write a pid file --> + <pidfile>@DBUS_SYSTEM_PID_FILE@</pidfile> + + <!-- Enable logging to syslog --> + <syslog/> + + <!-- Only allow socket-credentials-based authentication --> + <auth>EXTERNAL</auth> + + <!-- Only listen on a local socket. (abstract=/path/to/socket + means use abstract namespace, don't really create filesystem + file; only Linux supports this. Use path=/whatever on other + systems.) --> + <listen>@DBUS_SYSTEM_BUS_DEFAULT_ADDRESS@</listen> + + <policy context="default"> + <!-- All users can connect to system bus --> + <allow user="*"/> + + <!-- Holes must be punched in service configuration files for + name ownership and sending method calls --> + <deny own="*"/> + <deny send_type="method_call"/> + + <!-- Signals and reply messages (method returns, errors) are allowed + by default --> + <allow send_type="signal"/> + <allow send_requested_reply="true" send_type="method_return"/> + <allow send_requested_reply="true" send_type="error"/> + + <!-- All messages may be received by default --> + <allow receive_type="method_call"/> + <allow receive_type="method_return"/> + <allow receive_type="error"/> + <allow receive_type="signal"/> + + <!-- Allow anyone to talk to the message bus --> + <allow send_destination="org.freedesktop.DBus" + send_interface="org.freedesktop.DBus" /> + <allow send_destination="org.freedesktop.DBus" + send_interface="org.freedesktop.DBus.Introspectable"/> + <allow send_destination="org.freedesktop.DBus" + send_interface="org.freedesktop.DBus.Properties"/> + <allow send_destination="org.freedesktop.DBus" + send_interface="org.freedesktop.DBus.Containers1"/> + <!-- But disallow some specific bus services --> + <deny send_destination="org.freedesktop.DBus" + send_interface="org.freedesktop.DBus" + send_member="UpdateActivationEnvironment"/> + <deny send_destination="org.freedesktop.DBus" + send_interface="org.freedesktop.DBus.Debug.Stats"/> + <deny send_destination="org.freedesktop.DBus" + send_interface="org.freedesktop.systemd1.Activator"/> + </policy> + + <!-- Only systemd, which runs as root, may report activation failures. --> + <policy user="root"> + <allow send_destination="org.freedesktop.DBus" + send_interface="org.freedesktop.systemd1.Activator"/> + </policy> + + <!-- root may monitor the system bus. --> + <policy user="root"> + <allow send_destination="org.freedesktop.DBus" + send_interface="org.freedesktop.DBus.Monitoring"/> + </policy> + + <!-- If the Stats interface was enabled at compile-time, root may use it. + Copy this into system.local.conf or system.d/*.conf if you want to + enable other privileged users to view statistics and debug info --> + <policy user="root"> + <allow send_destination="org.freedesktop.DBus" + send_interface="org.freedesktop.DBus.Debug.Stats"/> + </policy> + + <!-- Include legacy configuration location --> + <include ignore_missing="yes">@SYSCONFDIR_FROM_PKGDATADIR@/dbus-1/system.conf</include> + + <!-- The defaults for these limits are hard-coded in dbus-daemon. + Some clarifications: + Times are in milliseconds (ms); 1000ms = 1 second + 133169152 bytes = 127 MiB + 33554432 bytes = 32 MiB + 150000ms = 2.5 minutes --> + <!-- <limit name="max_incoming_bytes">133169152</limit> --> + <!-- <limit name="max_incoming_unix_fds">64</limit> --> + <!-- <limit name="max_outgoing_bytes">133169152</limit> --> + <!-- <limit name="max_outgoing_unix_fds">64</limit> --> + <!-- <limit name="max_message_size">33554432</limit> --> + <!-- <limit name="max_message_unix_fds">16</limit> --> + <!-- <limit name="service_start_timeout">25000</limit> --> + <!-- <limit name="auth_timeout">5000</limit> --> + <!-- <limit name="pending_fd_timeout">150000</limit> --> + <!-- <limit name="max_completed_connections">2048</limit> --> + <!-- <limit name="max_incomplete_connections">64</limit> --> + <!-- <limit name="max_connections_per_user">256</limit> --> + <!-- <limit name="max_pending_service_starts">512</limit> --> + <!-- <limit name="max_names_per_connection">512</limit> --> + <!-- <limit name="max_match_rules_per_connection">512</limit> --> + <!-- <limit name="max_replies_per_connection">128</limit> --> + + <!-- Config files are placed here that among other things, punch + holes in the above policy for specific services. --> + <!-- <includedir>system.d</includedir> --> + + <!-- <includedir>@SYSCONFDIR_FROM_PKGDATADIR@/dbus-1/system.d</includedir> --> + + <!-- This is included last so local configuration can override what's + in this standard file --> + <include ignore_missing="yes">@SYSCONFDIR_FROM_PKGDATADIR@/dbus-1/system-local.conf</include> + + <!-- <include if_selinux_enabled="yes" selinux_root_relative="yes">contexts/dbus_contexts</include> --> + +</busconfig>