├── test ├── integration │ ├── .fmf │ │ └── version │ ├── fuzz │ │ └── dfuzzer │ │ │ ├── main.fmf │ │ │ └── test.sh │ ├── plans │ │ └── upstream_ci.fmf │ └── README ├── image │ └── dbrk-fedora.Dockerfile └── dbus │ ├── util-message.h │ ├── tool-flood.c │ ├── util-broker.h │ ├── meson.build │ └── bench-connect.c ├── subprojects ├── libcdvar-1.wrap ├── libcini-1.wrap ├── libclist-3.wrap ├── libcutf8-1.wrap ├── libcrbtree-3.wrap ├── libcshquote-1.wrap ├── libcstdaux-1.wrap ├── sys-1.wrap ├── libc-rs-0.2.wrap └── packagefiles │ └── libc-rs │ ├── 0001-clippy-allow-all.patch │ ├── meson-detect-version.sh │ └── meson.build ├── src ├── util │ ├── mod.rs │ ├── systemd.h │ ├── common.h │ ├── test-apparmor.c │ ├── nsec.h │ ├── proc.h │ ├── sockopt.h │ ├── audit.h │ ├── test-nsec.c │ ├── fs.h │ ├── dirwatch.h │ ├── audit-fallback.c │ ├── test-systemd.c │ ├── sampler.h │ ├── test-dirwatch.c │ ├── fdlist.h │ ├── selinux.h │ ├── test-error.c │ ├── apparmor.h │ ├── test-fs.c │ ├── selinux-fallback.c │ ├── acct.h │ ├── error.h │ ├── systemd.c │ ├── misc.h │ ├── syscall.h │ ├── dispatch.h │ ├── string.c │ ├── apparmor-fallback.c │ ├── nsec.c │ ├── test-proc.c │ ├── user.h │ ├── log.h │ ├── sampler.c │ ├── dirwatch.c │ ├── test-fdlist.c │ ├── string.h │ └── test-user.c ├── session │ ├── main.h │ └── main.c ├── units │ ├── user │ │ ├── dbus-broker-metrics.socket │ │ ├── meson.build │ │ └── dbus-broker.service.in │ └── system │ │ ├── dbus-broker-metrics.socket │ │ ├── meson.build │ │ └── dbus-broker.service.in ├── broker │ ├── main.h │ └── broker.h ├── catalog │ ├── meson.build │ ├── catalog-ids.h │ ├── dbus-broker.catalog │ └── dbus-broker-launch.catalog ├── bus │ ├── diag.h │ ├── test-reply.c │ ├── metrics.h │ ├── listener.h │ ├── reply.h │ ├── driver.h │ ├── activation.h │ ├── bus.h │ ├── reply.c │ ├── peer.h │ └── listener.c ├── lib-crate.rs ├── launch │ ├── test-nss-cache.c │ ├── nss-cache.h │ ├── launcher.h │ ├── service.h │ ├── config.dtd │ ├── test-config.c │ └── policy.h ├── lib-generated.rs └── dbus │ ├── address.h │ ├── test-address.c │ ├── test-message.c │ ├── sasl.h │ ├── protocol.h │ ├── connection.h │ ├── socket.h │ ├── queue.h │ ├── test-socket.c │ ├── message.h │ └── protocol.c ├── .editorconfig ├── .github ├── codeql-config.yml ├── meson-arch-lib32.cross ├── codeql-custom.qls └── workflows │ ├── coverity.yml │ ├── publish.yml │ ├── fuzz.yml │ └── lib-codeql.yml ├── docs ├── meson.build └── dbus-broker-launch.rst ├── meson_options.txt ├── README.md ├── packit.yml ├── AUTHORS └── meson.build /test/integration/.fmf/version: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /subprojects/libcdvar-1.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://github.com/c-util/c-dvar.git 3 | revision = v1 4 | -------------------------------------------------------------------------------- /subprojects/libcini-1.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://github.com/c-util/c-ini.git 3 | revision = v1 4 | -------------------------------------------------------------------------------- /subprojects/libclist-3.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://github.com/c-util/c-list.git 3 | revision = v3 4 | -------------------------------------------------------------------------------- /subprojects/libcutf8-1.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://github.com/c-util/c-utf8.git 3 | revision = v1 4 | -------------------------------------------------------------------------------- /subprojects/libcrbtree-3.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://github.com/c-util/c-rbtree.git 3 | revision = v3 4 | -------------------------------------------------------------------------------- /subprojects/libcshquote-1.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://github.com/c-util/c-shquote.git 3 | revision = v1 4 | -------------------------------------------------------------------------------- /subprojects/libcstdaux-1.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://github.com/c-util/c-stdaux.git 3 | revision = v1 4 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Utilities 2 | //! 3 | //! A collection of independent utilities for the bus broker. 4 | 5 | pub mod acct; 6 | -------------------------------------------------------------------------------- /subprojects/sys-1.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://github.com/bus1/sys.git 3 | revision = v1 4 | 5 | [provide] 6 | dependency_names = libosi-1, libsys-1, libtmp-1 7 | -------------------------------------------------------------------------------- /subprojects/libc-rs-0.2.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | diff_files = libc-rs/0001-clippy-allow-all.patch 3 | patch_directory = libc-rs 4 | revision = libc-0.2 5 | url = https://github.com/rust-lang/libc.git 6 | -------------------------------------------------------------------------------- /test/integration/fuzz/dfuzzer/main.fmf: -------------------------------------------------------------------------------- 1 | summary: Run dfuzzer on a couple of D-Bus interfaces 2 | test: ./test.sh 3 | recommend: 4 | - dfuzzer 5 | - systemd 6 | - util-linux 7 | duration: 30m 8 | -------------------------------------------------------------------------------- /src/session/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * D-Bus Session Initiator Main Entry 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | int32_t session_run(int32_t argc, uint8_t **argv); 11 | -------------------------------------------------------------------------------- /src/util/systemd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Systemd Utilities 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | int systemd_escape_unit(char **escapedp, const char *unescaped); 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | charset = utf-8 8 | 9 | [*.{c,h}] 10 | indent_style = space 11 | indent_size = 8 12 | -------------------------------------------------------------------------------- /src/units/user/dbus-broker-metrics.socket: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=D-Bus User Message Bus Metrics Socket 3 | 4 | [Socket] 5 | ListenStream=%t/bus_metrics 6 | Service=dbus.service 7 | 8 | [Install] 9 | Alias=dbus-metrics.socket 10 | -------------------------------------------------------------------------------- /src/units/system/dbus-broker-metrics.socket: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=D-Bus System Message Bus Metrics Socket 3 | 4 | [Socket] 5 | ListenStream=/run/dbus/system_bus_metrics_socket 6 | Service=dbus.service 7 | 8 | [Install] 9 | Alias=dbus-metrics.socket 10 | -------------------------------------------------------------------------------- /test/integration/plans/upstream_ci.fmf: -------------------------------------------------------------------------------- 1 | # vi: set sw=2 ts=2 et ft=yaml tw=80: 2 | 3 | # This plan discovers and executes all (enabled) integration tests 4 | 5 | summary: Upstream integration test suite 6 | discover: 7 | how: fmf 8 | execute: 9 | how: tmt 10 | -------------------------------------------------------------------------------- /src/session/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * D-Bus Session Initiator Main Entry 3 | */ 4 | 5 | #include 6 | #include 7 | #include "session/main.h" 8 | 9 | int main(int argc, char **argv) { 10 | return session_run((int32_t)argc, (uint8_t **)argv); 11 | } 12 | -------------------------------------------------------------------------------- /src/broker/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * D-Bus Broker Main Entry 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | enum { 11 | _MAIN_SUCCESS, 12 | MAIN_EXIT, 13 | MAIN_FAILED, 14 | }; 15 | 16 | extern int main_arg_controller; 17 | -------------------------------------------------------------------------------- /src/util/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Common definitions and helpers. 5 | */ 6 | 7 | #include 8 | 9 | enum { 10 | UTIL_TRISTATE_UNSET, 11 | UTIL_TRISTATE_YES, 12 | UTIL_TRISTATE_NO, 13 | 14 | _UTIL_TRISTATE_N, 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /src/units/user/meson.build: -------------------------------------------------------------------------------- 1 | # 2 | # target: /usr/lib/systemd/user/dbus-broker.service 3 | # 4 | 5 | configure_file( 6 | input: 'dbus-broker.service.in', 7 | output: 'dbus-broker.service', 8 | configuration: conf, 9 | install_dir: conf.get('userunitdir'), 10 | ) 11 | -------------------------------------------------------------------------------- /src/units/system/meson.build: -------------------------------------------------------------------------------- 1 | # 2 | # target: /usr/lib/systemd/system/dbus-broker.service 3 | # 4 | 5 | configure_file( 6 | input: 'dbus-broker.service.in', 7 | output: 'dbus-broker.service', 8 | configuration: conf, 9 | install_dir: conf.get('systemunitdir'), 10 | ) 11 | -------------------------------------------------------------------------------- /src/catalog/meson.build: -------------------------------------------------------------------------------- 1 | install_data( 2 | 'dbus-broker.catalog', 3 | install_dir: conf.get('catalogdir'), 4 | ) 5 | 6 | if use_launcher 7 | install_data( 8 | 'dbus-broker-launch.catalog', 9 | install_dir: conf.get('catalogdir'), 10 | ) 11 | endif 12 | -------------------------------------------------------------------------------- /subprojects/packagefiles/libc-rs/0001-clippy-allow-all.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/lib.rs b/src/lib.rs 2 | index 5989c64da..e41187c05 100644 3 | --- a/src/lib.rs 4 | +++ b/src/lib.rs 5 | @@ -1,4 +1,5 @@ 6 | //! libc - Raw FFI bindings to platforms' system libraries 7 | +#![allow(clippy::all)] 8 | #![crate_name = "libc"] 9 | #![crate_type = "rlib"] 10 | #![allow( 11 | -------------------------------------------------------------------------------- /src/bus/diag.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Bus Diagnostics 5 | */ 6 | 7 | #include 8 | #include 9 | #include "util/log.h" 10 | 11 | typedef struct Message Message; 12 | typedef struct Peer Peer; 13 | 14 | int diag_quota_queue_reply(Peer *sender, Peer *receiver, Message *m, LogProvenance prov); 15 | int diag_quota_dequeue(Peer *peer, LogProvenance prov); 16 | -------------------------------------------------------------------------------- /.github/codeql-config.yml: -------------------------------------------------------------------------------- 1 | # 2 | # CodeQL Configuration 3 | # 4 | # Custom configuration for the codeql-action of GitHub. It is sourced by the 5 | # CodeQL workflow, but can be used by local builds as well. 6 | # 7 | 8 | name: "CodeQL Configuration" 9 | 10 | disable-default-queries: false 11 | 12 | queries: 13 | - name: "Custom Queries for dbus-broker" 14 | uses: "./.github/codeql-custom.qls" 15 | -------------------------------------------------------------------------------- /src/util/test-apparmor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test AppArmor Handling 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include "util/apparmor.h" 9 | 10 | static void test_basic(void) { 11 | bool enabled; 12 | int r; 13 | 14 | r = bus_apparmor_is_enabled(&enabled); 15 | c_assert(!r); 16 | } 17 | 18 | int main(int argc, char **argv) { 19 | test_basic(); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /src/units/user/dbus-broker.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=D-Bus User Message Bus 3 | Documentation=man:dbus-broker-launch(1) 4 | DefaultDependencies=false 5 | 6 | After=dbus.socket dbus-metrics.socket 7 | Before=basic.target shutdown.target 8 | Conflicts=shutdown.target 9 | Requires=dbus.socket 10 | Wants=dbus-metrics.socket 11 | 12 | [Service] 13 | Type=notify-reload 14 | ExecStart=@bindir@/dbus-broker-launch --scope user 15 | Slice=session.slice 16 | 17 | [Install] 18 | Alias=dbus.service 19 | -------------------------------------------------------------------------------- /src/util/nsec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Nanosecond Time Management 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | typedef uint64_t nsec_t; 13 | 14 | #define NSEC_PRI PRIu64 15 | 16 | /* nsec */ 17 | 18 | nsec_t nsec_now(clockid_t clock); 19 | void nsec_sleep(clockid_t clock, nsec_t until); 20 | 21 | /* inline helpers */ 22 | 23 | static inline uint64_t nsec_to_usec(nsec_t t) { 24 | return t / 1000; 25 | } 26 | -------------------------------------------------------------------------------- /src/util/proc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Proc Helpers 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define PROC_PID_SELF ((pid_t)0) 12 | 13 | enum { 14 | _PROC_E_SUCCESS, 15 | 16 | PROC_E_NOT_FOUND, 17 | }; 18 | 19 | int proc_field(const char *data, const char *key, char **valuep); 20 | int proc_read(int fd, char **datap, size_t *n_datap); 21 | 22 | int proc_get_seclabel(pid_t pid, char **labelp, size_t *n_labelp); 23 | int proc_resolve_pidfd(int pidfd, pid_t *pidp); 24 | -------------------------------------------------------------------------------- /src/util/sockopt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Socket Options Helpers 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | enum { 11 | _SOCKOPT_E_SUCCESS, 12 | 13 | SOCKOPT_E_UNSUPPORTED, 14 | SOCKOPT_E_UNAVAILABLE, 15 | SOCKOPT_E_REAPED, 16 | }; 17 | 18 | typedef struct Log Log; 19 | 20 | int sockopt_get_peersec(int fd, char **labelp, size_t *lenp); 21 | int sockopt_get_peergroups(int fd, Log *log, uid_t uid, gid_t primary_gid, gid_t **gidsp, size_t *n_gidsp); 22 | int sockopt_get_peerpidfd(int fd, int *ret_pidfd); 23 | -------------------------------------------------------------------------------- /src/util/audit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Audit Helpers 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | enum { 11 | _UTIL_AUDIT_E_SUCCESS, 12 | 13 | UTIL_AUDIT_E_UNAVAILABLE, 14 | }; 15 | 16 | enum { 17 | UTIL_AUDIT_TYPE_AVC, 18 | UTIL_AUDIT_TYPE_POLICYLOAD, 19 | UTIL_AUDIT_TYPE_MAC_STATUS, 20 | }; 21 | 22 | int util_audit_drop_permissions(uint32_t uid, uint32_t gid); 23 | int util_audit_log(int type, const char *message, uid_t uid); 24 | 25 | int util_audit_init_global(void); 26 | void util_audit_deinit_global(void); 27 | -------------------------------------------------------------------------------- /src/units/system/dbus-broker.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=D-Bus System Message Bus 3 | Documentation=man:dbus-broker-launch(1) 4 | DefaultDependencies=false 5 | 6 | After=dbus.socket dbus-metrics.socket 7 | Before=basic.target shutdown.target 8 | Conflicts=shutdown.target 9 | Requires=dbus.socket 10 | Wants=dbus-metrics.socket 11 | 12 | [Service] 13 | Type=notify-reload 14 | OOMScoreAdjust=-900 15 | LimitNOFILE=16384 16 | ProtectSystem=full 17 | PrivateTmp=true 18 | PrivateDevices=true 19 | ExecStart=@bindir@/dbus-broker-launch --scope system --audit 20 | 21 | [Install] 22 | Alias=dbus.service 23 | -------------------------------------------------------------------------------- /.github/meson-arch-lib32.cross: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = ['gcc', '-m32'] 3 | cpp = ['g++', '-m32'] 4 | rust = ['rustc', '--target', 'i686-unknown-linux-gnu'] 5 | pkgconfig = 'i686-pc-linux-gnu-pkg-config' 6 | cups-config = 'cups-config' 7 | llvm-config = 'llvm-config32' 8 | strip = 'strip' 9 | rustdoc = 'rustdoc' 10 | 11 | [built-in options] 12 | libdir = 'lib32' 13 | 14 | [host_machine] 15 | system = 'linux' 16 | subsystem = 'linux' 17 | kernel = 'linux' 18 | cpu_family = 'x86' 19 | cpu = 'i686' 20 | endian = 'little' 21 | 22 | [properties] 23 | bindgen_clang_arguments = ['-target', 'i686-unknown-linux-gnu'] 24 | -------------------------------------------------------------------------------- /src/lib-crate.rs: -------------------------------------------------------------------------------- 1 | //! # Rust Bus Crate 2 | //! 3 | //! This library combines all Rust utilities of dbus-broker. It is linked to 4 | //! most executables of dbus-broker as utility library, relying on link-time 5 | //! garbage collection to drop any unneeded code. 6 | 7 | #![allow( 8 | clippy::redundant_field_names, 9 | )] 10 | 11 | extern crate alloc; 12 | extern crate core; 13 | 14 | pub mod session; 15 | pub mod util; 16 | 17 | #[cfg(test)] 18 | mod test { 19 | // Simple dummy to show the test-suite in the test results, even if 20 | // other tests are conditionally disabled. 21 | #[test] 22 | fn availability() { 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/util/test-nsec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test nanosecond time management 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include "util/nsec.h" 9 | 10 | static void test_nsec(void) { 11 | nsec_t n0, n1, n2; 12 | 13 | n0 = nsec_now(CLOCK_MONOTONIC); 14 | c_assert(n0 > 0); 15 | 16 | n1 = n0 + 8000; 17 | nsec_sleep(CLOCK_MONOTONIC, n1); 18 | 19 | n2 = nsec_now(CLOCK_MONOTONIC); 20 | c_assert(n2 > 0); 21 | c_assert(n2 > n0); 22 | c_assert(n2 - n0 >= 8000); 23 | } 24 | 25 | int main(int argc, char **argv) { 26 | test_nsec(); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /docs/meson.build: -------------------------------------------------------------------------------- 1 | custom_target( 2 | 'man-dbus-broker', 3 | output: 'dbus-broker.1', 4 | input: 'dbus-broker.rst', 5 | command: [prog_rst2man, '@INPUT@', '@OUTPUT@'], 6 | install: true, 7 | install_dir: join_paths(get_option('mandir'), 'man1') 8 | ) 9 | 10 | if use_launcher 11 | custom_target( 12 | 'man-dbus-broker-launch', 13 | output: 'dbus-broker-launch.1', 14 | input: 'dbus-broker-launch.rst', 15 | command: [prog_rst2man, '@INPUT@', '@OUTPUT@'], 16 | install: true, 17 | install_dir: join_paths(get_option('mandir'), 'man1') 18 | ) 19 | endif 20 | -------------------------------------------------------------------------------- /.github/codeql-custom.qls: -------------------------------------------------------------------------------- 1 | - import: "codeql-suites/cpp-lgtm.qls" 2 | from: "codeql/cpp-queries" 3 | - include: 4 | id: 5 | - "cpp/bad-strncpy-size" 6 | - "cpp/declaration-hides-variable" 7 | - "cpp/inconsistent-null-check" 8 | - "cpp/mistyped-function-arguments" 9 | - "cpp/nested-loops-with-same-variable" 10 | - "cpp/sizeof-side-effect" 11 | - "cpp/suspicious-pointer-scaling" 12 | - "cpp/suspicious-pointer-scaling-void" 13 | - "cpp/suspicious-sizeof" 14 | - "cpp/unsafe-strcat" 15 | - "cpp/unsafe-strncat" 16 | - "cpp/unsigned-difference-expression-compared-zero" 17 | - "cpp/unused-local-variable" 18 | tags: 19 | - "security" 20 | - "correctness" 21 | severity: "error" 22 | - exclude: 23 | id: 24 | - "cpp/useless-expression" 25 | - "cpp/fixme-comment" 26 | -------------------------------------------------------------------------------- /src/util/fs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * File System Helpers 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | typedef struct FsDirlist FsDirlist; 13 | 14 | enum { 15 | FS_DIR_FLAG_NO_HIDDEN = (1U << 0), 16 | }; 17 | 18 | struct FsDirlist { 19 | size_t n_entries; 20 | size_t n_allocated; 21 | struct dirent **entries; 22 | }; 23 | 24 | /* dirlist */ 25 | 26 | int fs_dirlist_new(FsDirlist **listp, size_t n_allocated); 27 | FsDirlist *fs_dirlist_free(FsDirlist *list); 28 | 29 | int fs_dirlist_push(FsDirlist *list, const struct dirent *de); 30 | void fs_dirlist_sort(FsDirlist *list); 31 | 32 | C_DEFINE_CLEANUP(FsDirlist *, fs_dirlist_free); 33 | 34 | /* dir */ 35 | 36 | int fs_dir_list(DIR *dir, FsDirlist **listp, unsigned int flags); 37 | -------------------------------------------------------------------------------- /subprojects/packagefiles/libc-rs/meson-detect-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Run `cargo metadata` to figure out the actual version of the local crate. 4 | # Print this on `stdout` and exit with 0. 5 | # 6 | # Paths to `cargo`, `jq`, the meson source root and subdir can be passed via 7 | # environment variables. 8 | 9 | set -eo pipefail 10 | 11 | BIN_CARGO="${BIN_CARGO:-"cargo"}" 12 | BIN_JQ="${BIN_JQ:-"jq"}" 13 | MESON_SOURCE_ROOT="${MESON_SOURCE_ROOT:-"."}" 14 | MESON_SUBDIR="${MESON_SUBDIR:-"."}" 15 | 16 | ${BIN_CARGO} \ 17 | metadata \ 18 | --format-version 1 \ 19 | --frozen \ 20 | --manifest-path "${MESON_SOURCE_ROOT}/${MESON_SUBDIR}/Cargo.toml" \ 21 | --no-deps \ 22 | | ${BIN_JQ} \ 23 | -cer \ 24 | '.packages | map(select(.name == "libc")) | .[0].version' 25 | -------------------------------------------------------------------------------- /test/integration/fuzz/dfuzzer/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # vi: set sw=4 ts=4 et tw=110: 3 | 4 | set -eux 5 | set -o pipefail 6 | 7 | TEST_USER="dfuzzer$SRANDOM" 8 | 9 | at_exit() { 10 | userdel -rf "$TEST_USER" 11 | } 12 | 13 | trap at_exit EXIT 14 | useradd "$TEST_USER" 15 | 16 | dbus-broker --version 17 | systemctl status --no-pager dbus-broker.service 18 | 19 | # Run dfuzzer on the PID 1's D-Bus interface. Drop privileges while doing so, since here we're interested in 20 | # the actual message broking instead of breaking systemd. 21 | # 22 | # org.freedesktop.systemd1 was picked here because its interface is very rich when it comes to function 23 | # signatures. Also, it's fuzzed in upstream by dfuzzer as well, which should make the test less prone to fails 24 | # due to issues on systemd's side. 25 | setpriv --reuid="$TEST_USER" --init-group -- dfuzzer -v --buffer-limit=10240 --bus org.freedesktop.systemd1 26 | -------------------------------------------------------------------------------- /src/util/dirwatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Directory Watch 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | typedef struct Dirwatch Dirwatch; 11 | 12 | enum { 13 | _DIRWATCH_E_SUCCESS, 14 | 15 | DIRWATCH_E_TRIGGERED, 16 | }; 17 | 18 | struct Dirwatch { 19 | int inotify_fd; 20 | }; 21 | 22 | #define DIRWATCH_NULL(_x) { \ 23 | .inotify_fd = -1, \ 24 | } 25 | 26 | /* dirwatch */ 27 | 28 | int dirwatch_new(Dirwatch **dwp); 29 | Dirwatch *dirwatch_free(Dirwatch *dw); 30 | 31 | int dirwatch_get_fd(Dirwatch *dw); 32 | int dirwatch_dispatch(Dirwatch *dw); 33 | int dirwatch_add(Dirwatch *dw, const char *path); 34 | 35 | /* inline helpers */ 36 | 37 | static inline void dirwatch_freep(Dirwatch **dw) { 38 | if (*dw) 39 | dirwatch_free(*dw); 40 | } 41 | -------------------------------------------------------------------------------- /src/util/audit-fallback.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Audit Helpers 3 | * 4 | * This fallback is used when libaudit is not available, and is meant to be 5 | * functionally equivalent to util/audit.c in case audit is disabled at 6 | * runtime, but without requiring the library. 7 | * 8 | * See util/audit.c for details. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "util/audit.h" 16 | #include "util/error.h" 17 | #include "util/misc.h" 18 | 19 | /* see src/util/audit.c for details */ 20 | int util_audit_drop_permissions(uint32_t uid, uint32_t gid) { 21 | return util_drop_permissions(uid, gid); 22 | } 23 | 24 | int util_audit_log(int type, const char *message, uid_t uid) { 25 | return UTIL_AUDIT_E_UNAVAILABLE; 26 | } 27 | 28 | int util_audit_init_global(void) { 29 | return 0; 30 | } 31 | 32 | void util_audit_deinit_global(void) { 33 | return; 34 | } 35 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('apparmor', type: 'boolean', value: false, description: 'AppArmor support') 2 | option('audit', type: 'boolean', value: false, description: 'Audit support') 3 | option('docs', type: 'boolean', value: false, description: 'Build documentation') 4 | option('doctest', type: 'boolean', value: false, description: 'Test code of documentation') 5 | option('launcher', type: 'boolean', value: true, description: 'Build compatibility launcher') 6 | option('linux-4-17', type: 'boolean', value: false, description: 'Require linux-4.17 at runtime and make use of its features') 7 | option('reference-test', type: 'boolean', value: false, description: 'Run test suite against reference implementation') 8 | option('selinux', type: 'boolean', value: false, description: 'SELinux support') 9 | option('system-console-users', type: 'array', value: [], description: 'Additional set of names of system-users to be considered at-console') 10 | option('tests', type: 'boolean', value: false, description: 'Include tests in the distribution') 11 | -------------------------------------------------------------------------------- /src/launch/test-nss-cache.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test NSS Cache 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include 9 | #include "launch/nss-cache.h" 10 | 11 | static void test_nss_cache(void) { 12 | _c_cleanup_(nss_cache_deinit) NSSCache cache = NSS_CACHE_INIT; 13 | uint32_t uid, gid; 14 | int r; 15 | 16 | r = nss_cache_get_uid(&cache, NULL, NULL, "com.example.InvalidUser"); 17 | c_assert(r == NSS_CACHE_E_INVALID_NAME); 18 | 19 | r = nss_cache_get_gid(&cache, NULL, "com.example.InvalidGroup"); 20 | c_assert(r == NSS_CACHE_E_INVALID_NAME); 21 | 22 | r = nss_cache_get_uid(&cache, &uid, &gid, "root"); 23 | c_assert(!r); 24 | c_assert(uid == 0); 25 | c_assert(gid == 0); 26 | 27 | r = nss_cache_get_gid(&cache, &gid, "root"); 28 | c_assert(!r); 29 | c_assert(gid == 0); 30 | } 31 | 32 | int main(int argc, char **argv) { 33 | test_nss_cache(); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /src/util/test-systemd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test systemd helpers 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include "util/systemd.h" 9 | 10 | static void test_systemd_escape_unit(void) { 11 | static const char * const from[] = { 12 | "foobar", 13 | "-foobar", 14 | "/foo/bar/", 15 | "\\foobar", 16 | ".foo.bar", 17 | }; 18 | static const char * const to[] = { 19 | "foobar", 20 | "\\x2dfoobar", 21 | "-foo-bar-", 22 | "\\x5cfoobar", 23 | "\\x2efoo.bar", 24 | }; 25 | 26 | for (size_t i = 0; i < C_ARRAY_SIZE(from); ++i) { 27 | _c_cleanup_(c_freep) char *e = NULL; 28 | int r; 29 | 30 | r = systemd_escape_unit(&e, from[i]); 31 | c_assert(!r); 32 | c_assert(!strcmp(e, to[i])); 33 | } 34 | } 35 | 36 | int main(int argc, char **argv) { 37 | test_systemd_escape_unit(); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /src/lib-generated.rs: -------------------------------------------------------------------------------- 1 | //! # Generated Rust Code 2 | //! 3 | //! This Rust crate bundles all generated Rust artifacts (mainly from 4 | //! `bindgen`) into a single Rust crate. It is then linked into the other 5 | //! Rust crates that need access to it. 6 | //! 7 | //! The main reason for separating generated code is to simplify build 8 | //! definitions: Rust relies on a structured source tree, which is hard to 9 | //! replicate when generating sources in a read-only source directory. Instead 10 | //! of copying everything, we separate generated sources into private crates 11 | //! and pull them in from all its users. 12 | 13 | #![no_std] 14 | 15 | extern crate alloc; 16 | extern crate core; 17 | 18 | #[allow( 19 | dead_code, 20 | non_camel_case_types, 21 | non_snake_case, 22 | non_upper_case_globals, 23 | )] 24 | mod generated { 25 | pub mod session_main; 26 | pub mod util_acct; 27 | } 28 | 29 | // The build system puts generated sources in a `./generated/*` sub-directory. 30 | // Lets strip this from any paths, given that the crate is already specific to 31 | // generated sources. 32 | pub use generated::*; 33 | -------------------------------------------------------------------------------- /test/image/dbrk-fedora.Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # dbrk-fedora - Fedora Test Image for DBus Broker 3 | # 4 | # A small Fedora-based image with dbus-broker included from a new build. Easy 5 | # way to test changes to dbus-broker on a real Fedora boot. 6 | # 7 | # Arguments: 8 | # 9 | # * DBRK_FROM="ghcr.io/bus1/dbrk-fedora-base:latest" 10 | # This controls the host container used as base for the image. 11 | # 12 | 13 | ARG DBRK_FROM="ghcr.io/bus1/dbrk-fedora-base:latest" 14 | FROM "${DBRK_FROM}" AS target 15 | 16 | # 17 | # Import our build sources and prepare the target environment. When finished, 18 | # we drop the build sources again, to keep the target layers small. 19 | # 20 | 21 | WORKDIR /dbrk 22 | COPY . src 23 | 24 | WORKDIR /dbrk/build 25 | RUN meson setup \ 26 | --prefix=/usr \ 27 | -Daudit=true \ 28 | -Dselinux=true \ 29 | . ../src 30 | RUN meson compile 31 | RUN meson test 32 | RUN meson install 33 | 34 | WORKDIR /dbrk/home 35 | RUN rm -rf /dbrk/src /dbrk/build 36 | -------------------------------------------------------------------------------- /src/bus/test-reply.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test Reply Tracking 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "bus/reply.h" 11 | 12 | static void test_basic(void) { 13 | ReplyRegistry registry; 14 | ReplyOwner owner; 15 | ReplySlot *slot1, *slot2; 16 | int r; 17 | 18 | reply_registry_init(®istry); 19 | reply_owner_init(&owner); 20 | 21 | r = reply_slot_new(&slot1, ®istry, &owner, NULL, NULL, 1, 1); 22 | c_assert(!r); 23 | 24 | r = reply_slot_new(&slot1, ®istry, &owner, NULL, NULL, 1, 1); 25 | c_assert(r == REPLY_E_EXISTS); 26 | 27 | slot2 = reply_slot_get_by_id(®istry, 1, 1); 28 | c_assert(slot2 == slot1); 29 | 30 | slot2 = reply_slot_get_by_id(®istry, 1, 2); 31 | c_assert(!slot2); 32 | 33 | slot2 = reply_slot_get_by_id(®istry, 2, 1); 34 | c_assert(!slot2); 35 | 36 | reply_slot_free(slot1); 37 | reply_owner_deinit(&owner); 38 | reply_registry_deinit(®istry); 39 | } 40 | 41 | int main(int argc, char **argv) { 42 | test_basic(); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /src/util/sampler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Sampler Helper 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define SAMPLER_TIMESTAMP_INVALID ((uint64_t) -1) 12 | 13 | typedef struct Sampler Sampler; 14 | 15 | struct Sampler { 16 | uint64_t count; 17 | uint64_t sum; 18 | uint64_t minimum; 19 | uint64_t maximum; 20 | uint64_t average; 21 | 22 | /* internal state */ 23 | clockid_t id; 24 | uint64_t timestamp; 25 | uint64_t sum_of_squares; 26 | }; 27 | 28 | #define SAMPLER_INIT(_id) { \ 29 | .minimum = (uint64_t) -1, \ 30 | .id = (_id), \ 31 | .timestamp = SAMPLER_TIMESTAMP_INVALID, \ 32 | } 33 | 34 | void sampler_init(Sampler *sampler, clockid_t id); 35 | void sampler_deinit(Sampler *sampler); 36 | 37 | uint64_t sampler_get_time(Sampler *sampler); 38 | void sampler_sample_add(Sampler *sampler, uint64_t timestamp); 39 | 40 | void sampler_sample_start(Sampler *sampler); 41 | void sampler_sample_end(Sampler *sampler); 42 | 43 | double sampler_read_standard_deviation(Sampler *sampler); 44 | -------------------------------------------------------------------------------- /src/util/test-dirwatch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test Directory Watch 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include "util/dirwatch.h" 9 | 10 | static void test_basic(void) { 11 | int r, fd; 12 | 13 | /* test cleanup helper */ 14 | { 15 | _c_cleanup_(dirwatch_freep) _c_unused_ Dirwatch *dw1 = NULL, *dw2 = NULL; 16 | 17 | /* prevent 'unused variable' warning */ 18 | dw1 = NULL; 19 | 20 | r = dirwatch_new(&dw2); 21 | c_assert(!r); 22 | } 23 | 24 | /* test no-op dispatcher */ 25 | { 26 | _c_cleanup_(dirwatch_freep) Dirwatch *dw = NULL; 27 | 28 | r = dirwatch_new(&dw); 29 | c_assert(!r); 30 | 31 | fd = dirwatch_get_fd(dw); 32 | c_assert(fd >= 0); 33 | 34 | r = dirwatch_dispatch(dw); 35 | c_assert(!r); 36 | 37 | r = dirwatch_add(dw, "."); 38 | c_assert(!r); 39 | 40 | r = dirwatch_dispatch(dw); 41 | c_assert(!r); 42 | } 43 | } 44 | 45 | int main(int argc, char **argv) { 46 | test_basic(); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /src/dbus/address.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * D-Bus Peer Addresses 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | typedef struct Address Address; 11 | 12 | enum { 13 | ADDRESS_TYPE_OTHER, 14 | ADDRESS_TYPE_ID, 15 | ADDRESS_TYPE_NAME, 16 | _ADDRESS_TYPE_N, 17 | }; 18 | 19 | /* invalid ID address */ 20 | #define ADDRESS_ID_INVALID (ULLONG_MAX) 21 | /* max length of string representation of ID addresses */ 22 | #define ADDRESS_ID_STRING_MAX (3 + C_DECIMAL_MAX(uint64_t)) 23 | 24 | struct Address { 25 | unsigned int type; 26 | char buffer[ADDRESS_ID_STRING_MAX + 1]; 27 | union { 28 | uint64_t id; 29 | const char *name; 30 | }; 31 | }; 32 | 33 | #define ADDRESS_NULL { .type = _ADDRESS_TYPE_OTHER } 34 | #define ADDRESS_INIT_ID(_id) { .type = ADDRESS_TYPE_ID, .id = (_id) } 35 | #define ADDRESS_INIT_NAME(_name) { .type = ADDRESS_TYPE_NAME, .name = (_name) } 36 | 37 | void address_init_from_id(Address *address, uint64_t id); 38 | void address_init_from_name(Address *address, const char *name); 39 | 40 | void address_from_string(Address *address, const char *string); 41 | const char *address_to_string(Address *address); 42 | 43 | void address_write(Address *address, char *buffer, size_t n_buffer); 44 | -------------------------------------------------------------------------------- /.github/workflows/coverity.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Coverity Analysis 3 | # 4 | # This workflow analyzes the project via the Coverity Scan Analysis Tool, and 5 | # uploads the final artifacts to the Coverity servers for further analysis. 6 | # 7 | # This workflow can be triggered manually. It is also run in regular intervals. 8 | # Note that Coverity limits the amount of builds allowed, so ensure you do not 9 | # exceed this with manual triggers. 10 | # 11 | 12 | name: "Coverity Analysis" 13 | 14 | on: 15 | schedule: 16 | - cron: '0 0 * * *' # daily at midnight 17 | workflow_dispatch: 18 | 19 | defaults: 20 | run: 21 | shell: "bash" 22 | 23 | permissions: 24 | contents: read 25 | 26 | jobs: 27 | analysis: 28 | name: "Coverity Analysis" 29 | 30 | if: github.repository == 'bus1/dbus-broker' 31 | 32 | container: 33 | image: "ghcr.io/readaheadeu/rae-ci-ubuntu:latest" 34 | options: --user root 35 | env: 36 | COVERITY_EMAIL: "no-reply@readahead.eu" 37 | COVERITY_TOKEN: ${{ secrets.DEPLOY_COVERITY_TOKEN }} 38 | runs-on: "ubuntu-latest" 39 | 40 | steps: 41 | - name: "Fetch Sources" 42 | uses: actions/checkout@v4 43 | 44 | - name: "Run Analysis" 45 | run: make coverity-scan 46 | 47 | - name: "Upload Analysis" 48 | run: make coverity-upload 49 | -------------------------------------------------------------------------------- /src/util/fdlist.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * File-Descriptor List 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | typedef struct FDList FDList; 12 | 13 | struct FDList { 14 | bool consumed : 1; 15 | struct cmsghdr cmsg[]; 16 | }; 17 | 18 | int fdlist_new_with_fds(FDList **listp, const int *fds, size_t n_fds); 19 | int fdlist_new_consume_fds(FDList **listp, const int *fds, size_t n_fds); 20 | int fdlist_new_dup_fds(FDList **listp, const int *fds, size_t n_fds); 21 | FDList *fdlist_free(FDList *list); 22 | void fdlist_truncate(FDList *list, size_t n_fds); 23 | int fdlist_steal(FDList *list, size_t index); 24 | 25 | C_DEFINE_CLEANUP(FDList *, fdlist_free); 26 | 27 | /* inline helpers */ 28 | 29 | static inline int *fdlist_data(FDList *list) { 30 | return list ? (int *)CMSG_DATA(list->cmsg) : NULL; 31 | } 32 | 33 | static inline size_t fdlist_count(FDList *list) { 34 | return list ? (list->cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int) : 0; 35 | } 36 | 37 | static inline size_t fdlist_size(FDList *list) { 38 | return list ? CMSG_SPACE(fdlist_count(list) * sizeof(int)) : 0; 39 | } 40 | 41 | static inline int fdlist_get(FDList *list, size_t index) { 42 | return index < fdlist_count(list) ? fdlist_data(list)[index] : -1; 43 | } 44 | -------------------------------------------------------------------------------- /src/bus/metrics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Metrics Listener 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "util/dispatch.h" 11 | 12 | typedef struct Bus Bus; 13 | typedef struct Metrics Metrics; 14 | typedef struct MetricsClient MetricsClient; 15 | 16 | struct MetricsClient { 17 | CList metrics_link; 18 | int socket_fd; 19 | DispatchFile socket_file; 20 | uint8_t *buffer; 21 | size_t i_buffer; 22 | size_t n_buffer; 23 | }; 24 | 25 | struct Metrics { 26 | Bus *bus; 27 | int socket_fd; 28 | DispatchFile socket_file; 29 | CList client_list; 30 | }; 31 | 32 | #define METRICS_NULL(_x) { \ 33 | .socket_fd = -1, \ 34 | .socket_file = DISPATCH_FILE_NULL((_x).socket_file), \ 35 | .client_list = C_LIST_INIT((_x).client_list), \ 36 | } 37 | 38 | int metrics_init_with_fd(Metrics *metrics, 39 | Bus *bus, 40 | DispatchContext *dispatcher, 41 | int socket_fd); 42 | void metrics_deinit(Metrics *metrics); 43 | 44 | C_DEFINE_CLEANUP(Metrics *, metrics_deinit); 45 | -------------------------------------------------------------------------------- /src/util/selinux.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Bus SELinux Helpers 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | typedef struct BusSELinuxRegistry BusSELinuxRegistry; 11 | typedef struct Log Log; 12 | 13 | enum { 14 | _SELINUX_E_SUCCESS, 15 | 16 | SELINUX_E_DENIED, 17 | }; 18 | 19 | bool bus_selinux_is_enabled(void); 20 | bool bus_selinux_is_enforcing(void); 21 | const char *bus_selinux_policy_root(void); 22 | 23 | int bus_selinux_registry_new(BusSELinuxRegistry **registryp, const char *fallback_context); 24 | BusSELinuxRegistry *bus_selinux_registry_ref(BusSELinuxRegistry *registry); 25 | BusSELinuxRegistry *bus_selinux_registry_unref(BusSELinuxRegistry *registry); 26 | 27 | C_DEFINE_CLEANUP(BusSELinuxRegistry *, bus_selinux_registry_unref); 28 | 29 | int bus_selinux_registry_add_name(BusSELinuxRegistry *registry, const char *name, const char *context); 30 | 31 | int bus_selinux_check_own(BusSELinuxRegistry *registry, 32 | const char *context_owner, 33 | const char *name); 34 | int bus_selinux_check_send(BusSELinuxRegistry *registry, 35 | const char *context_sender, 36 | const char *context_receiver); 37 | 38 | int bus_selinux_init_global(Log *log); 39 | void bus_selinux_deinit_global(void); 40 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Publish Releases 3 | # 4 | # This workflow can be manually triggered and will then publish the 5 | # specified release to the configured release channels. 6 | # 7 | 8 | name: "Publish Releases" 9 | 10 | on: 11 | workflow_dispatch: 12 | inputs: 13 | tag: 14 | description: "Git-Tag to Publish" 15 | default: "" 16 | required: true 17 | 18 | concurrency: 19 | cancel-in-progress: false 20 | group: "publish" 21 | 22 | defaults: 23 | run: 24 | shell: "bash" 25 | 26 | jobs: 27 | publish: 28 | name: "Publish GitHub" 29 | 30 | env: 31 | CTX_GITHUB_EVENT_INPUTS_TAG: ${{ github.event.inputs.tag }} 32 | permissions: 33 | contents: write 34 | runs-on: "ubuntu-latest" 35 | 36 | steps: 37 | - name: "Verify Input" 38 | run: | 39 | if [[ -z "${CTX_GITHUB_EVENT_INPUTS_TAG}" ]] ; then 40 | echo "error: empty git-tag specified as input" 41 | exit 1 42 | fi 43 | 44 | - name: "Clone Repository" 45 | uses: actions/checkout@v4 46 | with: 47 | ref: ${{ github.event.inputs.tag }} 48 | 49 | - name: "Publish GitHub" 50 | uses: readaheadeu/rae-actions/publish-github@v1 51 | with: 52 | ghtoken: ${{ secrets.GITHUB_TOKEN }} 53 | tag: ${{ github.event.inputs.tag }} 54 | -------------------------------------------------------------------------------- /src/bus/listener.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Socket Listener 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "bus/policy.h" 11 | #include "util/dispatch.h" 12 | 13 | typedef struct Bus Bus; 14 | typedef struct DispatchContext DispatchContext; 15 | typedef struct Listener Listener; 16 | 17 | struct Listener { 18 | Bus *bus; 19 | char guid[16]; 20 | int socket_fd; 21 | DispatchFile socket_file; 22 | PolicyRegistry *policy; 23 | CList peer_list; 24 | }; 25 | 26 | #define LISTENER_NULL(_x) { \ 27 | .socket_fd = -1, \ 28 | .socket_file = DISPATCH_FILE_NULL((_x).socket_file), \ 29 | .peer_list = C_LIST_INIT((_x).peer_list), \ 30 | } 31 | 32 | int listener_init_with_fd(Listener *listener, 33 | Bus *bus, 34 | DispatchContext *dispatcher, 35 | int socket_fd, 36 | PolicyRegistry *policy); 37 | void listener_deinit(Listener *listener); 38 | 39 | int listener_set_policy(Listener *listener, PolicyRegistry *policy); 40 | 41 | C_DEFINE_CLEANUP(Listener *, listener_deinit); 42 | -------------------------------------------------------------------------------- /src/launch/nss-cache.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * NSS Cache 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | typedef struct NSSCache NSSCache; 12 | 13 | enum { 14 | _NSS_CACHE_E_SUCCESS, 15 | 16 | NSS_CACHE_E_INVALID_NAME, 17 | }; 18 | 19 | struct NSSCache { 20 | CRBTree user_tree; 21 | CRBTree uid_tree; 22 | CRBTree group_tree; 23 | CRBTree gid_tree; 24 | }; 25 | 26 | #define NSS_CACHE_INIT { \ 27 | .user_tree = C_RBTREE_INIT, \ 28 | .uid_tree = C_RBTREE_INIT, \ 29 | .group_tree = C_RBTREE_INIT, \ 30 | .gid_tree = C_RBTREE_INIT, \ 31 | } 32 | 33 | /* nss cache */ 34 | 35 | void nss_cache_init(NSSCache *cache); 36 | void nss_cache_deinit(NSSCache *cache); 37 | 38 | int nss_cache_populate(NSSCache *cache); 39 | 40 | int nss_cache_get_uid(NSSCache *cache, uint32_t *uidp, uint32_t *gidp, const char *user); 41 | int nss_cache_get_gid(NSSCache *cache, uint32_t *gidp, const char *group); 42 | 43 | int nss_cache_resolve_system_console_users(NSSCache *nss_cache, uint32_t **uidsp, size_t *n_uidsp); 44 | -------------------------------------------------------------------------------- /src/util/test-error.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test Error Handling 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include "util/error.h" 9 | 10 | static void test_basic(void) { 11 | int r; 12 | 13 | /* error_origin(0) is a no-op */ 14 | r = error_origin(0); 15 | c_assert(!r); 16 | 17 | /* make sure no double evaluation happens */ 18 | r = error_origin(--r); 19 | c_assert(r == -1); 20 | 21 | /* error_origin() always returns <=0 */ 22 | r = error_origin(1); 23 | c_assert(r < 0); 24 | 25 | /* error_trace(0) is a no-op */ 26 | r = error_trace(0); 27 | c_assert(!r); 28 | 29 | /* make sure no double evaluation happens */ 30 | r = error_trace(--r); 31 | c_assert(r == -1); 32 | 33 | /* error_trace() never modifies the error code */ 34 | r = error_trace(1); 35 | c_assert(r == 1); 36 | 37 | /* error_fold(0) is a no-op */ 38 | r = error_fold(0); 39 | c_assert(!r); 40 | 41 | /* make sure no double evaluation happens */ 42 | r = error_fold(--r); 43 | c_assert(r == -1); 44 | 45 | /* error_fold() never returns positive codes */ 46 | r = error_fold(1); 47 | c_assert(r < 0); 48 | } 49 | 50 | int main(int argc, char **argv) { 51 | test_basic(); 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /src/util/apparmor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Bus AppArmor Helpers 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | typedef struct NameSet NameSet; 11 | 12 | typedef struct BusAppArmorRegistry BusAppArmorRegistry; 13 | 14 | enum { 15 | _BUS_APPARMOR_E_SUCCESS, 16 | 17 | BUS_APPARMOR_E_DENIED, 18 | }; 19 | 20 | int bus_apparmor_is_enabled(bool *enabledp); 21 | int bus_apparmor_dbus_supported(bool *supportedp); 22 | 23 | int bus_apparmor_registry_new(struct BusAppArmorRegistry **registryp, const char *fallback_context); 24 | BusAppArmorRegistry *bus_apparmor_registry_ref(BusAppArmorRegistry *registry); 25 | BusAppArmorRegistry *bus_apparmor_registry_unref(BusAppArmorRegistry *registry); 26 | 27 | C_DEFINE_CLEANUP(BusAppArmorRegistry *, bus_apparmor_registry_unref); 28 | 29 | int bus_apparmor_set_bus_type(BusAppArmorRegistry *registry, const char *bustype); 30 | 31 | int bus_apparmor_check_own(struct BusAppArmorRegistry *registry, const char *context, 32 | const char *name); 33 | int bus_apparmor_check_send(BusAppArmorRegistry *registry, 34 | const char *sender_context, const char *receiver_context, 35 | NameSet *subject, uint64_t subject_id, 36 | const char *path, const char *interface, const char *method); 37 | int bus_apparmor_check_eavesdrop(BusAppArmorRegistry *registry, const char *context); 38 | -------------------------------------------------------------------------------- /test/dbus/util-message.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Raw message helpers 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | void test_message_append_sasl(void **buf, size_t *n_buf); 11 | void test_message_append_hello(void **buf, size_t *n_buf); 12 | void test_message_append_broadcast(void **buf, 13 | size_t *n_buf, 14 | uint64_t sender_id); 15 | void test_message_append_signal(void **buf, 16 | size_t *n_buf, 17 | uint64_t sender_id, 18 | uint64_t destination_id); 19 | void test_message_append_ping(void **buf, 20 | size_t *n_buf, 21 | uint32_t serial, 22 | uint64_t sender_id, 23 | uint64_t destination_id); 24 | void test_message_append_ping2(void **buf, 25 | size_t *n_buf, 26 | uint32_t serial, 27 | const char *sender, 28 | const char *destination); 29 | void test_message_append_pong(void **buf, 30 | size_t *n_buf, 31 | uint32_t serial, 32 | uint32_t reply_serial, 33 | uint64_t sender_id, 34 | uint64_t destination_id); 35 | -------------------------------------------------------------------------------- /src/util/test-fs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test File System Helpers 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "util/fs.h" 12 | #include "util/string.h" 13 | 14 | static void test_dir_list(void) { 15 | _c_cleanup_(c_closedirp) DIR *dir = NULL; 16 | _c_cleanup_(fs_dirlist_freep) FsDirlist *list = NULL; 17 | const char *req[] = { 18 | "bin", 19 | "dev", 20 | "etc", 21 | "lib", 22 | "proc", 23 | "run", 24 | "sys", 25 | "tmp", 26 | "usr", 27 | "var", 28 | }; 29 | size_t i, pos; 30 | int r; 31 | 32 | dir = opendir("/"); 33 | c_assert(dir); 34 | 35 | r = fs_dir_list(dir, &list, 0); 36 | c_assert(!r); 37 | c_assert(list); 38 | c_assert(list->n_entries > 0); 39 | 40 | /* Verify all expected entries are found in order. */ 41 | pos = 0; 42 | for (i = 0; i < list->n_entries; ++i) { 43 | if (pos >= C_ARRAY_SIZE(req)) 44 | break; 45 | 46 | if (string_equal(list->entries[i]->d_name, req[pos])) 47 | ++pos; 48 | } 49 | c_assert(pos == C_ARRAY_SIZE(req)); 50 | } 51 | 52 | int main(int argc, char **argv) { 53 | test_dir_list(); 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /src/launch/launcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Launcher 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "util/dirwatch.h" 13 | #include "util/log.h" 14 | #include "util/misc.h" 15 | 16 | typedef struct Launcher Launcher; 17 | 18 | enum { 19 | _LAUNCHER_E_SUCCESS, 20 | 21 | LAUNCHER_E_INVALID_CONFIG, 22 | LAUNCHER_E_INVALID_SERVICE_FILE, 23 | }; 24 | 25 | struct Launcher { 26 | sd_event *event; 27 | sd_bus *bus_controller; 28 | sd_bus *bus_regular; 29 | Log log; 30 | int fd_listen; 31 | int fd_metrics; 32 | bool audit; 33 | bool user_scope; 34 | char *configfile; 35 | Dirwatch *dirwatch; 36 | sd_event_source *dirwatch_src; 37 | CRBTree services; 38 | CRBTree services_by_name; 39 | uint64_t service_ids; 40 | uint32_t uid; 41 | uint32_t gid; 42 | uint64_t max_bytes; 43 | uint64_t max_fds; 44 | uint64_t max_matches; 45 | bool at_console; 46 | }; 47 | 48 | int launcher_new( 49 | Launcher **launcherp, 50 | int listen_fd, 51 | int metrics_fd, 52 | bool audit, 53 | const char *configfile, 54 | bool user_scope 55 | ); 56 | Launcher *launcher_free(Launcher *launcher); 57 | 58 | C_DEFINE_CLEANUP(Launcher *, launcher_free); 59 | 60 | int launcher_listen_inherit(Launcher *launcher); 61 | int launcher_run(Launcher *launcher); 62 | -------------------------------------------------------------------------------- /src/catalog/catalog-ids.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Catalog Message Identifiers 5 | * 6 | * This file contains the statically generated identifiers for our log 7 | * messages. These allow to store additional metadata explaining a log message 8 | * in the systemd catalog. Each constant must have a corresponding entry in 9 | * dbus-broker.catalog file. 10 | */ 11 | 12 | #define DBUS_BROKER_CATALOG_ACTIVATE_NO_UNIT "7fc63312330b479bb32e598d47cef1a8" 13 | #define DBUS_BROKER_CATALOG_ACTIVATE_MASKED_UNIT "ee9799dab1e24d81b7bee7759a543e1b" 14 | #define DBUS_BROKER_CATALOG_BROKER_EXITED "a0fa58cafd6f4f0c8d003d16ccf9e797" 15 | #define DBUS_BROKER_CATALOG_DIRWATCH "c8c6cde1c488439aba371a664353d9d8" 16 | #define DBUS_BROKER_CATALOG_DISPATCH_STATS "8af3357071af4153af414daae07d38e7" 17 | #define DBUS_BROKER_CATALOG_NO_SOPEERGROUP "199d4300277f495f84ba4028c984214c" 18 | #define DBUS_BROKER_CATALOG_PROTOCOL_VIOLATION "b209c0d9d1764ab38d13b8e00d1784d6" 19 | #define DBUS_BROKER_CATALOG_QUOTA_QUEUE_REPLY "0691c10b72b14341955a75873c867e29" 20 | #define DBUS_BROKER_CATALOG_QUOTA_DEQUEUE "2809c31175b84192b574d2a3da9fa8da" 21 | #define DBUS_BROKER_CATALOG_RECEIVE_FAILED "6fa70fa776044fa28be7a21daf42a108" 22 | #define DBUS_BROKER_CATALOG_SERVICE_FAILED_OPEN "0ce0fa61d1a9433dabd67417f6b8e535" 23 | #define DBUS_BROKER_CATALOG_SERVICE_INVALID "24dc708d9e6a4226a3efe2033bb744de" 24 | #define DBUS_BROKER_CATALOG_SIGHUP "f15d2347662d483ea9bcd8aa1a691d28" 25 | -------------------------------------------------------------------------------- /.github/workflows/fuzz.yml: -------------------------------------------------------------------------------- 1 | name: Code Fuzzer 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | push: 7 | branches: [main] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | fuzzer: 14 | name: OSS-Fuzz Code Testing 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ matrix.architecture }}-${{ github.ref }} 17 | cancel-in-progress: true 18 | if: github.repository == 'bus1/dbus-broker' 19 | runs-on: ubuntu-latest 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | architecture: [x86_64] 25 | sanitizer: [address, undefined, memory] 26 | include: 27 | - architecture: i386 28 | sanitizer: address 29 | 30 | steps: 31 | - name: Build Fuzzers (${{ matrix.sanitizer }}) 32 | id: build 33 | uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master 34 | with: 35 | allowed-broken-targets-percentage: 0 36 | architecture: ${{ matrix.architecture }} 37 | dry-run: false 38 | oss-fuzz-project-name: 'dbus-broker' 39 | sanitizer: ${{ matrix.sanitizer }} 40 | - name: Run Fuzzers (${{ matrix.sanitizer }}) 41 | uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master 42 | with: 43 | dry-run: false 44 | fuzz-seconds: 600 45 | oss-fuzz-project-name: 'dbus-broker' 46 | sanitizer: ${{ matrix.sanitizer }} 47 | - name: Upload Crash 48 | if: failure() && steps.build.outcome == 'success' 49 | uses: actions/upload-artifact@v4 50 | with: 51 | name: ${{ matrix.sanitizer }}-${{ matrix.architecture }}-artifacts 52 | path: ./out/artifacts 53 | -------------------------------------------------------------------------------- /src/util/selinux-fallback.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Bus SELinux Fallback Helpers 3 | * 4 | * This fallback is used when libselinux is not available, and is meant to be 5 | * functionally equivalent to util/selinux.c in case SELinux is disabled, but 6 | * without requiring the library. 7 | * 8 | * See util/selinux.c for details. 9 | */ 10 | 11 | #include 12 | #include 13 | #include "util/selinux.h" 14 | 15 | bool bus_selinux_is_enabled(void) { 16 | return false; 17 | } 18 | 19 | bool bus_selinux_is_enforcing(void) { 20 | return false; 21 | } 22 | 23 | const char *bus_selinux_policy_root(void) { 24 | return NULL; 25 | } 26 | 27 | int bus_selinux_registry_new(BusSELinuxRegistry **registryp, const char *fallback_context) { 28 | *registryp = NULL; 29 | return 0; 30 | } 31 | 32 | BusSELinuxRegistry *bus_selinux_registry_ref(BusSELinuxRegistry *registry) { 33 | return NULL; 34 | } 35 | 36 | BusSELinuxRegistry *bus_selinux_registry_unref(BusSELinuxRegistry *registry) { 37 | return NULL; 38 | } 39 | 40 | int bus_selinux_registry_add_name(BusSELinuxRegistry *registry, const char *name, const char *context) { 41 | return 0; 42 | } 43 | 44 | int bus_selinux_check_own(BusSELinuxRegistry *registry, 45 | const char *owner_context, 46 | const char *name) { 47 | return 0; 48 | } 49 | 50 | int bus_selinux_check_send(BusSELinuxRegistry *registry, 51 | const char *context_sender, 52 | const char *context_receiver) { 53 | return 0; 54 | } 55 | 56 | int bus_selinux_init_global(Log *log) { 57 | return 0; 58 | } 59 | 60 | void bus_selinux_deinit_global(void) { 61 | return; 62 | } 63 | -------------------------------------------------------------------------------- /src/bus/reply.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Reply Registry 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "util/user.h" 12 | 13 | typedef struct ReplySlot ReplySlot; 14 | typedef struct ReplyRegistry ReplyRegistry; 15 | typedef struct ReplyOwner ReplyOwner; 16 | 17 | enum { 18 | _REPLY_E_SUCCESS, 19 | 20 | REPLY_E_EXISTS, 21 | REPLY_E_QUOTA, 22 | }; 23 | 24 | struct ReplySlot { 25 | ReplyRegistry *registry; 26 | ReplyOwner *owner; 27 | UserCharge charge; 28 | uint64_t id; 29 | uint32_t serial; 30 | CRBNode registry_node; 31 | CList owner_link; 32 | }; 33 | 34 | struct ReplyRegistry { 35 | CRBTree reply_tree; 36 | }; 37 | 38 | #define REPLY_REGISTRY_INIT { \ 39 | .reply_tree = C_RBTREE_INIT, \ 40 | } 41 | 42 | struct ReplyOwner { 43 | CList reply_list; 44 | }; 45 | 46 | #define REPLY_OWNER_INIT(_x) { \ 47 | .reply_list = C_LIST_INIT((_x).reply_list), \ 48 | } 49 | 50 | int reply_slot_new(ReplySlot **replyp, ReplyRegistry *registry, ReplyOwner *owner, User *user, User *actor, uint64_t id, uint32_t serial); 51 | ReplySlot *reply_slot_free(ReplySlot *slot); 52 | 53 | ReplySlot *reply_slot_get_by_id(ReplyRegistry *registry, uint64_t id, uint32_t serial); 54 | 55 | void reply_registry_init(ReplyRegistry *registry); 56 | void reply_registry_deinit(ReplyRegistry *registry); 57 | 58 | void reply_owner_init(ReplyOwner *owner); 59 | void reply_owner_deinit(ReplyOwner *owner); 60 | 61 | void reply_owner_get_stats(ReplyOwner *owner, unsigned int *n_objectsp); 62 | 63 | C_DEFINE_CLEANUP(ReplySlot *, reply_slot_free); 64 | -------------------------------------------------------------------------------- /src/util/acct.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Resource Accounting 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | typedef uint32_t acct_id_t; 11 | typedef uint64_t acct_value_t; 12 | 13 | typedef struct Acct Acct; 14 | typedef struct AcctActor AcctActor; 15 | typedef struct AcctCharge AcctCharge; 16 | typedef struct AcctUser AcctUser; 17 | 18 | enum: int { 19 | _ACCT_E_SUCCESS, 20 | 21 | ACCT_E_QUOTA, 22 | }; 23 | 24 | enum: size_t { 25 | ACCT_SLOT_BYTES, 26 | ACCT_SLOT_FDS, 27 | ACCT_SLOT_MATCHES, 28 | ACCT_SLOT_OBJECTS, 29 | _ACCT_SLOT_N, 30 | }; 31 | 32 | /* charge */ 33 | 34 | struct AcctCharge { 35 | void *trace; 36 | acct_value_t amount[_ACCT_SLOT_N]; 37 | }; 38 | 39 | #define ACCT_CHARGE_INIT {} 40 | 41 | void acct_charge_init(AcctCharge *charge); 42 | void acct_charge_deinit(AcctCharge *charge); 43 | 44 | /* acct */ 45 | 46 | int acct_new(Acct **acctp, const acct_value_t (*maxima)[_ACCT_SLOT_N]); 47 | Acct *acct_free(Acct *acct); 48 | 49 | int acct_ref_user(Acct *acct, AcctUser **userp, acct_id_t id); 50 | 51 | /* user */ 52 | 53 | AcctUser *acct_user_ref(AcctUser *user); 54 | AcctUser *acct_user_unref(AcctUser *user); 55 | 56 | int acct_user_new_actor(AcctUser *user, AcctActor **actorp); 57 | int acct_user_charge( 58 | AcctUser *user, 59 | AcctCharge *charge, 60 | AcctActor *claimant, 61 | const acct_value_t (*const amount)[_ACCT_SLOT_N] 62 | ); 63 | 64 | /* actor */ 65 | 66 | AcctActor *acct_actor_ref(AcctActor *actor); 67 | AcctActor *acct_actor_unref(AcctActor *actor); 68 | 69 | int acct_actor_charge( 70 | AcctActor *actor, 71 | AcctCharge *charge, 72 | AcctActor *claimant, 73 | const acct_value_t (*const amount)[_ACCT_SLOT_N] 74 | ); 75 | -------------------------------------------------------------------------------- /src/dbus/test-address.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test Address Handling 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include "dbus/address.h" 9 | 10 | static void test_basic(void) { 11 | Address addr; 12 | 13 | address_init_from_id(&addr, 0); 14 | c_assert(addr.type == ADDRESS_TYPE_ID); 15 | c_assert(!strcmp(address_to_string(&addr), ":1.0")); 16 | 17 | address_init_from_id(&addr, ADDRESS_ID_INVALID - 1); 18 | c_assert(addr.type == ADDRESS_TYPE_ID); 19 | c_assert(!strcmp(address_to_string(&addr), ":1.18446744073709551614")); 20 | 21 | address_from_string(&addr, ":1.0"); 22 | c_assert(addr.type == ADDRESS_TYPE_ID); 23 | c_assert(addr.id == 0); 24 | 25 | /* Would map to ADDRESS_ID_INVALID, thus it must be rejected. */ 26 | address_from_string(&addr, ":1.18446744073709551615"); 27 | c_assert(addr.type == ADDRESS_TYPE_OTHER); 28 | 29 | /* Out of range of uint64_t. */ 30 | address_from_string(&addr, ":1.18446744073709551616"); 31 | c_assert(addr.type == ADDRESS_TYPE_OTHER); 32 | 33 | /* Non-decimal number must be rejected. */ 34 | address_from_string(&addr, ":1.184467440737095516a0"); 35 | c_assert(addr.type == ADDRESS_TYPE_OTHER); 36 | 37 | /* Empty addresses are invalid. */ 38 | address_from_string(&addr, ""); 39 | c_assert(addr.type == ADDRESS_TYPE_OTHER); 40 | 41 | /* Non-1 namespaces are invalid. */ 42 | address_from_string(&addr, ":2.0"); 43 | c_assert(addr.type == ADDRESS_TYPE_OTHER); 44 | 45 | /* Well-known names become type NAME */ 46 | address_from_string(&addr, "foo.bar"); 47 | c_assert(addr.type == ADDRESS_TYPE_NAME); 48 | } 49 | 50 | int main(int argc, char **argv) { 51 | test_basic(); 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /src/util/error.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Error Handling 5 | * 6 | * All our API calls return integers to signal errors or success. Negative 7 | * error-codes are fatal errors that the caller should forward unchanged. 8 | * Positive error-codes are API errors that have documented behavior and must 9 | * be caught and handled by the caller. 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | int error_slow_origin(int r, const char *function, const char *file, int line); 16 | int error_slow_trace(int r, const char *function, const char *file, int line); 17 | int error_slow_fold(int r, const char *function, const char *file, int line); 18 | 19 | /** 20 | * error_origin() - fast-path of error_slow_origin() 21 | * @r: error code 22 | * 23 | * This is the fast-path of error_slow_origin(). See its description for 24 | * details. 25 | * 26 | * Return: 0 or negative error code, depending on @r. 27 | */ 28 | #define error_origin(r) C_CC_MACRO1(ERROR_ORIGIN, (r)) 29 | #define ERROR_ORIGIN(r) (_c_likely_(!r) ? 0 : error_slow_origin(r, __func__, __FILE__, __LINE__)) 30 | 31 | /** 32 | * error_trace() - fast-path of error_slow_trace() 33 | * @r: error code 34 | * 35 | * This is the fast-path of error_slow_trace(). See its description for 36 | * details. 37 | * 38 | * Return: @r is returned. 39 | */ 40 | #define error_trace(r) C_CC_MACRO1(ERROR_TRACE, (r)) 41 | #define ERROR_TRACE(r) (_c_likely_(r >= 0) ? r : error_slow_trace(r, __func__, __FILE__, __LINE__)) 42 | 43 | /** 44 | * error_fold() - fast-path of error_slow_fold() 45 | * @r: error code 46 | * 47 | * This is the fast-path of error_slow_fold(). See its description for 48 | * details. 49 | * 50 | * Return: 0 or negative error code, depending on @r. 51 | */ 52 | #define error_fold(r) C_CC_MACRO1(ERROR_FOLD, (r)) 53 | #define ERROR_FOLD(r) (_c_likely_(!r) ? 0 : error_slow_fold(r, __func__, __FILE__, __LINE__)) 54 | -------------------------------------------------------------------------------- /src/broker/broker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Broker 5 | */ 6 | 7 | #include 8 | #include 9 | #include "broker/controller.h" 10 | #include "bus/bus.h" 11 | #include "util/dispatch.h" 12 | #include "util/log.h" 13 | 14 | enum { 15 | _BROKER_E_SUCCESS, 16 | 17 | BROKER_E_FORWARD_FAILED, 18 | }; 19 | 20 | typedef struct Broker Broker; 21 | typedef struct User User; 22 | 23 | struct Broker { 24 | Log *log; 25 | Bus bus; 26 | DispatchContext dispatcher; 27 | 28 | int signals_fd; 29 | DispatchFile signals_file; 30 | 31 | Controller controller; 32 | }; 33 | 34 | /* broker */ 35 | 36 | int broker_new(Broker **brokerp, Log *log, const char *machine_id, int controller_fd, uint64_t max_bytes, uint64_t max_fds, uint64_t max_matches, uint64_t max_objects); 37 | Broker *broker_free(Broker *broker); 38 | 39 | int broker_run(Broker *broker); 40 | int broker_update_environment(Broker *broker, const char * const *env, size_t n_env); 41 | int broker_reload_config(Broker *broker, User *sender_user, uint64_t sender_id, uint32_t sender_serial); 42 | 43 | C_DEFINE_CLEANUP(Broker *, broker_free); 44 | 45 | /* inline helpers */ 46 | 47 | static inline Broker *BROKER(Bus *bus) { 48 | /* 49 | * This function up-casts a Bus to its parent class Broker. In our code 50 | * base we pretend a Bus is an abstract class with several virtual 51 | * methods. However, we only do this to clearly separate our code 52 | * bases. We never intended this to be modular. Hence, instead of 53 | * providing real vtables with userdata pointers, we instead allow 54 | * explicit up-casts to the parent type. 55 | * 56 | * This function performs the up-cast, relying on the fact that all our 57 | * Bus objects are always owned by a Broker object. 58 | */ 59 | return c_container_of(bus, Broker, bus); 60 | } 61 | -------------------------------------------------------------------------------- /src/util/systemd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Systemd Utilities 3 | */ 4 | 5 | #include 6 | #include 7 | #include "util/error.h" 8 | #include "util/systemd.h" 9 | 10 | static bool needs_escape(char c) { 11 | return !strchr("0123456789" 12 | "abcdefghijklmnopqrstuvwxyz" 13 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 14 | ":_.", c); 15 | } 16 | 17 | static char *escape_char(char *t, char c) { 18 | // Include terminating NUL to silence warnings about truncated strings. 19 | static const char table[17] = "0123456789abcdef"; 20 | 21 | *t++ = '\\'; 22 | *t++ = 'x'; 23 | *t++ = table[(c >> 4) & 0x0f]; 24 | *t++ = table[c & 0x0f]; 25 | 26 | return t; 27 | } 28 | 29 | /** 30 | * systemd_escape_unit() - escape unit name 31 | * @escapedp: output argument for escaped unit name 32 | * @unescaped: unescaped unit name to operate with 33 | * 34 | * This escapes the specified systemd unit name and returns it in the specified 35 | * output pointer. 36 | * 37 | * Return: 0 on success, negative error code on failure. 38 | */ 39 | int systemd_escape_unit(char **escapedp, const char *unescaped) { 40 | char *buffer, *dst; 41 | const char *src; 42 | 43 | buffer = malloc(strlen(unescaped) * 4 + 1); 44 | if (!buffer) 45 | return error_origin(-ENOMEM); 46 | 47 | src = unescaped; 48 | dst = buffer; 49 | 50 | if (*src == '.') 51 | dst = escape_char(dst, *src++); 52 | 53 | for ( ; *src; ++src) { 54 | if (*src == '/') 55 | *dst++ = '-'; 56 | else if (needs_escape(*src)) 57 | dst = escape_char(dst, *src); 58 | else 59 | *dst++ = *src; 60 | } 61 | 62 | *dst = 0; 63 | *escapedp = buffer; 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /src/util/misc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Miscellaneous Helpers 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #define MISC_MFD_CLOEXEC 0x0001U 11 | #define MISC_MFD_ALLOW_SEALING 0x0002U 12 | #define MISC_MFD_HUGETLB 0x0004U 13 | #define MISC_MFD_NOEXEC_SEAL 0x0008U 14 | #define MISC_MFD_EXEC 0x0010U 15 | 16 | #define MISC_F_SEAL_SEAL 0x0001U 17 | #define MISC_F_SEAL_SHRINK 0x0002U 18 | #define MISC_F_SEAL_GROW 0x0004U 19 | #define MISC_F_SEAL_WRITE 0x0008U 20 | #define MISC_F_SEAL_FUTURE_WRITE 0x0010U 21 | #define MISC_F_SEAL_EXEC 0x0020U 22 | 23 | int misc_memfd(const char *name, unsigned int uflags, unsigned int useals); 24 | int misc_memfd_add_seals(int fd, unsigned int seals); 25 | int misc_memfd_get_seals(int fd, unsigned int *sealsp); 26 | 27 | uint64_t util_umul64_saturating(uint64_t a, uint64_t b); 28 | unsigned int util_z2u_saturating(size_t v); 29 | unsigned int util_t2u_saturating(uint64_t v); 30 | int util_drop_permissions(uint32_t uid, uint32_t gid); 31 | 32 | void util_peak_update(size_t *peak, size_t update); 33 | 34 | /** 35 | * misc_vfreep() - Cleanup helper for NULL-terminated arrays of allocations 36 | * v: Pointer to the array of allocated objects (i.e., `void ***p`) 37 | * 38 | * This interprets `v` as `void ***`, assuming it points to an array of 39 | * allocated objects. `v` must not be NULL. 40 | * 41 | * If `*v` is NULL, this is a no-op. Otherwise, `*v` is iterated as array of 42 | * pointers, terminated by a NULL entry. All entries are passed to free(), with 43 | * a final call to `*v` itself. 44 | */ 45 | static inline void misc_vfreep(void *v) { 46 | void ***p = v; 47 | size_t i; 48 | 49 | if (*p) { 50 | for (i = 0; (*p)[i]; ++i) 51 | free((*p)[i]); 52 | free(*p); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/util/syscall.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Syscall Wrappers 5 | * 6 | * The linux syscalls are usually not directly accessible from applications, 7 | * since most standard libraries do not provide wrapper functions. This module 8 | * provides direct syscall wrappers via `syscall(3)' for a set of otherwise 9 | * unavailable syscalls. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | /** 17 | * syscall_memfd_create() - wrapper for memfd_create(2) syscall 18 | * @name: name for memfd inode 19 | * @flags: memfd flags 20 | * 21 | * This is a wrapper for the memfd_create(2) syscall. Currently, no user-space 22 | * wrapper is exported by any libc. 23 | * 24 | * Return: New memfd file-descriptor on success, -1 on failure. 25 | */ 26 | static inline int syscall_memfd_create(const char *name, unsigned int flags) { 27 | /* Make Travis happy. */ 28 | #if defined __NR_memfd_create 29 | long nr = __NR_memfd_create; 30 | #elif defined __x86_64__ 31 | long nr = 319; 32 | #elif defined __i386__ 33 | long nr = 356; 34 | #else 35 | # error "__NR_memfd_create is undefined" 36 | #endif 37 | return (int)syscall(nr, name, flags); 38 | } 39 | 40 | /** 41 | * syscall_pidfd_open() - wrapper for pidfd_open(2) syscall 42 | * @pid: pid to open 43 | * @flags: pidfd flags 44 | * 45 | * This is a wrapper for the pidfd_open(2) syscall. Only a very recent version 46 | * of glibc (2.36) exports a wrapper for this syscall, so we provide our own 47 | * for compatibility with other libc implementations. 48 | * 49 | * Return: New pidfd file-descriptor on success, -1 on failure. 50 | */ 51 | static inline int syscall_pidfd_open(pid_t pid, unsigned int flags) { 52 | #if defined __NR_pidfd_open 53 | long nr = __NR_pidfd_open; 54 | #elif defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__) 55 | long nr = 434; 56 | #else 57 | # error "__NR_pidfd_open is undefined" 58 | #endif 59 | return (int)syscall(nr, pid, flags); 60 | } 61 | -------------------------------------------------------------------------------- /test/integration/README: -------------------------------------------------------------------------------- 1 | # dbus-broker integration test suite 2 | 3 | dbus-broker's integration test suite uses tmt (Test Management Tool [0]) to organize and run tests. Since tmt 4 | offers a _lot_ of features, this document pinpoints the most _interesting_ ones to get stuff up and running 5 | quickly. 6 | 7 | ## How to contribute 8 | 9 | Creating a new test case is pretty simple: 10 | 11 | ``` 12 | $ cd test/integration 13 | $ tmt test create --template=shell test/name 14 | Test directory '/home/.../dbus-broker/test/integration/test/name' created. 15 | Test metadata '/home/.../dbus-broker/test/integration/test/name/main.fmf' created. 16 | Test script '/home/.../dbus-broker/test/integration/test/name/test.sh' created. 17 | ``` 18 | 19 | The newly created `test.sh` will be the actual test case, and `main.fmf` contains the test metadata, including 20 | test summary & description, test dependencies, runtime, and so on. See [1] for more details. 21 | 22 | After tweaking the test metadata it's usually a good idea to run `tmt lint test/name` to make sure that the 23 | configuration is still valid: 24 | 25 | ``` 26 | $ tmt lint test/name 27 | /test/name 28 | pass C000 fmf node passes schema validation 29 | pass C001 summary key is set and is reasonably long 30 | ... 31 | ``` 32 | 33 | To check if the test itself works as expected you can use `tmt run`: 34 | 35 | ``` 36 | $ cd test/name 37 | $ tmt run -vvv --all provision --how local tests --name . 38 | ... 39 | total: 1 test passed 40 | ``` 41 | 42 | The `tmt run` command is _very_ customizable (as is the rest of `tmt`). In this particular example we tell it 43 | to run all steps (`--all`) and override the `provision` and `tests` steps to run just one particular test on 44 | the local machine. As in previous cases, check the `tmt` documentation [0] and examples [2] for more details. 45 | 46 | ## Links 47 | 48 | [0] https://tmt.readthedocs.io/en/stable/overview.html 49 | 50 | [1] https://tmt.readthedocs.io/en/stable/spec/tests.html 51 | 52 | [2] https://tmt.readthedocs.io/en/stable/examples.html#run 53 | 54 | 55 | -------------------------------------------------------------------------------- /.github/workflows/lib-codeql.yml: -------------------------------------------------------------------------------- 1 | # 2 | # lib: CodeQL Analysis 3 | # 4 | # This is a reusable workflow that runs the CodeQL analyzer on the local 5 | # code-base and reports defects directly on GitHub. 6 | # 7 | 8 | # 9 | # NB: The actions log will show errors like: 10 | # 11 | # ERROR: ld.so: object '/__t/CodeQL/2.21.4/x64/codeql/tools/linux64/${LIB}_${PLATFORM}_trace.so' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored. 12 | # 13 | # This can be ignored. This happens because `codeql-action/init` installs 14 | # an LD_PRELOAD handler in a container, but the LD_PRELOAD environment 15 | # variable will also be set for `docker exec`. Hence, the docker execution 16 | # of each step will be unable to honor the LD_PRELOAD. This can be ignored 17 | # since CodeQL has no interest in tracing the docker execution, but only 18 | # the binaries _inside_ of the container. 19 | # 20 | 21 | name: "lib: codeql analysis" 22 | 23 | on: 24 | workflow_call: 25 | workflow_dispatch: 26 | 27 | defaults: 28 | run: 29 | shell: "bash" 30 | 31 | permissions: 32 | actions: "read" 33 | contents: "read" 34 | security-events: "write" 35 | 36 | jobs: 37 | scan: 38 | name: "CodeQL Analysis" 39 | 40 | container: 41 | image: "ghcr.io/readaheadeu/rae-ci-ubuntu:latest" 42 | options: "--user root" 43 | runs-on: "ubuntu-latest" 44 | 45 | steps: 46 | - name: "Fetch Sources" 47 | uses: actions/checkout@v4 48 | 49 | - name: "Initialize CodeQL" 50 | uses: github/codeql-action/init@v3 51 | with: 52 | config-file: "./.github/codeql-config.yml" 53 | languages: "cpp" 54 | 55 | - name: "Build Project" 56 | run: | 57 | meson setup \ 58 | --buildtype "debugoptimized" \ 59 | --warnlevel "2" \ 60 | -Dapparmor=true \ 61 | -Daudit=true \ 62 | -Dlauncher=true \ 63 | -Dselinux=true \ 64 | "./build" \ 65 | "." 66 | meson compile \ 67 | -C "./build" 68 | 69 | - name: "Perform CodeQL Analysis" 70 | uses: github/codeql-action/analyze@v3 71 | -------------------------------------------------------------------------------- /src/dbus/test-message.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test D-Bus Message Abstraction 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include "dbus/message.h" 9 | 10 | static void test_setup(void) { 11 | _c_cleanup_(message_unrefp) Message *m1 = NULL, *m2, *m3; 12 | MessageHeader hdr = { .endian = 'l' }; 13 | int r; 14 | 15 | /* verify constructors / destructors */ 16 | 17 | r = message_new_incoming(&m2, hdr); 18 | c_assert(r == 0); 19 | 20 | r = message_new_incoming(&m3, hdr); 21 | c_assert(r == 0); 22 | 23 | m3 = message_unref(m3); 24 | m1 = message_unref(m1); 25 | } 26 | 27 | static void test_size(void) { 28 | MessageHeader hdr = { .endian = 'l' }; 29 | Message *m; 30 | int r; 31 | 32 | /* verify total message size cannot exceed 128MB */ 33 | 34 | hdr.n_body = htole32(0); 35 | r = message_new_incoming(&m, hdr); 36 | c_assert(r == 0); 37 | message_unref(m); 38 | 39 | hdr.n_body = htole32(128); 40 | r = message_new_incoming(&m, hdr); 41 | c_assert(r == 0); 42 | message_unref(m); 43 | 44 | hdr.n_body = htole32(128UL * 1024UL * 1024UL - sizeof(MessageHeader)); 45 | r = message_new_incoming(&m, hdr); 46 | c_assert(r == 0); 47 | message_unref(m); 48 | 49 | hdr.n_body = htole32(128UL * 1024UL * 1024UL - sizeof(MessageHeader) + 1UL); 50 | r = message_new_incoming(&m, hdr); 51 | c_assert(r == MESSAGE_E_TOO_LARGE); 52 | 53 | hdr.n_fields = htole32(8); 54 | hdr.n_body = htole32(128UL * 1024UL * 1024UL - sizeof(MessageHeader) - 8); 55 | r = message_new_incoming(&m, hdr); 56 | c_assert(r == 0); 57 | message_unref(m); 58 | 59 | hdr.n_fields = htole32(8 + 1); 60 | hdr.n_body = htole32(128UL * 1024UL * 1024UL - sizeof(MessageHeader) - 8); 61 | r = message_new_incoming(&m, hdr); 62 | c_assert(r == MESSAGE_E_TOO_LARGE); 63 | } 64 | 65 | int main(int argc, char **argv) { 66 | test_setup(); 67 | test_size(); 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dbus-broker - Linux D-Bus Message Broker 2 | 3 | The dbus-broker project is an implementation of a message bus as 4 | defined by the D-Bus specification. Its aim is to provide high 5 | performance and reliability, while keeping compatibility to the D-Bus 6 | reference implementation. It is exclusively written for Linux systems, 7 | and makes use of many modern features provided by recent linux kernel 8 | releases. 9 | 10 | **WIKI:** 11 | https://github.com/bus1/dbus-broker/wiki 12 | 13 | **BUG REPORTS:** 14 | https://github.com/bus1/dbus-broker/issues 15 | 16 | **GIT:** 17 | 18 | ``` 19 | Cloning over ssh: git@github.com:bus1/dbus-broker.git 20 | Cloning over https: https://github.com/bus1/dbus-broker.git 21 | ``` 22 | 23 | **GITWEB:** 24 | https://github.com/bus1/dbus-broker 25 | 26 | **MAILINGLIST:** 27 | https://groups.google.com/forum/#!forum/bus1-devel 28 | 29 | ## Requirements 30 | 31 | The requirements for dbus-broker are: 32 | 33 | ``` 34 | glibc >= 2.16 35 | linux kernel >= 4.17 36 | libaudit >= 3.0 (optional) 37 | libcap-ng >= 0.6 (optional) 38 | libselinux >= 3.2 (optional) 39 | ``` 40 | 41 | Additionally, the compatibility launcher requires: 42 | 43 | ``` 44 | expat >= 2.2 45 | systemd >= 230 46 | ``` 47 | 48 | At build-time, the following software is required: 49 | 50 | ``` 51 | linux-api-headers >= 4.13 52 | meson >= 1.3 53 | pkg-config >= 0.29 54 | rust >= 1.84 55 | rust-bindgen >= 0.60 56 | dbus >= 1.10 (optional: only for tests) 57 | python-docutils >= 0.13 (optional: only for docs) 58 | ``` 59 | 60 | ## Install 61 | 62 | The meson build-system is used for dbus-broker. Contact upstream 63 | documentation for detailed help. In most situations the following 64 | commands are sufficient to build and install dbus-broker from source: 65 | 66 | ``` 67 | $ meson setup build 68 | $ meson compile -C build 69 | $ meson test -C build 70 | $ meson install -C build 71 | ``` 72 | 73 | For custom configuration options see meson_options.txt. 74 | 75 | ## License 76 | 77 | Apache Software License 2.0 78 | See AUTHORS for details. 79 | -------------------------------------------------------------------------------- /packit.yml: -------------------------------------------------------------------------------- 1 | create_sync_note: false 2 | downstream_package_name: "dbus-broker" 3 | upstream_package_name: "dbus-broker" 4 | upstream_tag_template: "v{version}" 5 | 6 | specfile_path: ".dist-git/dbus-broker.spec" 7 | 8 | files_to_sync: 9 | - "packit.yml" 10 | - src: ".dist-git/dbus-broker.spec" 11 | dest: "dbus-broker.spec" 12 | 13 | srpm_build_deps: 14 | - "meson" 15 | 16 | actions: 17 | post-upstream-clone: 18 | # Prepare sources for offline builds 19 | - meson subprojects download 20 | # Use the downstream configuration 21 | - git clone --depth=1 "https://src.fedoraproject.org/rpms/dbus-broker" ".dist-git" 22 | # Drop "sources" so rebase-helper does not treat this as dist-git 23 | - rm -fv ".dist-git/sources" 24 | # Drop backported patches 25 | - sed -ri '/^Patch.*\\:.+\\.patch/d' ".dist-git/dbus-broker.spec" 26 | 27 | create-archive: 28 | # Override `git archive`, since we use subprojects. 29 | - bash -c 'tar -pczf .dist-git/$PACKIT_PROJECT_NAME_VERSION.tar.gz --exclude="./.*" --transform="s/^\./$PACKIT_PROJECT_NAME_VERSION/" .' 30 | - bash -c "echo .dist-git/$PACKIT_PROJECT_NAME_VERSION.tar.gz" 31 | 32 | jobs: 33 | - job: copr_build 34 | trigger: commit 35 | targets: 36 | - fedora-all-aarch64 37 | - fedora-all-i386 38 | - fedora-all-ppc64le 39 | - fedora-all-s390x 40 | - fedora-all-x86_64 41 | 42 | - job: copr_build 43 | trigger: pull_request 44 | targets: 45 | - fedora-all-aarch64 46 | - fedora-all-i386 47 | - fedora-all-ppc64le 48 | - fedora-all-s390x 49 | - fedora-all-x86_64 50 | 51 | - job: tests 52 | trigger: commit 53 | fmf_path: test/integration/ 54 | tmt_plan: upstream_ci 55 | targets: 56 | - fedora-latest-stable-aarch64 57 | - fedora-latest-stable-x86_64 58 | - fedora-rawhide-aarch64 59 | - fedora-rawhide-x86_64 60 | 61 | - job: tests 62 | trigger: pull_request 63 | fmf_path: test/integration/ 64 | tmt_plan: upstream_ci 65 | targets: 66 | - fedora-latest-stable-aarch64 67 | - fedora-latest-stable-x86_64 68 | - fedora-rawhide-aarch64 69 | - fedora-rawhide-x86_64 70 | 71 | - job: propose_downstream 72 | trigger: release 73 | dist_git_branches: 74 | - main 75 | -------------------------------------------------------------------------------- /src/dbus/sasl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Server-Side SASL Parser 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | typedef struct SASLClient SASLClient; 13 | typedef struct SASLServer SASLServer; 14 | 15 | enum { 16 | _SASL_E_SUCCESS, 17 | 18 | SASL_E_FAILURE, 19 | SASL_E_PROTOCOL_VIOLATION, 20 | }; 21 | 22 | /* client */ 23 | 24 | enum { 25 | SASL_CLIENT_STATE_INIT, 26 | SASL_CLIENT_STATE_DONE, 27 | SASL_CLIENT_STATE_AUTH, 28 | SASL_CLIENT_STATE_DATA, 29 | SASL_CLIENT_STATE_UNIX_FD, 30 | }; 31 | 32 | struct SASLClient { 33 | unsigned int state; 34 | }; 35 | 36 | #define SASL_CLIENT_NULL {} 37 | 38 | void sasl_client_init(SASLClient *sasl); 39 | void sasl_client_deinit(SASLClient *sasl); 40 | 41 | int sasl_client_dispatch(SASLClient *sasl, const char *input, size_t n_input, const char **outputp, size_t *n_outputp); 42 | 43 | C_DEFINE_CLEANUP(SASLClient *, sasl_client_deinit); 44 | 45 | /* server */ 46 | 47 | enum { 48 | SASL_SERVER_STATE_INIT, 49 | SASL_SERVER_STATE_DONE, 50 | SASL_SERVER_STATE_AUTH, 51 | SASL_SERVER_STATE_CHALLENGE, 52 | SASL_SERVER_STATE_AUTHENTICATED, 53 | SASL_SERVER_STATE_NEGOTIATED_FDS, 54 | }; 55 | 56 | struct SASLServer { 57 | unsigned int state; 58 | bool fds_allowed; 59 | uid_t uid; 60 | char ok_response[sizeof("OK 0123456789abcdef0123456789abdcef") - 1]; 61 | }; 62 | 63 | #define SASL_SERVER_NULL {} 64 | 65 | void sasl_server_init(SASLServer *sasl, uid_t uid, const char *guid); 66 | void sasl_server_deinit(SASLServer *sasl); 67 | 68 | int sasl_server_dispatch(SASLServer *sasl, const char *input, size_t n_input, const char **outputp, size_t *n_outputp); 69 | 70 | C_DEFINE_CLEANUP(SASLServer *, sasl_server_deinit); 71 | 72 | /* inline helpers */ 73 | 74 | static inline bool sasl_client_is_done(SASLClient *client) { 75 | return _c_likely_(client->state == SASL_CLIENT_STATE_DONE); 76 | } 77 | 78 | static inline bool sasl_server_is_done(SASLServer *server) { 79 | return _c_likely_(server->state == SASL_SERVER_STATE_DONE); 80 | } 81 | -------------------------------------------------------------------------------- /test/dbus/tool-flood.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Flood destination with pings 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include 9 | #include "util-broker.h" 10 | #include "util-message.h" 11 | #include "dbus/protocol.h" 12 | 13 | static void test_connect_system_blocking_fd(int *fdp) { 14 | _c_cleanup_(c_closep) int fd = -1; 15 | _c_cleanup_(c_freep) void *hello = NULL; 16 | struct sockaddr_un addr = {}; 17 | size_t n_hello = 0; 18 | uint8_t reply[316]; 19 | ssize_t len; 20 | int r; 21 | 22 | test_message_append_sasl(&hello, &n_hello); 23 | test_message_append_hello(&hello, &n_hello); 24 | 25 | fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 26 | assert(fd >= 0); 27 | 28 | addr.sun_family = AF_UNIX; 29 | strcpy(addr.sun_path, "/run/dbus/system_bus_socket"); 30 | 31 | r = connect(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path) + 1); 32 | assert(r >= 0); 33 | 34 | len = write(fd, hello, n_hello); 35 | assert(len == (ssize_t)n_hello); 36 | 37 | len = recv(fd, reply, sizeof(reply), MSG_WAITALL); 38 | assert(len == (ssize_t)sizeof(reply)); 39 | 40 | *fdp = fd; 41 | fd = -1; 42 | } 43 | 44 | noreturn static void test_flood(const char *destination) { 45 | _c_cleanup_(c_closep) int fd = -1; 46 | uint32_t serial = 0;; 47 | 48 | test_connect_system_blocking_fd(&fd); 49 | 50 | for (;;) { 51 | _c_cleanup_(c_freep) void *ping = NULL; 52 | size_t n_ping = 0; 53 | ssize_t len; 54 | 55 | test_message_append_ping2(&ping, &n_ping, ++serial, NULL, destination); 56 | 57 | len = write(fd, ping, n_ping); 58 | assert(len == (ssize_t)n_ping); 59 | 60 | if (serial % 1000 == 0) 61 | fprintf(stderr, "PING! (%"PRIu32")\n", serial); 62 | 63 | assert(serial); 64 | } 65 | } 66 | 67 | int main(int argc, char **argv) { 68 | assert(argc == 2); 69 | 70 | test_flood(argv[1]); 71 | } 72 | -------------------------------------------------------------------------------- /src/dbus/protocol.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * DBus Protocol Constants 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | enum { 11 | DBUS_NAME_FLAG_ALLOW_REPLACEMENT = (1ULL << 0), 12 | DBUS_NAME_FLAG_REPLACE_EXISTING = (1ULL << 1), 13 | DBUS_NAME_FLAG_DO_NOT_QUEUE = (1ULL << 2), 14 | }; 15 | 16 | enum { 17 | DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1, 18 | DBUS_REQUEST_NAME_REPLY_IN_QUEUE = 2, 19 | DBUS_REQUEST_NAME_REPLY_EXISTS = 3, 20 | DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4, 21 | }; 22 | 23 | enum { 24 | DBUS_RELEASE_NAME_REPLY_RELEASED = 1, 25 | DBUS_RELEASE_NAME_REPLY_NON_EXISTENT = 2, 26 | DBUS_RELEASE_NAME_REPLY_NOT_OWNER = 3, 27 | }; 28 | 29 | enum { 30 | DBUS_START_REPLY_SUCCESS = 1, 31 | DBUS_START_REPLY_ALREADY_RUNNING = 2, 32 | }; 33 | 34 | enum { 35 | DBUS_MESSAGE_TYPE_INVALID = 0, 36 | DBUS_MESSAGE_TYPE_METHOD_CALL = 1, 37 | DBUS_MESSAGE_TYPE_METHOD_RETURN = 2, 38 | DBUS_MESSAGE_TYPE_ERROR = 3, 39 | DBUS_MESSAGE_TYPE_SIGNAL = 4, 40 | _DBUS_MESSAGE_TYPE_N, 41 | }; 42 | 43 | enum { 44 | DBUS_MESSAGE_FIELD_INVALID = 0, 45 | DBUS_MESSAGE_FIELD_PATH = 1, 46 | DBUS_MESSAGE_FIELD_INTERFACE = 2, 47 | DBUS_MESSAGE_FIELD_MEMBER = 3, 48 | DBUS_MESSAGE_FIELD_ERROR_NAME = 4, 49 | DBUS_MESSAGE_FIELD_REPLY_SERIAL = 5, 50 | DBUS_MESSAGE_FIELD_DESTINATION = 6, 51 | DBUS_MESSAGE_FIELD_SENDER = 7, 52 | DBUS_MESSAGE_FIELD_SIGNATURE = 8, 53 | DBUS_MESSAGE_FIELD_UNIX_FDS = 9, 54 | _DBUS_MESSAGE_FIELD_N, 55 | }; 56 | 57 | enum { 58 | DBUS_HEADER_FLAG_NO_REPLY_EXPECTED = (1UL << 0), 59 | DBUS_HEADER_FLAG_NO_AUTO_START = (1UL << 1), 60 | DBUS_HEADER_FLAG_ALLOW_INTERACTIVE_AUTHORIZATION = (1UL << 2), 61 | }; 62 | 63 | bool dbus_validate_name(const char *name, size_t n_name); 64 | bool dbus_validate_namespace(const char *namespace, size_t n_namespace); 65 | bool dbus_validate_interface(const char *interface, size_t n_interface); 66 | bool dbus_validate_member(const char *member, size_t n_member); 67 | bool dbus_validate_error_name(const char *name, size_t n_name); 68 | -------------------------------------------------------------------------------- /src/util/dispatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Event Dispatcher 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | enum { 12 | _DISPATCH_E_SUCCESS, 13 | 14 | DISPATCH_E_EXIT, 15 | DISPATCH_E_FAILURE, 16 | }; 17 | 18 | typedef struct DispatchContext DispatchContext; 19 | typedef struct DispatchFile DispatchFile; 20 | typedef int (*DispatchFn) (DispatchFile *file); 21 | 22 | /* files */ 23 | 24 | struct DispatchFile { 25 | DispatchContext *context; 26 | CList ready_link; 27 | DispatchFn fn; 28 | 29 | int fd; 30 | uint32_t user_mask; 31 | uint32_t kernel_mask; 32 | uint32_t events; 33 | }; 34 | 35 | #define DISPATCH_FILE_NULL(_x) { \ 36 | .ready_link = C_LIST_INIT((_x).ready_link), \ 37 | .fd = -1, \ 38 | } 39 | 40 | int dispatch_file_init(DispatchFile *file, 41 | DispatchContext *ctx, 42 | DispatchFn fn, 43 | int fd, 44 | uint32_t mask, 45 | uint32_t events); 46 | void dispatch_file_deinit(DispatchFile *file); 47 | 48 | void dispatch_file_select(DispatchFile *file, uint32_t mask); 49 | void dispatch_file_deselect(DispatchFile *file, uint32_t mask); 50 | void dispatch_file_clear(DispatchFile *file, uint32_t mask); 51 | 52 | /* contexts */ 53 | 54 | struct DispatchContext { 55 | CList ready_list; 56 | int epoll_fd; 57 | size_t n_files; 58 | }; 59 | 60 | #define DISPATCH_CONTEXT_NULL(_x) { \ 61 | .ready_list = C_LIST_INIT((_x).ready_list), \ 62 | .epoll_fd = -1, \ 63 | } 64 | 65 | int dispatch_context_init(DispatchContext *ctx); 66 | void dispatch_context_deinit(DispatchContext *ctx); 67 | 68 | int dispatch_context_poll(DispatchContext *ctx, int timeout); 69 | int dispatch_context_dispatch(DispatchContext *ctx); 70 | 71 | /* inline helpers */ 72 | 73 | static inline uint32_t dispatch_file_events(DispatchFile *file) { 74 | return file->events & file->user_mask; 75 | } 76 | -------------------------------------------------------------------------------- /src/bus/driver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * DBus Driver 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | typedef struct Bus Bus; 11 | typedef struct MatchOwner MatchOwner; 12 | typedef struct Message Message; 13 | typedef struct Peer Peer; 14 | typedef struct User User; 15 | 16 | enum { 17 | _DRIVER_E_SUCCESS, 18 | 19 | DRIVER_E_UNIMPLEMENTED, 20 | 21 | DRIVER_E_PEER_ALREADY_REGISTERED, 22 | DRIVER_E_PEER_NOT_YET_REGISTERED, 23 | DRIVER_E_PEER_NOT_REGISTERED, 24 | DRIVER_E_PEER_NOT_PRIVILEGED, 25 | 26 | DRIVER_E_MONITOR_READ_ONLY, 27 | 28 | DRIVER_E_UNEXPECTED_FDS, 29 | DRIVER_E_UNEXPECTED_MESSAGE_TYPE, 30 | DRIVER_E_UNEXPECTED_PATH, 31 | DRIVER_E_UNEXPECTED_INTERFACE, 32 | DRIVER_E_UNEXPECTED_METHOD, 33 | DRIVER_E_UNEXPECTED_PROPERTY, 34 | DRIVER_E_READONLY_PROPERTY, 35 | DRIVER_E_UNEXPECTED_SIGNATURE, 36 | DRIVER_E_UNEXPECTED_REPLY, 37 | 38 | DRIVER_E_FORWARD_FAILED, 39 | 40 | DRIVER_E_QUOTA, 41 | 42 | DRIVER_E_UNEXPECTED_FLAGS, 43 | DRIVER_E_UNEXPECTED_ENVIRONMENT_UPDATE, 44 | 45 | DRIVER_E_SEND_DENIED, 46 | DRIVER_E_RECEIVE_DENIED, 47 | DRIVER_E_EXPECTED_REPLY_EXISTS, 48 | 49 | DRIVER_E_NAME_RESERVED, 50 | DRIVER_E_NAME_UNIQUE, 51 | DRIVER_E_NAME_INVALID, 52 | DRIVER_E_NAME_REFUSED, 53 | DRIVER_E_NAME_NOT_FOUND, 54 | DRIVER_E_NAME_NOT_ACTIVATABLE, 55 | DRIVER_E_NAME_OWNER_NOT_FOUND, 56 | DRIVER_E_PEER_NOT_FOUND, 57 | DRIVER_E_DESTINATION_NOT_FOUND, 58 | 59 | DRIVER_E_MATCH_INVALID, 60 | DRIVER_E_MATCH_NOT_FOUND, 61 | 62 | DRIVER_E_ADT_NOT_SUPPORTED, 63 | DRIVER_E_SELINUX_NOT_SUPPORTED, 64 | 65 | _DRIVER_E_MAX, 66 | }; 67 | 68 | int driver_name_activation_failed(Bus *bus, Activation *activation, uint64_t serial, unsigned int name_error); 69 | int driver_reload_config_completed(Bus *bus, uint64_t sender_id, uint32_t reply_serial); 70 | int driver_reload_config_invalid(Bus *bus, uint64_t sender_id, uint32_t reply_serial); 71 | 72 | int driver_dispatch(Peer *peer, Message *message); 73 | int driver_goodbye(Peer *peer, bool silent); 74 | -------------------------------------------------------------------------------- /subprojects/packagefiles/libc-rs/meson.build: -------------------------------------------------------------------------------- 1 | # 2 | # Meson Integration for Rust libc Bindings 3 | # 4 | # This is a meson build file for the Rust crate `libc` (provided by 5 | # `rust-lang/libc`). The build mirrors what `Cargo.toml` and `build.rs` 6 | # of the crate specify, with the following caveats: 7 | # 8 | # - Currently, only cfgs relevant for linux are considered. 9 | # - `_FILE_OFFSET_BITS=64` and `_TIME_BITS=64` are enabled unconditionally. 10 | # - Only the stable branch `libc-0.2` is supported. 11 | # - Last compatibility check was made with `libc-0.2.175`. Newer version 12 | # might have added further `build.rs` or `Cargo.toml` configurations. 13 | # 14 | 15 | project( 16 | 'libc-rs-0.2', 17 | default_options: [ 18 | 'rust_std=2021', 19 | ], 20 | license: 'MIT OR Apache-2.0', 21 | meson_version: '>=1.3', 22 | version: '0.2', 23 | ) 24 | 25 | add_languages('rust', native: false) 26 | 27 | rust_edition = '2021' 28 | rust_msv = '1.63' 29 | 30 | rust = meson.get_compiler('rust') 31 | 32 | # 33 | # System Requirements 34 | # 35 | 36 | if rust.version().version_compare('<' + rust_msv) 37 | error('Found Rust ' + rust.version() + ' but requires >=' + rust_msv) 38 | endif 39 | 40 | cargo = find_program('cargo', native: true, required: true, version: '>=' + rust_msv) 41 | jq = find_program('jq', native: true, required: true, version: '>=1.6') 42 | 43 | # 44 | # Detect version of the libc sources 45 | # 46 | 47 | libc_version = run_command( 48 | './meson-detect-version.sh', 49 | capture: true, 50 | check: true, 51 | env: { 52 | 'BIN_CARGO': cargo.full_path(), 53 | 'BIN_JQ': jq.full_path(), 54 | }, 55 | ).stdout().strip() 56 | 57 | summary('version', libc_version) 58 | 59 | # 60 | # Build libc as rust library 61 | # 62 | 63 | libc_rlib = static_library( 64 | 'libc', 65 | ['src/lib.rs'], 66 | rust_args: [ 67 | # Unconditionally enabled by `build.rs`. 68 | '--cfg=libc_const_extern_fn', 69 | ], 70 | ) 71 | 72 | libc_dep = declare_dependency( 73 | link_with: libc_rlib, 74 | version: libc_version, 75 | ) 76 | 77 | meson.override_dependency('libc-rs-0.2', libc_dep, static: true) 78 | -------------------------------------------------------------------------------- /src/util/string.c: -------------------------------------------------------------------------------- 1 | /* 2 | * String Helpers 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include "util/error.h" 9 | #include "util/string.h" 10 | 11 | int util_strtou32(uint32_t *valp, const char *string) { 12 | unsigned long val; 13 | char *end; 14 | 15 | static_assert(sizeof(val) >= sizeof(uint32_t), "unsigned long is less than 32 bits"); 16 | 17 | errno = 0; 18 | val = strtoul(string, &end, 10); 19 | if (errno != 0) { 20 | if (errno == ERANGE) 21 | return UTIL_STRING_E_RANGE; 22 | 23 | return error_origin(-errno); 24 | } else if (*end || string == end) { 25 | return UTIL_STRING_E_INVALID; 26 | } else if (val > UINT32_MAX) { 27 | return UTIL_STRING_E_RANGE; 28 | } 29 | 30 | *valp = val; 31 | 32 | return 0; 33 | } 34 | 35 | int util_strtou64(uint64_t *valp, const char *string) { 36 | unsigned long long val; 37 | char *end; 38 | 39 | static_assert(sizeof(val) >= sizeof(uint64_t), "unsigned long long is less than 64 bits"); 40 | 41 | errno = 0; 42 | val = strtoull(string, &end, 10); 43 | if (errno != 0) { 44 | if (errno == ERANGE) 45 | return UTIL_STRING_E_RANGE; 46 | 47 | return error_origin(-errno); 48 | } else if (*end || string == end) { 49 | return UTIL_STRING_E_INVALID; 50 | } else if (val > UINT64_MAX) { 51 | return UTIL_STRING_E_RANGE; 52 | } 53 | 54 | *valp = val; 55 | 56 | return 0; 57 | } 58 | 59 | int util_strtoint(int *valp, const char *string) { 60 | long val; 61 | char *end; 62 | 63 | errno = 0; 64 | val = strtol(string, &end, 10); 65 | if (errno != 0) { 66 | if (errno == ERANGE) 67 | return UTIL_STRING_E_RANGE; 68 | 69 | return error_origin(-errno); 70 | } else if (*end || string == end) { 71 | return UTIL_STRING_E_INVALID; 72 | } else if (val > INT_MAX || val < INT_MIN) { 73 | return UTIL_STRING_E_RANGE; 74 | } 75 | 76 | *valp = val; 77 | 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /test/dbus/util-broker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Test Infrastructure around dbus-broker 5 | */ 6 | 7 | #undef NDEBUG 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | typedef struct Broker Broker; 18 | 19 | struct Broker { 20 | pthread_t thread; 21 | struct sockaddr_un address; 22 | socklen_t n_address; 23 | int listener_fd; 24 | int pipe_fds[2]; 25 | pid_t pid; 26 | pid_t child_pid; 27 | }; 28 | 29 | #define BROKER_NULL { \ 30 | .address.sun_family = AF_UNIX, \ 31 | .n_address = sizeof(struct sockaddr_un), \ 32 | .listener_fd = -1, \ 33 | .pipe_fds[0] = -1, \ 34 | .pipe_fds[1] = -1, \ 35 | } 36 | 37 | /* misc */ 38 | 39 | bool util_is_reference(void); 40 | void util_event_new(sd_event **eventp); 41 | void util_fork_broker(sd_bus **busp, sd_event *event, int listener_fd, pid_t *pidp); 42 | void util_fork_daemon(sd_event *event, int pipe_fd, pid_t *pidp); 43 | 44 | /* broker */ 45 | 46 | void util_broker_new(Broker **brokerp); 47 | Broker *util_broker_free(Broker *broker); 48 | void util_broker_spawn(Broker *broker); 49 | void util_broker_terminate(Broker *broker); 50 | void util_broker_settle(Broker *broker); 51 | 52 | void util_broker_connect_fd(Broker *broker, int *fdp); 53 | void util_broker_connect_raw(Broker *broker, sd_bus **busp); 54 | void util_broker_connect(Broker *broker, sd_bus **busp); 55 | void util_broker_connect_monitor(Broker *broker, sd_bus **busp); 56 | void util_broker_disconnect(sd_bus *bus); 57 | 58 | void util_broker_consume_method_call(sd_bus *bus, const char *interface, const char *member); 59 | void util_broker_consume_method_return(sd_bus *bus); 60 | void util_broker_consume_method_error(sd_bus *bus, const char *name); 61 | void util_broker_consume_signal(sd_bus *bus, const char *interface, const char *member); 62 | 63 | C_DEFINE_CLEANUP(Broker *, util_broker_free); 64 | -------------------------------------------------------------------------------- /src/launch/service.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * D-Bus Service 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "launch/launcher.h" 12 | 13 | typedef struct Service Service; 14 | typedef struct ServiceData ServiceData; 15 | 16 | struct Service { 17 | Launcher *launcher; 18 | sd_bus_slot *slot_watch_jobs; 19 | sd_bus_slot *slot_watch_unit; 20 | sd_bus_slot *slot_start_unit; 21 | CRBNode rb; 22 | CRBNode rb_by_name; 23 | uint64_t n_missing_unit; 24 | uint64_t n_masked_unit; 25 | 26 | uint64_t last_serial; 27 | ServiceData *active_data; 28 | char *active_unit; 29 | char *job; 30 | 31 | bool not_found; 32 | bool running; 33 | bool reload_tag; 34 | bool is_transient; 35 | 36 | char *name; 37 | uint64_t instance; 38 | ServiceData *data; 39 | char id[]; 40 | }; 41 | 42 | struct ServiceData { 43 | char *path; 44 | char *unit; 45 | char *user; 46 | uid_t uid; 47 | 48 | size_t argc; 49 | char *argv[]; 50 | }; 51 | 52 | int service_new(Service **servicep, 53 | Launcher *launcher, 54 | const char *name, 55 | CRBNode **slot_by_name, 56 | CRBNode *parent_by_name, 57 | const char *path, 58 | const char *unit, 59 | size_t argc, 60 | char **argv, 61 | const char *user, 62 | uid_t uid); 63 | Service *service_free(Service *service); 64 | 65 | C_DEFINE_CLEANUP(Service *, service_free); 66 | 67 | int service_update(Service *service, const char *path, const char *unit, size_t argc, char **argv, const char *user, uid_t uid); 68 | 69 | int service_compare(CRBTree *t, void *k, CRBNode *n); 70 | int service_compare_by_name(CRBTree *t, void *k, CRBNode *n); 71 | 72 | int service_activate(Service *service, uint64_t serial); 73 | 74 | int service_add(Service *service); 75 | int service_remove(Service *service); 76 | 77 | int service_data_new( 78 | ServiceData **datap, 79 | const char *path, 80 | const char *unit, 81 | const char *user, 82 | uid_t uid, 83 | size_t argc, 84 | char **argv 85 | ); 86 | ServiceData *service_data_free(ServiceData *data); 87 | 88 | C_DEFINE_CLEANUP(ServiceData *, service_data_free); 89 | 90 | int service_data_duplicate(ServiceData *data, ServiceData **dupp); 91 | -------------------------------------------------------------------------------- /src/bus/activation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Name Activation 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "bus/policy.h" 11 | #include "util/user.h" 12 | 13 | typedef struct Activation Activation; 14 | typedef struct ActivationMessage ActivationMessage; 15 | typedef struct ActivationRequest ActivationRequest; 16 | typedef struct Bus Bus; 17 | typedef struct Message Message; 18 | typedef struct Name Name; 19 | typedef struct NameOwner NameOwner; 20 | typedef struct NameSnapshot NameSnapshot; 21 | 22 | enum { 23 | _ACTIVATION_E_SUCCESS, 24 | 25 | ACTIVATION_E_QUOTA, 26 | 27 | ACTIVATION_E_ALREADY_ACTIVATABLE, 28 | }; 29 | 30 | struct ActivationRequest { 31 | UserCharge charge; 32 | uint64_t sender_id; 33 | uint32_t serial; 34 | CList link; 35 | }; 36 | 37 | struct ActivationMessage { 38 | User *user; 39 | UserCharge charges[2]; 40 | CList link; 41 | Message *message; 42 | PolicySnapshot *senders_policy; 43 | NameSnapshot *senders_names; 44 | }; 45 | 46 | struct Activation { 47 | Bus *bus; 48 | Name *name; 49 | User *user; 50 | CList activation_messages; 51 | CList activation_requests; 52 | uint64_t pending; 53 | }; 54 | 55 | #define ACTIVATION_NULL(_x) { \ 56 | .activation_messages = C_LIST_INIT((_x).activation_messages), \ 57 | .activation_requests = C_LIST_INIT((_x).activation_requests), \ 58 | } 59 | 60 | /* requests */ 61 | 62 | ActivationRequest *activation_request_free(ActivationRequest *request); 63 | 64 | /* messages */ 65 | 66 | ActivationMessage *activation_message_free(ActivationMessage *message); 67 | 68 | /* activation */ 69 | 70 | int activation_init(Activation *activation, Bus *bus, Name *name, User *user); 71 | void activation_deinit(Activation *activation); 72 | 73 | void activation_get_stats_for(Activation *activation, 74 | uint64_t owner_id, 75 | unsigned int *n_bytesp, 76 | unsigned int *n_fdsp); 77 | int activation_queue_message(Activation *activation, 78 | User *user, 79 | NameOwner *names, 80 | PolicySnapshot *policy, 81 | Message *m); 82 | int activation_queue_request(Activation *activation, User *user, uint64_t sender_id, uint32_t serial); 83 | 84 | C_DEFINE_CLEANUP(Activation *, activation_deinit); 85 | -------------------------------------------------------------------------------- /src/util/apparmor-fallback.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Bus AppArmor Fallback Helpers 3 | * 4 | * This fallback is used when libapparmor is not available, and is meant to be 5 | * functionally equivalent to util/apparmor.c in case AppArmor is disabled. 6 | * 7 | * See util/apparmor.c for details. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include "util/apparmor.h" 14 | 15 | /** 16 | * bus_apparmor_is_enabled() - checks if AppArmor is currently enabled 17 | * @enabled: return argument telling if AppArmor is enabled 18 | * 19 | * If the AppArmor module is not loaded, or AppArmor is disabled in the 20 | * kernel, set @enabledp to 'false', otherwise set it to 'true'. 21 | * 22 | * Returns: 0 if check succeeded, or negative error code on failure. 23 | */ 24 | int bus_apparmor_is_enabled(bool *enabledp) { 25 | *enabledp = false; 26 | return 0; 27 | } 28 | 29 | /** 30 | * bus_apparmor_dbus_supported() - check for apparmor dbus support 31 | * @supported: return argument telling if AppArmor DBus is supported 32 | * 33 | * If the AppArmor module is not loaded, or AppArmor does not support DBus, 34 | * set @supportedp to 'false', otherwise set it to 'true'. 35 | * 36 | * Returns: 0 if check succeeded, or negative error code on failure. 37 | */ 38 | int bus_apparmor_dbus_supported(bool *supportedp) { 39 | *supportedp = false; 40 | return 0; 41 | } 42 | 43 | int bus_apparmor_registry_new(struct BusAppArmorRegistry **registryp, const char *fallback_context) { 44 | *registryp = NULL; 45 | return 0; 46 | } 47 | 48 | BusAppArmorRegistry *bus_apparmor_registry_ref(BusAppArmorRegistry *registry) { 49 | return NULL; 50 | } 51 | 52 | BusAppArmorRegistry *bus_apparmor_registry_unref(BusAppArmorRegistry *registry) { 53 | return NULL; 54 | } 55 | 56 | int bus_apparmor_set_bus_type(BusAppArmorRegistry *registry, const char *bustype) { 57 | return 0; 58 | } 59 | 60 | int bus_apparmor_check_own(struct BusAppArmorRegistry *registry, const char *owner_context, 61 | const char *name) { 62 | return 0; 63 | } 64 | 65 | int bus_apparmor_check_send(BusAppArmorRegistry *registry, 66 | const char *sender_context, const char *receiver_context, 67 | NameSet *subject, uint64_t subject_id, 68 | const char *path, const char *interface, const char *method) { 69 | return 0; 70 | } 71 | 72 | int bus_apparmor_check_eavesdrop(BusAppArmorRegistry *registry, const char *context) { 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | LICENSE: 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | COPYRIGHT: (ordered alphabetically) 15 | Copyright (C) 2016-2023 Red Hat, Inc. 16 | Copyright (C) 2023 David Rheinsberg 17 | Copyright (C) 2023 Tom Gundersen 18 | 19 | AUTHORS: (ordered alphabetically) 20 | Allison Karlitskaya 21 | Attila Lakatos 22 | Barnabás Pőcze 23 | Camron Carter 24 | Chris Paulson-Ellis 25 | Chris PeBenito 26 | Daniel Rusek 27 | Daniele Nicolodi 28 | darkblaze69 29 | David Rheinsberg 30 | Evgeny Vereshchagin 31 | Frantisek Sumsal 32 | Georg Müller 33 | Hugo Osvaldo Barrera 34 | Jacob Alzén 35 | Jake Dane 36 | Jeffrey Bosboom 37 | Khem Raj 38 | Lily Danzig 39 | Laurent Bigonville 40 | Luca Boccassi 41 | Marc-Antoine Perennou 42 | Marcus Sundberg 43 | Mark Esler 44 | Michal Schmidt 45 | Mike Gilbert 46 | msizanoen1 47 | Ryan Wilson 48 | seaeunlee 49 | Stefan Agner 50 | Thomas Mühlbacher 51 | Tim Gates 52 | Tom Gundersen 53 | Tomas Korbar 54 | Yanko Kaneti 55 | Zbigniew Jędrzejewski-Szmek 56 | -------------------------------------------------------------------------------- /src/dbus/connection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Connection 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "dbus/sasl.h" 11 | #include "dbus/socket.h" 12 | #include "util/dispatch.h" 13 | 14 | typedef struct Connection Connection; 15 | typedef struct Message Message; 16 | typedef struct User User; 17 | 18 | enum { 19 | _CONNECTION_E_SUCCESS, 20 | 21 | CONNECTION_E_SASL_FAILURE, 22 | CONNECTION_E_SASL_VIOLATION, 23 | 24 | CONNECTION_E_EOF, 25 | CONNECTION_E_QUOTA, 26 | CONNECTION_E_UNEXPECTED_FDS, 27 | }; 28 | 29 | struct Connection { 30 | Socket socket; 31 | DispatchFile socket_file; 32 | SASLServer sasl_server; 33 | SASLClient sasl_client; 34 | 35 | bool server : 1; 36 | bool authenticated : 1; 37 | }; 38 | 39 | #define CONNECTION_NULL(_x) { \ 40 | .socket = SOCKET_NULL((_x).socket), \ 41 | .socket_file = DISPATCH_FILE_NULL((_x).socket_file), \ 42 | .sasl_server = SASL_SERVER_NULL, \ 43 | .sasl_client = SASL_CLIENT_NULL, \ 44 | } 45 | 46 | int connection_init_server(Connection *connection, 47 | DispatchContext *dispatch_ctx, 48 | DispatchFn dispatch_fn, 49 | User *user, 50 | const char *guid, 51 | int fd); 52 | int connection_init_client(Connection *connection, 53 | DispatchContext *dispatch_ctx, 54 | DispatchFn dispatch_fn, 55 | User *user, 56 | int fd); 57 | void connection_deinit(Connection *connection); 58 | 59 | void connection_get_stats(Connection *connection, 60 | unsigned int *n_in_bytesp, 61 | unsigned int *n_in_fdsp, 62 | unsigned int *n_out_bytesp, 63 | unsigned int *n_out_fdsp); 64 | int connection_open(Connection *connection); 65 | void connection_shutdown(Connection *connection); 66 | void connection_close(Connection *connection); 67 | int connection_dispatch(Connection *connection, uint32_t events); 68 | 69 | int connection_dequeue(Connection *connection, Message **messagep); 70 | int connection_queue(Connection *connection, User *user, Message *message); 71 | 72 | C_DEFINE_CLEANUP(Connection *, connection_deinit); 73 | 74 | /* inline helpers */ 75 | 76 | static inline bool connection_is_running(Connection *connection) { 77 | return socket_is_running(&connection->socket); 78 | } 79 | -------------------------------------------------------------------------------- /src/dbus/socket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * D-Bus Socket Abstraction 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "dbus/message.h" 11 | #include "dbus/queue.h" 12 | #include "util/user.h" 13 | 14 | typedef struct FDList FDList; 15 | typedef struct Socket Socket; 16 | typedef struct SocketBuffer SocketBuffer; 17 | 18 | #define SOCKET_LINE_PREALLOC (64UL) /* fits the longest sane SASL exchange */ 19 | #define SOCKET_FD_MAX (253UL) /* taken from kernel SCM_MAX_FD */ 20 | #define SOCKET_MMSG_MAX (16) /* randomly picked, no tuning done so far */ 21 | 22 | enum { 23 | _SOCKET_E_SUCCESS, 24 | 25 | /* I/O handling */ 26 | SOCKET_E_LOST_INTEREST, 27 | SOCKET_E_PREEMPTED, 28 | 29 | /* socket errors */ 30 | SOCKET_E_EOF, 31 | SOCKET_E_QUOTA, 32 | SOCKET_E_SHUTDOWN, 33 | }; 34 | 35 | /* socket IO */ 36 | 37 | struct Socket { 38 | User *user; 39 | int fd; 40 | 41 | bool shutdown : 1; 42 | bool reset : 1; 43 | bool hup_in : 1; 44 | bool hup_out : 1; 45 | 46 | struct { 47 | IQueue queue; 48 | MessageHeader header; 49 | Message *message; 50 | } in; 51 | 52 | struct SocketOut { 53 | CList queue; 54 | CList pending; 55 | } out; 56 | }; 57 | 58 | #define SOCKET_NULL(_x) { \ 59 | .fd = -1, \ 60 | .in.queue = IQUEUE_NULL((_x).in.queue), \ 61 | .out.queue = C_LIST_INIT((_x).out.queue), \ 62 | .out.pending = C_LIST_INIT((_x).out.pending), \ 63 | } 64 | 65 | void socket_init(Socket *socket, User *user, int fd); 66 | void socket_deinit(Socket *socket); 67 | 68 | int socket_dequeue_line(Socket *socket, const char **linep, size_t *np); 69 | int socket_dequeue(Socket *socket, Message **messagep); 70 | 71 | int socket_queue_line(Socket *socket, User *user, const char *line, size_t n); 72 | int socket_queue(Socket *socket, User *user, Message *message); 73 | 74 | int socket_dispatch(Socket *socket, uint32_t event); 75 | void socket_shutdown(Socket *socket); 76 | void socket_close(Socket *socket); 77 | void socket_get_stats(Socket *socket, 78 | unsigned int *n_in_bytesp, 79 | unsigned int *n_in_fdsp, 80 | unsigned int *n_out_bytesp, 81 | unsigned int *n_out_fdsp); 82 | 83 | C_DEFINE_CLEANUP(Socket *, socket_deinit); 84 | 85 | /* inline helpers */ 86 | 87 | static inline bool socket_is_running(Socket *socket) { 88 | return !socket->reset; 89 | } 90 | -------------------------------------------------------------------------------- /test/dbus/meson.build: -------------------------------------------------------------------------------- 1 | # 2 | # target: libtest.so 3 | # 4 | 5 | sources_test = [ 6 | 'util-broker.c', 7 | 'util-message.c', 8 | ] 9 | 10 | deps_test = [ 11 | dep_bus, 12 | dep_cstdaux, 13 | dep_libsystemd, 14 | dep_thread, 15 | ] 16 | 17 | targets_test = [ 18 | exe_dbus_broker, 19 | ] 20 | 21 | static_test = static_library( 22 | 'test-private', 23 | sources_test, 24 | c_args: [ 25 | '-fvisibility=hidden', 26 | '-fno-common', 27 | ], 28 | dependencies: deps_test, 29 | pic: true, 30 | link_depends: targets_test, 31 | ) 32 | 33 | dep_test = declare_dependency( 34 | include_directories: include_directories('.'), 35 | link_with: static_test, 36 | dependencies: deps_test, 37 | version: meson.project_version(), 38 | ) 39 | 40 | # 41 | # target: tool-* 42 | # 43 | 44 | tool_flood = executable('tool-flood', ['tool-flood.c'], dependencies: [dep_test]) 45 | 46 | # 47 | # target: bench-* / test-* 48 | # 49 | 50 | test_kwargs = { 51 | 'dependencies': [dep_test], 52 | 'install': use_tests, 53 | 'install_dir': conf.get('testdir') / 'dbus', 54 | } 55 | 56 | bench_connect = executable('bench-connect', sources: ['bench-connect.c'], kwargs: test_kwargs) 57 | bench_message = executable('bench-message', sources: ['bench-message.c'], kwargs: test_kwargs) 58 | 59 | test_broker = executable('test-broker', sources: ['test-broker.c'], kwargs: test_kwargs) 60 | test_driver = executable('test-driver', sources: ['test-driver.c'], kwargs: test_kwargs) 61 | test_fdstream = executable('test-fdstream', sources: ['test-fdstream.c'], kwargs: test_kwargs) 62 | test_lifetime = executable('test-lifetime', sources: ['test-lifetime.c'], kwargs: test_kwargs) 63 | test_matches = executable('test-matches', sources: ['test-matches.c'], kwargs: test_kwargs) 64 | 65 | suites = [ 66 | { 'suite': 'dbus-broker', 'env': ['DBUS_BROKER_TEST_BROKER=' + exe_dbus_broker.full_path()]}, 67 | ] 68 | 69 | if use_reference_test 70 | dbus_daemon_bin = dep_dbus.get_variable(pkgconfig: 'bindir') / 'dbus-daemon' 71 | suites += [ 72 | { 'suite': 'dbus-daemon', 'env': ['DBUS_BROKER_TEST_DAEMON=' + dbus_daemon_bin]}, 73 | ] 74 | endif 75 | 76 | foreach suite : suites 77 | benchmark('Connection', bench_connect, timeout: 60, kwargs: suite) 78 | benchmark('Message passing', bench_message, timeout: 120, kwargs: suite) 79 | 80 | test('Broker API', test_broker, kwargs: suite) 81 | test('Driver API', test_driver, kwargs: suite) 82 | test('FD Stream Constraints', test_fdstream, kwargs: suite) 83 | test('Client Lifetime', test_lifetime, kwargs: suite) 84 | test('Signals and Matches', test_matches, kwargs: suite) 85 | endforeach 86 | -------------------------------------------------------------------------------- /src/util/nsec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Nanosecond Time Management 3 | * 4 | * This module provides time management utilities around the `nsec_t` type, 5 | * which carries time information encoded as nano-seconds since a 6 | * clock-specific EPOCH. The clock source is not encoded at all but must be 7 | * transferred via other means, if necessary. 8 | * 9 | * A 64-bit unsigned integer is used as backing data type. This can store 10 | * seconds up to: 11 | * 12 | * 2^64 / 1_000_000_000 = 18,446,744,073.7 13 | * 14 | * or years up to: 15 | * 16 | * 2^64 / 1_000_000_000 / 60 / 60 / 24 / 365 ~= 584 17 | * 18 | * This is a suitable range for time-keeping in most situations. If any 19 | * calendar, or other date-related functionality is needed, this type might not 20 | * be suitable. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "util/nsec.h" 28 | 29 | #define NSEC_PER_SEC UINT64_C(1000000000) 30 | 31 | /* Similar to `intprops.h`: provides `_MAX` for signed types without it. */ 32 | #define NSEC_TIME_T_MAX \ 33 | ((time_t)( \ 34 | (UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) \ 35 | ) 36 | 37 | /** 38 | * nsec_now() - get the current time in nanoseconds 39 | * @clock: clock to query 40 | * 41 | * Read the current time and return it in nanoseconds. The clock must be 42 | * specified by the caller. Only non-fallible clocks can be used with this 43 | * function. 44 | * 45 | * Return: the timestamp in nano seconds. 46 | */ 47 | nsec_t nsec_now(clockid_t clock) { 48 | struct timespec ts; 49 | int r; 50 | 51 | r = clock_gettime(clock, &ts); 52 | c_assert(r >= 0); 53 | 54 | return (uint64_t)ts.tv_sec * NSEC_PER_SEC + (uint64_t)ts.tv_nsec; 55 | } 56 | 57 | /** 58 | * nsec_sleep() - pause execution 59 | * @clock: clock to use for time keeping 60 | * @until: absolute timeout of the pause 61 | * 62 | * Pause exeuction until the absolute timeout specified by `until` is reached. 63 | * The timeout must be relative to the clock specified by `clock`. 64 | * 65 | * The operation is automatically repeated, if it is interrupted by a signal. 66 | */ 67 | void nsec_sleep(clockid_t clock, nsec_t until) { 68 | struct timespec ts; 69 | uint64_t tv_sec, tv_nsec; 70 | int r; 71 | 72 | tv_sec = until / NSEC_PER_SEC; 73 | tv_nsec = until % NSEC_PER_SEC; 74 | 75 | c_assert(tv_sec <= NSEC_TIME_T_MAX); 76 | 77 | ts.tv_sec = (time_t)tv_sec; 78 | ts.tv_nsec = (long)tv_nsec; 79 | 80 | do { 81 | /* Note that `clock_nanosleep()` does not use `errno`! */ 82 | r = clock_nanosleep(clock, TIMER_ABSTIME, &ts, NULL); 83 | c_assert(r == 0 || r == EINTR); 84 | } while (r == EINTR); 85 | } 86 | -------------------------------------------------------------------------------- /src/dbus/queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * D-Bus Input/Output Queues 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "util/fdlist.h" 11 | #include "util/user.h" 12 | 13 | typedef struct IQueue IQueue; 14 | 15 | #define IQUEUE_LINE_MAX (16UL * 1024UL) /* taken from dbus-daemon(1) */ 16 | #define IQUEUE_RECV_MAX (2UL * 1024UL) /* based on average message size */ 17 | 18 | enum { 19 | _IQUEUE_E_SUCCESS, 20 | 21 | IQUEUE_E_PENDING, 22 | IQUEUE_E_QUOTA, 23 | IQUEUE_E_VIOLATION, 24 | }; 25 | 26 | struct IQueue { 27 | User *user; 28 | 29 | UserCharge charge_data; 30 | UserCharge charge_fds; 31 | char *data; 32 | size_t data_size; 33 | size_t data_start; 34 | size_t data_end; 35 | size_t data_cursor; 36 | FDList *fds; 37 | 38 | struct { 39 | UserCharge charge_data; 40 | UserCharge charge_fds; 41 | void *data; 42 | size_t n_data; 43 | size_t n_copied; 44 | FDList *fds; 45 | } pending; 46 | 47 | char buffer[IQUEUE_RECV_MAX]; 48 | }; 49 | 50 | #define IQUEUE_NULL(_x) { \ 51 | .charge_data = USER_CHARGE_INIT, \ 52 | .charge_fds = USER_CHARGE_INIT, \ 53 | .data = (_x).buffer, \ 54 | .data_size = sizeof((_x).buffer), \ 55 | .pending.charge_data = USER_CHARGE_INIT, \ 56 | .pending.charge_fds = USER_CHARGE_INIT, \ 57 | } 58 | 59 | struct OQueue { 60 | User *user; 61 | CList list_buffers; 62 | CList list_inflight; 63 | }; 64 | 65 | #define OQUEUE_NULL(_x) { \ 66 | .list_buffers = C_LIST_INIT((_x).list_buffers), \ 67 | .list_inflight = C_LIST_INIT((_x).list_inflight), \ 68 | } 69 | 70 | /* input queue */ 71 | 72 | void iqueue_init(IQueue *iq, User *user); 73 | void iqueue_deinit(IQueue *iq); 74 | 75 | void iqueue_flush(IQueue *iq); 76 | int iqueue_set_target(IQueue *iq, void *data, size_t n_data); 77 | int iqueue_get_cursor(IQueue *iq, 78 | void **bufferp, 79 | size_t **fromp, 80 | size_t *top, 81 | FDList ***fdsp, 82 | UserCharge **charge_fdsp); 83 | 84 | int iqueue_pop_line(IQueue *iq, const char **linep, size_t *np); 85 | int iqueue_pop_data(IQueue *iq, FDList **fds); 86 | 87 | /* inline helpers */ 88 | 89 | static inline void *iqueue_get_target(IQueue *iq) { 90 | return iq->pending.data; 91 | } 92 | 93 | static inline bool iqueue_is_eof(IQueue *iq) { 94 | return iq->data_cursor >= iq->data_end && 95 | (!iq->pending.data || iq->pending.n_copied < iq->pending.n_data); 96 | } 97 | -------------------------------------------------------------------------------- /src/util/test-proc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test Proc Utilities 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include "util/misc.h" 9 | #include "util/proc.h" 10 | #include "util/string.h" 11 | #include "util/syscall.h" 12 | 13 | static void test_field(void) { 14 | char *value; 15 | int r; 16 | 17 | r = proc_field( 18 | "key: value", 19 | "key", 20 | &value 21 | ); 22 | c_assert(!r); 23 | c_assert(string_equal(value, "value")); 24 | c_free(value); 25 | 26 | r = proc_field( 27 | "key:", 28 | "key", 29 | &value 30 | ); 31 | c_assert(!r); 32 | c_assert(string_equal(value, "")); 33 | c_free(value); 34 | 35 | r = proc_field( 36 | "key1: none\n \t \nkey\t \t: \t value\t \t\nkey2: none", 37 | "key", 38 | &value 39 | ); 40 | c_assert(!r); 41 | c_assert(string_equal(value, "value")); 42 | c_free(value); 43 | 44 | r = proc_field("key: value", "key0", &value); 45 | c_assert(r == PROC_E_NOT_FOUND); 46 | r = proc_field("key0: value", "key", &value); 47 | c_assert(r == PROC_E_NOT_FOUND); 48 | r = proc_field(" key: value", "key", &value); 49 | c_assert(r == PROC_E_NOT_FOUND); 50 | r = proc_field("key key: value", "key", &value); 51 | c_assert(r == PROC_E_NOT_FOUND); 52 | r = proc_field("key", "key", &value); 53 | c_assert(r == PROC_E_NOT_FOUND); 54 | } 55 | 56 | static void test_read(void) { 57 | _c_cleanup_(c_closep) int fd = -1; 58 | const char *str = "01234567"; 59 | size_t i, n_data; 60 | char *data; 61 | ssize_t l; 62 | int r; 63 | 64 | fd = misc_memfd("test-proc", MISC_MFD_CLOEXEC | MISC_MFD_NOEXEC_SEAL, 0); 65 | c_assert(fd >= 0); 66 | 67 | for (i = 0; i < 1024; i += strlen(str)) { 68 | l = pwrite(fd, str, strlen(str), i); 69 | c_assert(l == 8); 70 | } 71 | 72 | r = proc_read(fd, &data, &n_data); 73 | c_assert(!r); 74 | c_assert(n_data == 1024); 75 | c_free(data); 76 | 77 | for ( ; i < 8192; i += strlen(str)) { 78 | l = pwrite(fd, str, strlen(str), i); 79 | c_assert(l == 8); 80 | } 81 | 82 | r = proc_read(fd, &data, &n_data); 83 | c_assert(!r); 84 | c_assert(n_data == 8192); 85 | c_free(data); 86 | } 87 | 88 | static void test_resolve_pidfd(void) { 89 | _c_cleanup_(c_closep) int pidfd = -1; 90 | pid_t pid; 91 | int r; 92 | 93 | pidfd = syscall_pidfd_open(getpid(), 0); 94 | if (pidfd < 0) { 95 | c_assert(errno == ENOSYS); 96 | return; 97 | } 98 | 99 | c_assert(pidfd >= 0); 100 | 101 | r = proc_resolve_pidfd(pidfd, &pid); 102 | c_assert(!r); 103 | c_assert(pid == getpid()); 104 | } 105 | 106 | int main(int argc, char **argv) { 107 | test_field(); 108 | test_read(); 109 | test_resolve_pidfd(); 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /src/util/user.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * User Accounting 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "util/ref.h" 12 | 13 | typedef struct Log Log; 14 | typedef struct UserCharge UserCharge; 15 | typedef struct UserUsage UserUsage; 16 | typedef struct User User; 17 | typedef struct UserRegistry UserRegistry; 18 | 19 | /* XXX: move this to some global broker header file */ 20 | enum { 21 | USER_SLOT_BYTES, 22 | USER_SLOT_FDS, 23 | USER_SLOT_MATCHES, 24 | USER_SLOT_OBJECTS, 25 | _USER_SLOT_N, 26 | }; 27 | 28 | static inline const char *user_slot_to_string(size_t slot) { 29 | switch (slot) { 30 | case USER_SLOT_BYTES: 31 | return "bytes"; 32 | case USER_SLOT_FDS: 33 | return "FDs"; 34 | case USER_SLOT_MATCHES: 35 | return "matches"; 36 | case USER_SLOT_OBJECTS: 37 | return "objects"; 38 | default: 39 | c_assert(0); 40 | abort(); 41 | } 42 | } 43 | 44 | enum { 45 | _USER_E_SUCCESS, 46 | 47 | USER_E_QUOTA, 48 | }; 49 | 50 | /* usage */ 51 | 52 | struct UserUsage { 53 | _Atomic unsigned long n_refs; 54 | User *user; 55 | uid_t uid; 56 | CRBNode user_node; 57 | 58 | bool logged : 1; 59 | 60 | unsigned int slots[]; 61 | }; 62 | 63 | /* charge */ 64 | 65 | struct UserCharge { 66 | UserUsage *usage; 67 | size_t slot; 68 | unsigned int charge; 69 | }; 70 | 71 | #define USER_CHARGE_INIT {} 72 | 73 | void user_charge_init(UserCharge *charge); 74 | void user_charge_deinit(UserCharge *charge); 75 | 76 | /* user */ 77 | 78 | struct User { 79 | _Atomic unsigned long n_refs; 80 | UserRegistry *registry; 81 | uid_t uid; 82 | CRBNode registry_node; 83 | 84 | CRBTree usage_tree; 85 | unsigned int n_usages; 86 | 87 | struct { 88 | unsigned int n; 89 | unsigned int max; 90 | } slots[]; 91 | }; 92 | 93 | void user_free(_Atomic unsigned long *n_refs, void *userdata); 94 | int user_charge(User *user, UserCharge *charge, User *actor, size_t slot, unsigned int amount); 95 | 96 | /* registry */ 97 | 98 | struct UserRegistry { 99 | Log *log; 100 | CRBTree user_tree; 101 | size_t n_slots; 102 | unsigned int *maxima; 103 | }; 104 | 105 | #define USER_REGISTRY_NULL { \ 106 | .user_tree = C_RBTREE_INIT, \ 107 | } 108 | 109 | int user_registry_init(UserRegistry *registry, Log *log, size_t n_slots, const unsigned int *maxima); 110 | void user_registry_deinit(UserRegistry *registry); 111 | int user_registry_ref_user(UserRegistry *registry, User **userp, uid_t uid); 112 | 113 | /* inline helpers */ 114 | 115 | static inline User *user_ref(User *user) { 116 | if (user) 117 | ref_inc(&user->n_refs); 118 | return user; 119 | } 120 | 121 | static inline User *user_unref(User *user) { 122 | if (user) 123 | ref_dec(&user->n_refs, user_free, NULL); 124 | return NULL; 125 | } 126 | 127 | C_DEFINE_CLEANUP(User *, user_unref); 128 | -------------------------------------------------------------------------------- /src/util/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Log Context 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | typedef struct Log Log; 13 | typedef struct LogProvenance LogProvenance; 14 | 15 | enum { 16 | _LOG_E_SUCCESS, 17 | 18 | LOG_E_OVERSIZED, 19 | LOG_E_TRUNCATED, 20 | }; 21 | 22 | enum { 23 | LOG_MODE_NONE, 24 | LOG_MODE_STDERR, 25 | LOG_MODE_JOURNAL, 26 | }; 27 | 28 | struct Log { 29 | int log_fd; 30 | unsigned short mode; 31 | bool consumed : 1; 32 | bool lossy : 1; 33 | uint64_t n_dropped; 34 | 35 | int error; 36 | int level; 37 | 38 | int mem_fd; 39 | void *map; 40 | size_t map_size; 41 | size_t offset; 42 | }; 43 | 44 | struct LogProvenance { 45 | const char *file; 46 | int line; 47 | const char *func; 48 | }; 49 | 50 | #define LOG_NULL { \ 51 | .log_fd = -1, \ 52 | .mem_fd = -1, \ 53 | .map = MAP_FAILED, \ 54 | } 55 | 56 | #define LOG_PROVENANCE_HERE ((LogProvenance){ \ 57 | .file = __FILE__, \ 58 | .line = __LINE__, \ 59 | .func = __func__, \ 60 | }) 61 | 62 | /* log context */ 63 | 64 | void log_init(Log *log); 65 | void log_init_stderr(Log *log, int stderr_fd); 66 | void log_init_journal(Log *log, int journal_fd); 67 | void log_init_journal_consume(Log *log, int journal_fd); 68 | void log_deinit(Log *log); 69 | 70 | int log_get_fd(Log *log); 71 | void log_set_lossy(Log *log, bool lossy); 72 | 73 | int log_vcommitf(Log *log, const char *format, va_list args); 74 | 75 | void log_append(Log *log, const void *data, size_t n_data); 76 | void log_vappendf(Log *log, const char *format, va_list args); 77 | void log_append_common(Log *log, 78 | int level, 79 | int error, 80 | const char *id, 81 | LogProvenance prov); 82 | 83 | /* inline helpers */ 84 | 85 | static inline int log_commitf(Log *log, const char *format, ...) { 86 | va_list args; 87 | int r; 88 | 89 | va_start(args, format); 90 | r = log_vcommitf(log, format, args); 91 | va_end(args); 92 | 93 | return r; 94 | } 95 | 96 | static inline int log_commit_silent(Log *log) { 97 | return log_commitf(log, NULL); 98 | } 99 | 100 | static inline void log_appends(Log *log, const char *string) { 101 | return log_append(log, string, strlen(string)); 102 | } 103 | 104 | static inline void log_appendf(Log *log, const char *format, ...) { 105 | va_list args; 106 | 107 | va_start(args, format); 108 | log_vappendf(log, format, args); 109 | va_end(args); 110 | } 111 | 112 | #define log_append_here(_log, _level, _r, _id) \ 113 | log_append_common((_log), (_level), (_r), (_id), LOG_PROVENANCE_HERE) 114 | -------------------------------------------------------------------------------- /src/dbus/test-socket.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test D-Bus Socket Abstraction 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "dbus/message.h" 12 | #include "dbus/socket.h" 13 | 14 | static void test_setup(void) { 15 | _c_cleanup_(socket_deinit) Socket server = SOCKET_NULL(server), client = SOCKET_NULL(client); 16 | 17 | socket_init(&server, NULL, -1); 18 | socket_init(&client, NULL, -1); 19 | } 20 | 21 | static void test_line(void) { 22 | _c_cleanup_(socket_deinit) Socket client = SOCKET_NULL(client), server = SOCKET_NULL(server); 23 | const char *test = "TEST", *line; 24 | size_t n_bytes; 25 | int pair[2], r; 26 | 27 | r = socketpair(AF_UNIX, SOCK_STREAM, 0, pair); 28 | c_assert(r >= 0); 29 | 30 | socket_init(&client, NULL, pair[0]); 31 | socket_init(&server, NULL, pair[1]); 32 | 33 | r = socket_dequeue_line(&server, &line, &n_bytes); 34 | c_assert(!r && !line); 35 | 36 | r = socket_queue_line(&client, NULL, test, strlen(test)); 37 | c_assert(r == 0); 38 | 39 | r = socket_queue_line(&client, NULL, test, strlen(test)); 40 | c_assert(r == 0); 41 | 42 | r = socket_dispatch(&client, EPOLLOUT); 43 | c_assert(r == SOCKET_E_LOST_INTEREST); 44 | r = socket_dispatch(&server, EPOLLIN); 45 | c_assert(!r || r == SOCKET_E_PREEMPTED); 46 | 47 | r = socket_dequeue_line(&server, &line, &n_bytes); 48 | c_assert(!r && line); 49 | c_assert(n_bytes == strlen(test)); 50 | c_assert(memcmp(test, line, n_bytes) == 0); 51 | 52 | r = socket_dequeue_line(&server, &line, &n_bytes); 53 | c_assert(!r && line); 54 | c_assert(n_bytes == strlen(test)); 55 | c_assert(memcmp(test, line, n_bytes) == 0); 56 | 57 | r = socket_dequeue_line(&server, &line, &n_bytes); 58 | c_assert(!r && !line); 59 | } 60 | 61 | static void test_message(void) { 62 | _c_cleanup_(socket_deinit) Socket client = SOCKET_NULL(client), server = SOCKET_NULL(server); 63 | _c_cleanup_(message_unrefp) Message *message1 = NULL, *message2 = NULL; 64 | MessageHeader header = { 65 | .endian = 'l', 66 | }; 67 | int pair[2], r; 68 | 69 | r = socketpair(AF_UNIX, SOCK_STREAM, 0, pair); 70 | c_assert(r >= 0); 71 | 72 | socket_init(&client, NULL, pair[0]); 73 | socket_init(&server, NULL, pair[1]); 74 | 75 | r = socket_dequeue(&server, &message2); 76 | c_assert(!r && !message2); 77 | 78 | r = message_new_incoming(&message1, header); 79 | c_assert(r == 0); 80 | 81 | r = socket_queue(&client, NULL, message1); 82 | c_assert(!r); 83 | 84 | r = socket_dispatch(&client, EPOLLOUT); 85 | c_assert(r == SOCKET_E_LOST_INTEREST); 86 | r = socket_dispatch(&server, EPOLLIN); 87 | c_assert(!r || r == SOCKET_E_PREEMPTED); 88 | 89 | r = socket_dequeue(&server, &message2); 90 | c_assert(!r && message2); 91 | 92 | c_assert(memcmp(message1->header, message2->header, sizeof(header)) == 0); 93 | } 94 | 95 | int main(int argc, char **argv) { 96 | test_setup(); 97 | test_line(); 98 | test_message(); 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /src/bus/bus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Bus Context 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "bus/listener.h" 12 | #include "bus/match.h" 13 | #include "bus/name.h" 14 | #include "bus/peer.h" 15 | #include "util/sampler.h" 16 | #include "util/user.h" 17 | 18 | enum { 19 | _BUS_E_SUCCESS, 20 | 21 | BUS_E_FAILURE, 22 | }; 23 | 24 | enum { 25 | BUS_LOG_POLICY_TYPE_INTERNAL, 26 | BUS_LOG_POLICY_TYPE_SELINUX, 27 | BUS_LOG_POLICY_TYPE_APPARMOR, 28 | }; 29 | 30 | typedef struct Bus Bus; 31 | typedef struct Log Log; 32 | typedef struct Message Message; 33 | typedef struct User User; 34 | 35 | struct Bus { 36 | Log *log; 37 | User *user; 38 | pid_t pid; 39 | int pid_fd; 40 | gid_t *gids; 41 | size_t n_gids; 42 | char *seclabel; 43 | size_t n_seclabel; 44 | char machine_id[33]; 45 | char guid[16]; 46 | 47 | UserRegistry users; 48 | NameRegistry names; 49 | MatchCounters match_counters; 50 | MatchRegistry wildcard_matches; 51 | MatchRegistry sender_matches; 52 | PeerRegistry peers; 53 | 54 | uint64_t n_monitors; 55 | uint64_t listener_ids; 56 | uint64_t activation_ids; 57 | uint64_t stats_ids; 58 | 59 | Sampler sampler; 60 | }; 61 | 62 | #define BUS_NULL(_x) { \ 63 | .pid_fd = -1, \ 64 | .users = USER_REGISTRY_NULL, \ 65 | .names = NAME_REGISTRY_INIT, \ 66 | .match_counters = MATCH_COUNTERS_INIT, \ 67 | .wildcard_matches = MATCH_REGISTRY_INIT((_x).wildcard_matches), \ 68 | .sender_matches = MATCH_REGISTRY_INIT((_x).sender_matches), \ 69 | .peers = PEER_REGISTRY_INIT, \ 70 | .sampler = SAMPLER_INIT(CLOCK_THREAD_CPUTIME_ID), \ 71 | } 72 | 73 | int bus_init(Bus *bus, 74 | Log *log, 75 | const char *machine_id, 76 | unsigned int max_bytes, 77 | unsigned int max_fds, 78 | unsigned int max_matches, 79 | unsigned int max_objects); 80 | void bus_deinit(Bus *bus); 81 | 82 | Peer *bus_find_peer_by_name(Bus *bus, Name **namep, const char *name); 83 | void bus_get_monitor_destinations(Bus *bus, CList *destinations, Peer *sender, MessageMetadata *metadata); 84 | void bus_get_broadcast_destinations(Bus *bus, CList *destinations, MatchRegistry *matches, Peer *sender, MessageMetadata *metadata); 85 | 86 | void bus_log_append_receiver(Bus *bus, uint64_t receiver_id, NameSet *receiver_names, const char *receiver_label); 87 | void bus_log_append_sender(Bus *bus, uint64_t sender_id, NameSet *sender_names, const char *sender_label); 88 | void bus_log_append_transaction(Bus *bus, uint64_t sender_id, uint64_t receiver_id, NameSet *sender_names, NameSet *receiver_names, const char *sender_label, const char *receiver_label, Message *message); 89 | void bus_log_append_policy_send(Bus *bus, int policy_type, uint64_t sender_id, uint64_t receiver_id, NameSet *sender_names, NameSet *receiver_names, const char *sender_label, const char *receiver_label, Message *message); 90 | void bus_log_append_policy_receive(Bus *bus, int policy_type, uint64_t sender_id, uint64_t receiver_id, NameSet *sender_names, NameSet *receievr_names, Message *message); 91 | -------------------------------------------------------------------------------- /src/catalog/dbus-broker.catalog: -------------------------------------------------------------------------------- 1 | -- 199d4300277f495f84ba4028c984214c 2 | Subject: Falling back to resolving auxiliary groups using NSS 3 | Defined-By: dbus-broker 4 | Documentation: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/core/sock.c?id=28b5ba2aa0f55d80adb2624564ed2b170c19519e 5 | Support: https://groups.google.com/forum/#!forum/bus1-devel 6 | 7 | In order to determine the policy to apply to a peer we need to 8 | know the owning user as well as the users auxiliary groups. 9 | 10 | The user and group ids are provided to us by the SO_PEERCREDS 11 | sockopt, and on newer kernels we also get the auxiliary groups 12 | via the SO_PEERGROUPS sockopts. 13 | 14 | However, on older kernels where the SO_PEERGROUPS sockopt is not 15 | available, we fall back to query the auxiliary groups over NSS. 16 | This is racy as the auxiliary groups may change, and the groups 17 | we end up with may not be the ones at the time the socket was 18 | opened by the client. This is highly discouraged, and upating 19 | to a kernel with SO_PEERGROUPS support is recommended. 20 | 21 | -- 8af3357071af4153af414daae07d38e7 22 | Subject: Dispatched @DBUS_BROKER_METRICS_DISPATCH_COUNT@ messages 23 | Defined-By: dbus-broker 24 | Support: https://groups.google.com/forum/#!forum/bus1-devel 25 | 26 | This message is printed by dbus-broker when shutting down. It includes metric 27 | information collected during the runtime of dbus-broker. 28 | 29 | The message lists the number of dispatched messages 30 | (in this case @DBUS_BROKER_METRICS_DISPATCH_COUNT@) as well as the mean time to 31 | handling a single message. The time measurements exclude the time spent on 32 | writing to and reading from the kernel. 33 | 34 | -- b209c0d9d1764ab38d13b8e00d1784d6 35 | Subject: Peer @DBUS_BROKER_SENDER_UNIQUE_NAME@ violated the D-Bus protocol 36 | Defined-By: dbus-broker 37 | Support: https://groups.google.com/forum/#!forum/bus1-devel 38 | 39 | The peer @DBUS_BROKER_SENDER_UNIQUE_NAME@ attempted to violate the protocol 40 | and is therefore disconnected. 41 | 42 | -- 6fa70fa776044fa28be7a21daf42a108 43 | Subject: Peer @DBUS_BROKER_RECEIVER_UNIQUE_NAME@ could not receive a requested message 44 | Defined-By: dbus-broker 45 | Support: https://groups.google.com/forum/#!forum/bus1-devel 46 | 47 | The peer @DBUS_BROKER_SENDER_UNIQUE_NAME@ failed to receive a message it requested 48 | and is therefore disconnected. The message in question could be a method 49 | error or reply or a broadcast the peer has subscribed to. 50 | 51 | Disconnecting the peer ensures that no messages are silently dropped. 52 | 53 | -- 2809c31175b84192b574d2a3da9fa8da 54 | Subject: "Peer @DBUS_BROKER_SENDER_UNIQUE_NAME@ is being disconnected as it does not have the resources to queue further messages." 55 | Defined-By: dbus-broker 56 | Support: https://groups.google.com/forum/#!forum/bus1-devel 57 | 58 | The peer @DBUS_BROKER_SENDER_UNIQUE_NAME@ exceeded its quota and thus its 59 | attempt to queue a message on the bus was declined by the broker. The message 60 | in question could be any type of D-Bus message (method-call, reply, signal, 61 | error). 62 | 63 | Disconnecting the peer ensures that no messages are silently dropped. 64 | 65 | -- 0691c10b72b14341955a75873c867e29 66 | Subject: "Peer @DBUS_BROKER_RECEIVER_UNIQUE_NAME@ is being disconnected as it does not have the resources to receive a reply it requested." 67 | Defined-By: dbus-broker 68 | Support: https://groups.google.com/forum/#!forum/bus1-devel 69 | 70 | The peer @DBUS_BROKER_RECEIVER_UNIQUE_NAME@ exceeded its quota and thus it 71 | cannot receiver a reply from @DBUS_BROKER_SENDER_UNIQUE_NAME@, which it 72 | requested earlier. 73 | 74 | Disconnecting the receiver ensures that no messages are silently dropped. 75 | -------------------------------------------------------------------------------- /test/dbus/bench-connect.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Connection Benchmarks 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "util/sampler.h" 11 | #include "util-broker.h" 12 | #include "util-message.h" 13 | 14 | #define TEST_N_ITERATIONS 500 15 | 16 | static void test_connect_blocking_fd(Broker *broker, int *fdp) { 17 | _c_cleanup_(c_closep) int fd = -1; 18 | int r; 19 | 20 | fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 21 | c_assert(fd >= 0); 22 | 23 | r = connect(fd, (struct sockaddr *)&broker->address, broker->n_address); 24 | c_assert(r >= 0); 25 | 26 | *fdp = fd; 27 | fd = -1; 28 | } 29 | 30 | static void test_connect_one(Sampler *sampler, void *input, ssize_t n_input, ssize_t n_output) { 31 | _c_cleanup_(util_broker_freep) Broker *broker = NULL; 32 | char output[n_output]; 33 | ssize_t len; 34 | int fd; 35 | 36 | util_broker_new(&broker); 37 | util_broker_spawn(broker); 38 | util_broker_settle(broker); 39 | 40 | sampler_sample_start(sampler); 41 | 42 | test_connect_blocking_fd(broker, &fd); 43 | 44 | len = write(fd, input, n_input); 45 | c_assert(len == (ssize_t)n_input); 46 | 47 | len = recv(fd, output, sizeof(output), MSG_WAITALL); 48 | c_assert(len == (ssize_t)n_output); 49 | 50 | sampler_sample_end(sampler); 51 | 52 | fd = c_close(fd); 53 | 54 | util_broker_terminate(broker); 55 | } 56 | 57 | static void test_sasl(void) { 58 | _c_cleanup_(sampler_deinit) Sampler sampler = SAMPLER_INIT(CLOCK_MONOTONIC_RAW); 59 | _c_cleanup_(c_freep) void *buf = NULL; 60 | size_t n_buf = 0; 61 | 62 | test_message_append_sasl(&buf, &n_buf); 63 | 64 | for (unsigned int i = 0; i < TEST_N_ITERATIONS; ++i) 65 | test_connect_one(&sampler, buf, n_buf, 58); 66 | 67 | fprintf(stderr, "SASL transaction completed in %"PRIu64" (+/- %.0f) us\n", 68 | sampler.average / 1000, sampler_read_standard_deviation(&sampler) / 1000); 69 | } 70 | 71 | static void test_hello(void) { 72 | _c_cleanup_(sampler_deinit) Sampler sampler = SAMPLER_INIT(CLOCK_MONOTONIC_RAW); 73 | _c_cleanup_(c_freep) void *buf = NULL; 74 | size_t n_buf = 0; 75 | 76 | test_message_append_sasl(&buf, &n_buf); 77 | test_message_append_hello(&buf, &n_buf); 78 | 79 | for (unsigned int i = 0; i < TEST_N_ITERATIONS; ++i) 80 | test_connect_one(&sampler, buf, n_buf, 316); 81 | 82 | fprintf(stderr, "SASL + Hello transaction completed in %"PRIu64" (+/- %.0f) us\n", 83 | sampler.average / 1000, sampler_read_standard_deviation(&sampler) / 1000); 84 | } 85 | 86 | static void test_transaction(void) { 87 | _c_cleanup_(sampler_deinit) Sampler sampler = SAMPLER_INIT(CLOCK_MONOTONIC_RAW); 88 | _c_cleanup_(c_freep) void *buf = NULL; 89 | size_t n_buf = 0; 90 | 91 | test_message_append_sasl(&buf, &n_buf); 92 | test_message_append_hello(&buf, &n_buf); 93 | test_message_append_signal(&buf, &n_buf, 1, 1); 94 | 95 | for (unsigned int i = 0; i < TEST_N_ITERATIONS; ++i) 96 | test_connect_one(&sampler, buf, n_buf, 436); 97 | 98 | fprintf(stderr, "SASL + Hello + message transaction completed in %"PRIu64" (+/- %.0f) us\n", 99 | sampler.average / 1000, sampler_read_standard_deviation(&sampler) / 1000); 100 | } 101 | 102 | int main(int argc, char **argv) { 103 | test_sasl(); 104 | test_hello(); 105 | test_transaction(); 106 | } 107 | -------------------------------------------------------------------------------- /src/bus/reply.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Reply Registry 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "bus/reply.h" 10 | #include "util/error.h" 11 | #include "util/user.h" 12 | 13 | typedef struct ReplySlotKey ReplySlotKey; 14 | 15 | struct ReplySlotKey { 16 | uint64_t id; 17 | uint32_t serial; 18 | }; 19 | 20 | static int reply_slot_compare(CRBTree *tree, void *k, CRBNode *rb) { 21 | ReplySlot *slot = c_container_of(rb, ReplySlot, registry_node); 22 | ReplySlotKey *key = k; 23 | 24 | if (key->id < slot->id) 25 | return -1; 26 | if (key->id > slot->id) 27 | return 1; 28 | 29 | if (key->serial < slot->serial) 30 | return -1; 31 | if (key->serial > slot->serial) 32 | return 1; 33 | 34 | return 0; 35 | } 36 | 37 | int reply_slot_new(ReplySlot **replyp, ReplyRegistry *registry, ReplyOwner *owner, User *user, User *actor, uint64_t id, uint32_t serial) { 38 | ReplySlot *reply; 39 | CRBNode **slot, *parent; 40 | ReplySlotKey key = { 41 | .id = id, 42 | .serial = serial, 43 | }; 44 | int r; 45 | 46 | slot = c_rbtree_find_slot(®istry->reply_tree, reply_slot_compare, &key, &parent); 47 | if (!slot) 48 | return REPLY_E_EXISTS; 49 | 50 | reply = calloc(1, sizeof(*reply)); 51 | if (!reply) 52 | return error_origin(-ENOMEM); 53 | 54 | reply->registry = registry; 55 | reply->owner = owner; 56 | reply->charge = (UserCharge)USER_CHARGE_INIT; 57 | reply->registry_node = (CRBNode)C_RBNODE_INIT(reply->registry_node); 58 | reply->owner_link = (CList)C_LIST_INIT(reply->owner_link); 59 | reply->id = id; 60 | reply->serial = serial; 61 | 62 | r = user_charge(user, &reply->charge, actor, USER_SLOT_OBJECTS, 1); 63 | if (r) 64 | return (r == USER_E_QUOTA) ? REPLY_E_QUOTA : error_fold(r); 65 | 66 | c_rbtree_add(®istry->reply_tree, parent, slot, &reply->registry_node); 67 | c_list_link_tail(&owner->reply_list, &reply->owner_link); 68 | 69 | *replyp = reply; 70 | 71 | return 0; 72 | } 73 | 74 | ReplySlot *reply_slot_free(ReplySlot *slot) { 75 | if (!slot) 76 | return NULL; 77 | 78 | user_charge_deinit(&slot->charge); 79 | c_list_unlink(&slot->owner_link); 80 | c_rbnode_unlink(&slot->registry_node); 81 | 82 | free(slot); 83 | 84 | return NULL; 85 | } 86 | 87 | ReplySlot *reply_slot_get_by_id(ReplyRegistry *registry, uint64_t id, uint32_t serial) { 88 | ReplySlotKey key = { 89 | .id = id, 90 | .serial = serial, 91 | }; 92 | 93 | return c_rbtree_find_entry(®istry->reply_tree, reply_slot_compare, &key, ReplySlot, registry_node); 94 | } 95 | 96 | void reply_registry_init(ReplyRegistry *registry) { 97 | *registry = (ReplyRegistry)REPLY_REGISTRY_INIT; 98 | } 99 | 100 | void reply_registry_deinit(ReplyRegistry *registry) { 101 | c_assert(c_rbtree_is_empty(®istry->reply_tree)); 102 | } 103 | 104 | void reply_owner_init(ReplyOwner *owner) { 105 | *owner = (ReplyOwner)REPLY_OWNER_INIT(*owner); 106 | } 107 | 108 | void reply_owner_deinit(ReplyOwner *owner) { 109 | c_assert(c_list_is_empty(&owner->reply_list)); 110 | } 111 | 112 | void reply_owner_get_stats(ReplyOwner *owner, unsigned int *n_objectsp) { 113 | ReplySlot *reply; 114 | unsigned int n_objects = 0; 115 | 116 | c_list_for_each_entry(reply, &owner->reply_list, owner_link) 117 | n_objects += reply->charge.charge; 118 | 119 | *n_objectsp = n_objects; 120 | } 121 | -------------------------------------------------------------------------------- /src/util/sampler.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Sampler Helper 3 | * 4 | * The sampler object is used to compute the min/max/avg/std deviation of 5 | * samples of CPU time, in fixed size and without memory allocations. 6 | * 7 | * The values of min/max/avg are meant to be read out of the struct directly, 8 | * whereas the standard deviation can only be accessed using a helper function 9 | * (as it is not actually stored directly, but computed on-demand). 10 | * 11 | * Only one sample may be active at any point in time, and every sample that is 12 | * started, must be stopped. 13 | * 14 | * See `Note on a Method for Calculating Corrected Sums of Squares and Products' 15 | * by W. P. Welford, 1962. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "util/sampler.h" 23 | 24 | void sampler_init(Sampler *sampler, clockid_t id) { 25 | *sampler = (Sampler)SAMPLER_INIT(id); 26 | } 27 | 28 | void sampler_deinit(Sampler *sampler) { 29 | c_assert(sampler->timestamp == SAMPLER_TIMESTAMP_INVALID); 30 | sampler_init(sampler, sampler->id); 31 | } 32 | 33 | /** 34 | * sampler_get_time() - get the current thread CPU time 35 | * 36 | * Read the current thread CPU time to be used to record samples. 37 | * 38 | * Return: the timestamp in nano seconds. 39 | */ 40 | uint64_t sampler_get_time(Sampler *sampler) { 41 | struct timespec ts; 42 | int r; 43 | 44 | r = clock_gettime(sampler->id, &ts); 45 | c_assert(r >= 0); 46 | 47 | return ts.tv_sec * UINT64_C(1000000000) + ts.tv_nsec; 48 | } 49 | 50 | /** 51 | * sampler_sample_add() - add one sample 52 | * @sampler: object to operate on 53 | * @timestamp: time the sample was started 54 | * 55 | * Update the internal state with a new sample, started at @timestamp 56 | * and ending at the time the function is called. 57 | */ 58 | void sampler_sample_add(Sampler *sampler, uint64_t timestamp) { 59 | uint64_t sample, average_old; 60 | 61 | sample = sampler_get_time(sampler) - timestamp; 62 | 63 | sampler->count ++; 64 | sampler->sum += sample; 65 | 66 | average_old = sampler->average; 67 | sampler->average = sampler->sum / sampler->count; 68 | sampler->sum_of_squares += (sample - average_old) * (sample - sampler->average); 69 | 70 | if (sampler->minimum > sample) 71 | sampler->minimum = sample; 72 | 73 | if (sampler->maximum < sample) 74 | sampler->maximum = sample; 75 | } 76 | 77 | /** 78 | * sampler_sample_start() - start a new sample 79 | * @sampler: object to operate on 80 | * 81 | * Start a new sample by recording the current timestamp, verifying that 82 | * a sample is not currently running. 83 | */ 84 | void sampler_sample_start(Sampler *sampler) { 85 | c_assert(sampler->timestamp == SAMPLER_TIMESTAMP_INVALID); 86 | sampler->timestamp = sampler_get_time(sampler); 87 | } 88 | 89 | /** 90 | * sampler_sample_end() - end a running sample 91 | * @sampler: object to operate on 92 | * 93 | * End a currently running sample, and update the internal state. 94 | */ 95 | void sampler_sample_end(Sampler *sampler) { 96 | c_assert(sampler->timestamp != SAMPLER_TIMESTAMP_INVALID); 97 | 98 | sampler_sample_add(sampler, sampler->timestamp); 99 | 100 | sampler->timestamp = SAMPLER_TIMESTAMP_INVALID; 101 | } 102 | 103 | /** 104 | * sampler_read_standard_deviation() - read out the current standard deviation 105 | * @sampler: objcet to operate on 106 | * 107 | * This computes and returns the standard deviation of the samples recorded 108 | * so far. The standard devitaion is not stored internally, but computed 109 | * on-demand. 110 | * 111 | * If the standard deviation is not defined (no samples were taken), then 112 | * zero is returned. 113 | * 114 | * Return: the standard deviation, or 0 if not defined. 115 | */ 116 | double sampler_read_standard_deviation(Sampler *sampler) { 117 | if (!sampler->count) 118 | return 0; 119 | 120 | return sqrt(sampler->sum_of_squares / sampler->count); 121 | } 122 | -------------------------------------------------------------------------------- /src/util/dirwatch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Directory Watch 3 | * 4 | * The Dirwatch object implements a simple notification mechanism based on 5 | * file-system monitoring. It allows to get notified whenever a file inside of 6 | * a list of directories is modified. 7 | * 8 | * Since file-system monitoring is frowned upon, we only implement what is 9 | * necessary to be compatible to dbus-daemon. This means, only very basic 10 | * direct watches without detailed reporting are supported. 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "util/dirwatch.h" 18 | #include "util/error.h" 19 | 20 | /** 21 | * dirwatch_new() - initialize directory watch 22 | * @dwp: output variable for directory watch 23 | * 24 | * This initializes a new directory watch. 25 | * 26 | * Return: 0 on success, negative error code on failure. 27 | */ 28 | int dirwatch_new(Dirwatch **dwp) { 29 | _c_cleanup_(dirwatch_freep) Dirwatch *dw = NULL; 30 | 31 | dw = malloc(sizeof(*dw)); 32 | if (!dw) 33 | return error_origin(-ENOMEM); 34 | 35 | *dw = (Dirwatch)DIRWATCH_NULL(*dw); 36 | 37 | dw->inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); 38 | if (dw->inotify_fd < 0) 39 | return error_origin(-errno); 40 | 41 | *dwp = dw; 42 | dw = NULL; 43 | return 0; 44 | } 45 | 46 | /** 47 | * dirwatch_free() - deinitialize directory watch 48 | * @dw: directory watch to operate on, or NULL 49 | * 50 | * This deinitializes @dw. 51 | * 52 | * Return: NULL is returned. 53 | */ 54 | Dirwatch *dirwatch_free(Dirwatch *dw) { 55 | if (dw) { 56 | c_close(dw->inotify_fd); 57 | free(dw); 58 | } 59 | return NULL; 60 | } 61 | 62 | /** 63 | * dirwatch_get_fd() - return context FD 64 | * @dw: directory watch to operate on 65 | * 66 | * Return the inotify context file-descriptor. The caller is supposed to 67 | * monitor it for read-events and call dirwatch_dispatch() when ready. 68 | * 69 | * Return: File-descriptor is returned. 70 | */ 71 | int dirwatch_get_fd(Dirwatch *dw) { 72 | return dw->inotify_fd; 73 | } 74 | 75 | /** 76 | * dirwatch_dispatch() - dispatch the directory watch 77 | * @dw: directory watch to operate on 78 | * 79 | * This dispatches all active events on the dirwatch. If a file-system 80 | * modification was detected, DIRWATCH_E_TRIGGERED is signalled to the caller. 81 | * 82 | * Return: 0 is returned on success, DIRWATCH_E_TRIGGERED if a file-system 83 | * modification was detected, a negative error code on failure. 84 | */ 85 | int dirwatch_dispatch(Dirwatch *dw) { 86 | uint8_t buffer[16 * (sizeof(struct inotify_event) + NAME_MAX + 1)]; 87 | bool triggered = false; 88 | ssize_t l; 89 | 90 | do { 91 | l = read(dw->inotify_fd, buffer, sizeof(buffer)); 92 | if (l > 0) { 93 | triggered = true; 94 | } else if (!l) { 95 | return error_origin(-EINVAL); 96 | } else if (errno != EAGAIN) { 97 | return error_origin(-errno); 98 | } 99 | } while (l > 0); 100 | 101 | return triggered ? DIRWATCH_E_TRIGGERED : 0; 102 | } 103 | 104 | /** 105 | * dirwatch_add() - add path to watch-list 106 | * @dw: directory watch to operate on 107 | * @path: path to watch 108 | */ 109 | int dirwatch_add(Dirwatch *dw, const char *path) { 110 | int r; 111 | 112 | r = inotify_add_watch(dw->inotify_fd, 113 | path, 114 | IN_CLOSE_WRITE | IN_DELETE 115 | | IN_MOVED_TO 116 | | IN_MOVED_FROM); 117 | if (r < 0) { 118 | /* non-existant dirs are silently ignored by dbus-daemon */ 119 | if (errno == ENOENT || errno == ENOTDIR) 120 | return 0; 121 | 122 | return error_origin(-errno); 123 | } 124 | 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /src/launch/config.dtd: -------------------------------------------------------------------------------- 1 | 6 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 44 | 45 | 46 | 51 | 52 | 53 | 77 | 78 | 79 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 112 | 113 | 114 | 116 | -------------------------------------------------------------------------------- /src/util/test-fdlist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test Utility FD List 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include 9 | #include "util/fdlist.h" 10 | 11 | static void test_setup(void) { 12 | _c_cleanup_(fdlist_freep) FDList *l = NULL; 13 | int r; 14 | 15 | r = fdlist_new_with_fds(&l, NULL, 0); 16 | c_assert(!r); 17 | 18 | l = fdlist_free(l); 19 | c_assert(!l); 20 | } 21 | 22 | static void test_dummy(void) { 23 | FDList *l; 24 | int r, dummies[] = { 7, 6, 5, 4, 3, 2, 1, 0 }; 25 | size_t i, j; 26 | 27 | /* 28 | * Allocate FDList objects with sizes 0-8, with data taken from 29 | * @dummies. We verify that the file-descriptors are not touched (since 30 | * they are dummy values that do not correspond to an FD), and that the 31 | * fdlist returns the correct data. 32 | */ 33 | 34 | for (i = 0; i < C_ARRAY_SIZE(dummies) + 1; ++i) { 35 | r = fdlist_new_with_fds(&l, dummies, i); 36 | c_assert(!r); 37 | 38 | c_assert(fdlist_count(l) == i); 39 | 40 | for (j = 0; j < i; ++j) 41 | c_assert((size_t)fdlist_get(l, j) == C_ARRAY_SIZE(dummies) - j - 1); 42 | 43 | l = fdlist_free(l); 44 | } 45 | } 46 | 47 | static void test_consumer(void) { 48 | int r, prev, p[2]; 49 | FDList *l; 50 | 51 | /* 52 | * Verify that the `consume' feature of FDList objects actually takes 53 | * posession of passed FDs and closes them on destruction. We use 54 | * epoll-fds as examples here, though any FD would work. 55 | * 56 | * We use the fact that FD spaces are sparse to verify that a given FD 57 | * was actually properly closed. 58 | */ 59 | 60 | prev = epoll_create1(EPOLL_CLOEXEC); 61 | c_assert(prev >= 0); 62 | 63 | p[0] = epoll_create1(EPOLL_CLOEXEC); 64 | c_assert(p[0] >= 0); 65 | c_assert(p[0] == prev + 1); 66 | 67 | p[1] = epoll_create1(EPOLL_CLOEXEC); 68 | c_assert(p[1] >= 0); 69 | c_assert(p[1] == prev + 2); 70 | 71 | r = fdlist_new_consume_fds(&l, p, C_ARRAY_SIZE(p)); 72 | c_assert(!r); 73 | 74 | c_assert(fdlist_count(l) == C_ARRAY_SIZE(p)); 75 | c_assert(!memcmp(fdlist_data(l), p, sizeof(p))); 76 | 77 | fdlist_truncate(l, 0); 78 | 79 | r = epoll_create1(EPOLL_CLOEXEC); 80 | c_assert(r == prev + 1); 81 | close(r); 82 | 83 | close(prev); 84 | 85 | l = fdlist_free(l); 86 | } 87 | 88 | static void test_dup(void) { 89 | int r, prev, p[2], expected[2]; 90 | FDList *l; 91 | 92 | /* 93 | * Verify that the `dup' feature of FDList objects actually duplicates 94 | * the passed FDs and closes them on destruction. We use epoll-fds as 95 | * examples here, though any FD would work. 96 | * 97 | * We use the fact that FD spaces are sparse to verify that a given FD 98 | * was actually properly closed. 99 | */ 100 | 101 | prev = epoll_create1(EPOLL_CLOEXEC); 102 | c_assert(prev >= 0); 103 | 104 | p[0] = epoll_create1(EPOLL_CLOEXEC); 105 | c_assert(p[0] >= 0); 106 | c_assert(p[0] == prev + 1); 107 | 108 | p[1] = epoll_create1(EPOLL_CLOEXEC); 109 | c_assert(p[1] >= 0); 110 | c_assert(p[1] == prev + 2); 111 | 112 | r = fdlist_new_dup_fds(&l, p, C_ARRAY_SIZE(p)); 113 | c_assert(!r); 114 | 115 | expected[0] = prev + 3; 116 | expected[1] = prev + 4; 117 | 118 | c_assert(fdlist_count(l) == C_ARRAY_SIZE(p)); 119 | c_assert(!memcmp(fdlist_data(l), expected, sizeof(expected))); 120 | 121 | fdlist_truncate(l, 0); 122 | 123 | r = epoll_create1(EPOLL_CLOEXEC); 124 | c_assert(r == prev + 3); 125 | close(r); 126 | 127 | close(p[1]); 128 | close(p[0]); 129 | close(prev); 130 | 131 | l = fdlist_free(l); 132 | } 133 | 134 | int main(int argc, char **argv) { 135 | test_setup(); 136 | test_dummy(); 137 | test_consumer(); 138 | test_dup(); 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /docs/dbus-broker-launch.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | dbus-broker-launch 3 | ================== 4 | 5 | ---------------------------------- 6 | Launcher for D-Bus Message Brokers 7 | ---------------------------------- 8 | 9 | :Manual section: 1 10 | :Manual group: User Commands 11 | 12 | SYNOPSIS 13 | ======== 14 | 15 | | ``dbus-broker-launch`` [ OPTIONS ] 16 | | ``dbus-broker-launch`` ``--version`` 17 | | ``dbus-broker-launch`` ``--help`` 18 | 19 | 20 | DESCRIPTION 21 | =========== 22 | 23 | **dbus-broker-launch** is a launcher for **dbus-broker**, spawning and managing 24 | a D-Bus Message Bus. The launcher aims to be fully compatible to the D-Bus 25 | reference implementation **dbus-daemon**\(1), supporting the same configuration 26 | syntax and runtime environment. 27 | 28 | Each instance of **dbus-broker-launch** manages exactly one message bus. Each 29 | message bus is independent. The configuration file can either be specified via 30 | the command-line, or the default is picked from */usr/share/dbus-1/*. Nearly 31 | all of the configuration attributes are supported. See **dbus-daemon**\(1) for 32 | details on the configuration syntax. 33 | 34 | OPTIONS 35 | ======= 36 | 37 | The following command-line options are supported. If an option is passed, which 38 | is not listed here, the launcher will deny startup and exit with an error. 39 | 40 | -h, --help print usage information and exit immediately 41 | --version print build-version and exit immediately 42 | --audit enable logging to the linux audit subsystem 43 | (no-op if audit support was not compiled in; 44 | **Default**: off) 45 | --config-file=PATH config file to use (**Default**: 46 | */usr/share/dbus-1/{system,session}.conf*) 47 | --scope=SCOPE select scope to run in (one of: *system*, 48 | *user*; **Default**: *system*) 49 | 50 | LOGGING 51 | ======= 52 | 53 | By default, **dbus-broker-launch** logs messages to the system journal. The 54 | messages are augmented with lots of metadata, so be sure to check the 55 | additional journal-fields. The human-readable log-message is intentionally kept 56 | short. 57 | 58 | On startup and shutdown, the launcher logs initial messages that contain 59 | information on the parsed configuration files and service definitions. No other 60 | log-messages are generated, except those originating in **dbus-broker**\(1). 61 | 62 | SCOPE 63 | ===== 64 | 65 | Unlike **dbus-daemon**\(1), **dbus-broker-launch** activates all services as 66 | systemd units. Services that already come with a systemd-unit are activated as 67 | usual, but services that lack a systemd unit are activated as transient unit, 68 | with an ad-hoc unit-file generated at runtime. This guarantees that all 69 | services run in a well-defined environment. 70 | 71 | The **--scope** parameter defines which systemd instance the launcher shall use 72 | to activate services. In case of *system*, the launcher will use the system 73 | instance of systemd. In case of *user*, the user instance is used instead. 74 | 75 | Furthermore, the selected scope also defines which configuration file is used 76 | if none is specified on the command-line. 77 | 78 | The selected scope does not have any further effect. It is only needed to 79 | define the activation environment for loaded service definitions. If no 80 | activatable services are declared, the scope will have no effect at all. 81 | 82 | SOCKETS 83 | ======= 84 | 85 | The socket to listen on for client connections must be created and passed to 86 | **dbus-broker-launch** by its parent process. The protocol must follow the 87 | socket-activation as defined by **systemd.socket**\(1). Only a single socket is 88 | supported right now. 89 | 90 | Additional *%path%* attributes in the configuration are 91 | ignored. 92 | 93 | PRIVILEGES 94 | ========== 95 | 96 | The launcher needs read-access to its configuration file. Other than that, no 97 | privileges are needed. If the *%user%* configuration attribute is 98 | used, the launcher will drop privileges when executing **dbus-broker**. 99 | 100 | If activatable services are declared, the launcher will need access to the 101 | corresponding systemd instance. The launcher must be allowed to spawn transient 102 | units, as well as manage units declared in the service definitions. 103 | 104 | SEE ALSO 105 | ======== 106 | 107 | ``dbus-daemon``\(1) 108 | ``dbus-broker``\(1) 109 | -------------------------------------------------------------------------------- /src/dbus/message.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * D-Bus Messages 5 | */ 6 | 7 | #include 8 | #include 9 | #include "dbus/address.h" 10 | #include "dbus/protocol.h" 11 | #include "util/ref.h" 12 | 13 | typedef struct FDList FDList; 14 | typedef struct Log Log; 15 | typedef struct Message Message; 16 | typedef struct MessageHeader MessageHeader; 17 | typedef struct MessageMetadata MessageMetadata; 18 | 19 | /* max message size; taken from spec */ 20 | #define MESSAGE_SIZE_MAX (128UL * 1024UL * 1024UL) 21 | 22 | /* max patch buffer size; see message_stitch_sender() */ 23 | #define MESSAGE_PATCH_MAX (C_ALIGN_TO(1 + 3 + 4 + ADDRESS_ID_STRING_MAX + 1, 8)) 24 | 25 | enum { 26 | _MESSAGE_E_SUCCESS, 27 | 28 | MESSAGE_E_CORRUPT_HEADER, 29 | MESSAGE_E_TOO_LARGE, 30 | MESSAGE_E_INVALID_HEADER, 31 | MESSAGE_E_INVALID_BODY, 32 | 33 | MESSAGE_E_MISSING_FDS, 34 | }; 35 | 36 | struct MessageMetadata { 37 | struct { 38 | uint8_t type; 39 | uint8_t flags; 40 | uint8_t version; 41 | uint32_t serial; 42 | } header; 43 | 44 | uint64_t sender_id; 45 | 46 | struct { 47 | unsigned int available; 48 | const char *path; 49 | const char *interface; 50 | const char *member; 51 | const char *error_name; 52 | uint32_t reply_serial; 53 | const char *destination; 54 | const char *sender; 55 | const char *signature; 56 | uint32_t unix_fds; 57 | } fields; 58 | 59 | struct { 60 | char element; 61 | const void *value; 62 | } args[64]; 63 | size_t n_args; 64 | }; 65 | 66 | #define MESSAGE_METADATA_INIT { \ 67 | .sender_id = ADDRESS_ID_INVALID, \ 68 | } 69 | 70 | struct Message { 71 | _Atomic unsigned long n_refs; 72 | 73 | bool big_endian : 1; 74 | bool allocated_data : 1; 75 | bool parsed : 1; 76 | 77 | FDList *fds; 78 | 79 | size_t n_data; 80 | size_t n_copied; 81 | size_t n_header; 82 | size_t n_body; 83 | 84 | void *data; 85 | MessageHeader *header; 86 | MessageMetadata metadata; 87 | void *body; 88 | 89 | void *original_sender; 90 | struct iovec vecs[4]; 91 | alignas(8) uint8_t patch[MESSAGE_PATCH_MAX]; 92 | alignas(8) uint8_t extra[]; 93 | }; 94 | 95 | #define MESSAGE_INIT(_big_endian) { \ 96 | .n_refs = REF_INIT, \ 97 | .big_endian = _big_endian, \ 98 | .metadata = MESSAGE_METADATA_INIT, \ 99 | } 100 | 101 | struct MessageHeader { 102 | uint8_t endian; 103 | uint8_t type; 104 | uint8_t flags; 105 | uint8_t version; 106 | uint32_t n_body; 107 | uint32_t serial; 108 | uint32_t n_fields; 109 | } _c_packed_; 110 | 111 | int message_new_incoming(Message **messagep, MessageHeader header); 112 | int message_new_outgoing(Message **messagep, void *data, size_t n_data); 113 | void message_free(_Atomic unsigned long *n_refs, void *userdata); 114 | 115 | int message_parse_metadata(Message *message); 116 | void message_stitch_sender(Message *message, uint64_t sender_id); 117 | 118 | void message_log_append(Message *message, Log *log); 119 | 120 | /* inline helpers */ 121 | 122 | /** 123 | * message_ref() - XXX 124 | */ 125 | static inline Message *message_ref(Message *message) { 126 | if (message) 127 | ref_inc(&message->n_refs); 128 | return message; 129 | } 130 | 131 | /** 132 | * message_unref() - XXX 133 | */ 134 | static inline Message *message_unref(Message *message) { 135 | if (message) 136 | ref_dec(&message->n_refs, message_free, NULL); 137 | return NULL; 138 | } 139 | 140 | /** 141 | * message_read_serial() - XXX 142 | */ 143 | static inline uint32_t message_read_serial(Message *message) { 144 | if (message->header->type != DBUS_MESSAGE_TYPE_METHOD_CALL || 145 | _c_unlikely_(message->header->flags & DBUS_HEADER_FLAG_NO_REPLY_EXPECTED)) 146 | return 0; 147 | 148 | if (_c_likely_(!message->big_endian)) 149 | return le32toh(message->header->serial); 150 | else 151 | return be32toh(message->header->serial); 152 | } 153 | 154 | C_DEFINE_CLEANUP(Message *, message_unref); 155 | -------------------------------------------------------------------------------- /src/catalog/dbus-broker-launch.catalog: -------------------------------------------------------------------------------- 1 | -- 7fc63312330b479bb32e598d47cef1a8 2 | Subject: Activation request failed due to missing unit 3 | Defined-By: dbus-broker 4 | Support: https://groups.google.com/forum/#!forum/bus1-devel 5 | 6 | systemd failed to activate the name @DBUS_BROKER_LAUNCH_SERVICE_NAME@ 7 | because the service @DBUS_BROKER_LAUNCH_SERVICE_UNIT@ could not be found. 8 | 9 | A possible reason for this is that @DBUS_BROKER_LAUNCH_SERVICE_UNIT@ is an 10 | alias to a disabled service. This is allowed by design, to give the 11 | administrator a way to disable name activation for a given service while 12 | still allowing it to be installed and potentially started in other ways. 13 | 14 | In order not to flood the logs, this message is only emitted once per name. 15 | 16 | -- ee9799dab1e24d81b7bee7759a543e1b 17 | Subject: Activation request failed due to masked unit 18 | Defined-By: dbus-broker 19 | Support: https://groups.google.com/forum/#!forum/bus1-devel 20 | 21 | systemd failed to activate the name @DBUS_BROKER_LAUNCH_SERVICE_NAME@ 22 | because the service @DBUS_BROKER_LAUNCH_SERVICE_UNIT@ was masked. 23 | 24 | It is a valid setup to mask units to disable them locally, but keep them 25 | installed. Therefore, this is not considered an error. If you want the service 26 | to be activatable via D-Bus, you must unmask the service. The systemd 27 | documentation contains more information on masked services. 28 | 29 | In order not to flood the logs, this message is only emitted once per name. 30 | 31 | -- f15d2347662d483ea9bcd8aa1a691d28 32 | Subject: dbus-broker-launch received a SIGHUP 33 | Defined-By: dbus-broker 34 | Support: https://groups.google.com/forum/#!forum/bus1-devel 35 | 36 | The HUP signal was sent to the dbus-broker-launch process from PID @DBUS_BROKER_LAUNCH_SIGNAL_PID@, 37 | triggering a configuration reload. 38 | 39 | Note that SIGHUP is delivered asynchronously, so the caller doesn't know 40 | when the requested reload has completed. It is therefore recommended 41 | to use the org.freedekstop.DBus.ReloadConfig method call on the driver 42 | instead. 43 | 44 | -- c8c6cde1c488439aba371a664353d9d8 45 | Subject: A configuration directory was written to 46 | Defined-By: dbus-broker 47 | Support: https://groups.google.com/forum/#!forum/bus1-devel 48 | 49 | A write was detected to one of the directories containing D-Bus configuration 50 | files, triggering a configuration reload. 51 | 52 | This functionality exists for backwards compatibility to pick up changes to 53 | D-Bus configuration without an explicit reolad request. Typically when 54 | installing or removing third-party software causes D-Bus configuration files 55 | to be added or removed. 56 | 57 | It is worth noting that this may cause partial configuration to be loaded in 58 | case dispatching this notification races with the writing of the configuration 59 | files. However, a future notification will then cause the configuration to be 60 | reladed again. 61 | 62 | -- 0ce0fa61d1a9433dabd67417f6b8e535 63 | Subject: Failed to open service file 64 | Defined-By: dbus-broker 65 | Support: https://groups.google.com/forum/#!forum/bus1-devel 66 | 67 | A service file could not be opened for reading. For compatibility reasons the 68 | error is ignored and loading of the other service files continues. 69 | 70 | Failure to open the file file may be caused by for instance, a concurrent 71 | removal of the file or the file not being readable. 72 | 73 | -- 24dc708d9e6a4226a3efe2033bb744de 74 | Subject: Invalid service file 75 | Defined-By: dbus-broker 76 | Support: https://groups.google.com/forum/#!forum/bus1-devel 77 | 78 | A service file is a ini-type configuration file. 79 | 80 | It has one required section 81 | named [D-BUS Service]. The section contains the required key 'Name', which 82 | must be a valid D-Bus name that is unique across all service files. It also 83 | contains at least one of the two optional keys 'SystemdService' and 'Exec', 84 | as well as optionally the key 'User'. Exec must be a valid shell command and 85 | User must be a valid user on the system. 86 | 87 | A service file should be named after the D-Bus name it configures. That is 88 | a file containing Name=org.foo.bar1 should be named org.foo.bar1.service. 89 | For backwards compatibility, we only warn when files do not follow this 90 | convention when run as a user bus. The system bus considers this an error 91 | and ignores the service file. 92 | 93 | -- a0fa58cafd6f4f0c8d003d16ccf9e797 94 | Subject: dbus-broker process exited 95 | Defined-By: dbus-broker 96 | Support: https://groups.google.com/forum/#!forum/bus1-devel 97 | 98 | The dbus-broker-launch process was notified that its child, dbus-broker 99 | exited. 100 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | # 2 | # Global Project Setup 3 | # 4 | 5 | project( 6 | 'dbus-broker', 7 | default_options: [ 8 | 'c_std=c11', 9 | 'rust_std=2021', 10 | ], 11 | license: 'Apache-2.0', 12 | meson_version: '>=1.3', 13 | version: '37', 14 | ) 15 | 16 | add_languages('c', native: false) 17 | add_languages('rust', native: false) 18 | 19 | bindgen_msv = '0.60' 20 | rust_edition = '2021' 21 | rust_msv = '1.84' 22 | 23 | cc = meson.get_compiler('c') 24 | conf = configuration_data() 25 | mod_pkgconfig = import('pkgconfig') 26 | mod_rust = import('rust') 27 | rust = meson.get_compiler('rust') 28 | 29 | # 30 | # System Requirements 31 | # 32 | 33 | if rust.version().version_compare('<' + rust_msv) 34 | error('Found Rust ' + rust.version() + ' but requires >=' + rust_msv) 35 | endif 36 | 37 | bindgen_prog = find_program('bindgen', native: true, required: true, version: '>=' + bindgen_msv) 38 | bindgen_version = bindgen_prog.version() 39 | 40 | # 41 | # Mandatory Dependencies 42 | # 43 | 44 | dep_cdvar = dependency('libcdvar-1', version: '>=1.1.0') 45 | dep_cini = dependency('libcini-1', version: '>=1.1.0') 46 | dep_clist = dependency('libclist-3', version: '>=3.1.0') 47 | dep_crbtree = dependency('libcrbtree-3', version: '>=3.2.0') 48 | dep_cshquote = dependency('libcshquote-1', version: '>=1.1.0') 49 | dep_cstdaux = dependency('libcstdaux-1', version: '>=1.5.0') 50 | dep_libc_rs = dependency('libc-rs-0.2', version: '>=0.2.175') 51 | dep_math = cc.find_library('m') 52 | dep_osi = dependency('libosi-1', default_options: {'libc': true, 'std': true}, version: '>=1.0.0') 53 | dep_thread = dependency('threads') 54 | 55 | # 56 | # Default CFLAGS 57 | # 58 | 59 | add_project_arguments(dep_cstdaux.get_variable('cflags').split(' '), language: 'c') 60 | add_project_arguments('-D_TIME_BITS=64', language: 'c') 61 | add_project_arguments('-DBINDIR="' + join_paths(get_option('prefix'), get_option('bindir')) + '"', language: 'c') 62 | add_project_arguments('-DPACKAGE_VERSION=' + meson.project_version(), language: 'c') 63 | 64 | # 65 | # Config: apparmor 66 | # 67 | 68 | use_apparmor = get_option('apparmor') 69 | if use_apparmor 70 | dep_libapparmor = dependency('libapparmor', version: '>=3.0') 71 | endif 72 | 73 | # 74 | # Config: audit 75 | # 76 | 77 | use_audit = get_option('audit') 78 | if use_audit 79 | dep_libaudit = dependency('audit', version: '>=3.0') 80 | dep_libcapng = dependency('libcap-ng', version: '>=0.6') 81 | endif 82 | 83 | # 84 | # Config: docs 85 | # 86 | 87 | use_docs = get_option('docs') 88 | if use_docs 89 | prog_rst2man = find_program('rst2man', 'rst2man.py') 90 | endif 91 | 92 | # 93 | # Config: doctest 94 | # 95 | 96 | use_doctest = get_option('doctest') 97 | 98 | # 99 | # Config: launcher 100 | # 101 | 102 | use_launcher = get_option('launcher') 103 | if use_launcher 104 | dep_expat = dependency('expat', version: '>=2.2') 105 | dep_libsystemd = dependency('libsystemd', version: '>=230') 106 | dep_systemd = dependency('systemd', version: '>=230') 107 | 108 | add_project_arguments('-DSYSTEMUIDMAX=' + dep_systemd.get_variable('systemuidmax'), language: 'c') 109 | conf.set('systemunitdir', dep_systemd.get_variable('systemdsystemunitdir')) 110 | conf.set('userunitdir', dep_systemd.get_variable('systemduserunitdir')) 111 | conf.set('catalogdir', dep_systemd.get_variable('catalogdir')) 112 | endif 113 | 114 | # 115 | # Config: reference-test 116 | # 117 | 118 | use_reference_test = get_option('reference-test') 119 | if use_reference_test 120 | dep_dbus = dependency('dbus-1', version: '>=1.10') 121 | endif 122 | 123 | # 124 | # Config: selinux 125 | # 126 | 127 | use_selinux = get_option('selinux') 128 | if use_selinux 129 | dep_libselinux = dependency('libselinux', version: '>=3.2') 130 | endif 131 | 132 | # 133 | # Config: system-console-users 134 | # 135 | 136 | use_system_console_users = get_option('system-console-users') 137 | 138 | acc_sysusers = '' 139 | foreach user : use_system_console_users 140 | acc_sysusers += '"' + user + '",' 141 | endforeach 142 | 143 | add_project_arguments('-DSYSTEM_CONSOLE_USERS=' + acc_sysusers, language: 'c') 144 | 145 | # 146 | # Config: tests 147 | # 148 | 149 | use_tests = get_option('tests') 150 | conf.set('testdir', get_option('prefix') / 'lib/dbus-broker/tests') 151 | 152 | # 153 | # Global Parameters 154 | # 155 | 156 | conf.set('bindir', join_paths(get_option('prefix'), get_option('bindir'))) 157 | 158 | # 159 | # Subdirs 160 | # 161 | 162 | subdir('src') 163 | 164 | if use_docs 165 | subdir('docs') 166 | endif 167 | 168 | if use_launcher 169 | subdir('test/dbus') 170 | endif 171 | -------------------------------------------------------------------------------- /src/util/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * String Helpers 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | enum { 12 | _UTIL_STRING_E_SUCCESS, 13 | 14 | UTIL_STRING_E_INVALID, 15 | UTIL_STRING_E_RANGE, 16 | }; 17 | 18 | int util_strtou32(uint32_t *valp, const char *string); 19 | int util_strtou64(uint64_t *valp, const char *string); 20 | int util_strtoint(int *valp, const char *string); 21 | 22 | /** 23 | * string_compare() - compare two strings 24 | * @a: first string to compare, or NULL 25 | * @b: second string to compare, or NULL 26 | * 27 | * Compare two strings, the same way strcmp() does it. 28 | * Additionally, NULL is allowed as input, which compares equal to itself 29 | * and smaller than any other string. 30 | * 31 | * Return: Less than, greater than or equal to zero, as strcmp(). 32 | */ 33 | _c_pure_ static inline int string_compare(const char *a, const char *b) { 34 | if (a == b) 35 | return 0; 36 | 37 | return (!a || !b) ? (a ? 1 : -1) : strcmp(a, b); 38 | } 39 | 40 | /** 41 | * string_equal() - compare strings for equality 42 | * @a: first string to compare, or NULL 43 | * @b: second string to compare, or NULL 44 | * 45 | * Compare two strings for equality, the same way strcmp() does it. 46 | * Additionally, NULL is allowed as input and compares equal to itself only. 47 | * Unlike strcmp(), this returns a boolean. 48 | * 49 | * Return: True if both are equal, false if not. 50 | */ 51 | _c_pure_ static inline bool string_equal(const char *a, const char *b) { 52 | return (!a || !b) ? (a == b) : !strcmp(a, b); 53 | } 54 | 55 | /** 56 | * string_prefix() - check prefix of a string 57 | * @str: string to check 58 | * @prefix: prefix to look for 59 | * 60 | * This checks whether @str starts with @prefix. If it does, a pointer to the 61 | * first character in @str after the prefix is returned, if not, NULL is 62 | * returned. 63 | * 64 | * Return: Pointer directly behind the prefix in @str, or NULL if not found. 65 | */ 66 | _c_pure_ static inline char *string_prefix(const char *str, const char *prefix) { 67 | size_t l = strlen(prefix); 68 | return !strncmp(str, prefix, l) ? (char *)str + l : NULL; 69 | } 70 | 71 | /** 72 | * string_to_hex() - encode string as ascii-hex 73 | * @str: string to encode from 74 | * @n: length of @str in bytes 75 | * @hex: destination buffer 76 | * 77 | * This hex-encodes the source string into the destination buffer. The 78 | * destination buffer must be at least twice as big as the source. 79 | */ 80 | static inline void string_to_hex(const char *str, size_t n, char *hex) { 81 | // Include terminating NUL to silence warnings about truncated strings. 82 | static const char table[17] = "0123456789abcdef"; 83 | size_t i; 84 | 85 | for (i = 0; i < n; ++i) { 86 | *hex++ = table[(*str >> 4) & 0x0f]; 87 | *hex++ = table[(*str++) & 0x0f]; 88 | } 89 | } 90 | 91 | /** 92 | * string_from_hex() - decode ascii-hex string 93 | * @str: string buffer to write into 94 | * @n: length of @str in bytes 95 | * @hex: hex encoded buffer to decode 96 | * 97 | * This hex-decodes @hex into the string buffer @str. Be aware that @hex must 98 | * be twice the size as @str / @n. 99 | * 100 | * Return: True if successful, false if invalid. 101 | */ 102 | static inline bool string_from_hex(char *str, size_t n, const char *hex) { 103 | static const uint8_t table[128] = { 104 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 105 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 106 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 107 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, -1, -1, -1, -1, -1, -1, 108 | -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, 109 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 110 | -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, 111 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 112 | }; 113 | uint8_t v1, v2; 114 | 115 | for ( ; n; --n, hex += 2) { 116 | v1 = table[hex[0] & 0x7f]; 117 | v2 = table[hex[1] & 0x7f]; 118 | if (_c_unlikely_((hex[0] | hex[1] | v1 | v2) & 0x80)) 119 | return false; 120 | 121 | *str++ = (v1 << 4) | v2; 122 | } 123 | 124 | return true; 125 | } 126 | -------------------------------------------------------------------------------- /src/util/test-user.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test User Accounting 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include "util/user.h" 9 | 10 | static void test_setup(void) { 11 | UserRegistry registry; 12 | User *entry1, *entry2, *entry3; 13 | int r; 14 | 15 | r = user_registry_init(®istry, NULL, _USER_SLOT_N, (unsigned int[]){ 1024, 1024, 1024, 1024, 1024 }); 16 | c_assert(!r); 17 | 18 | r = user_registry_ref_user(®istry, &entry1, 1); 19 | c_assert(r == 0); 20 | c_assert(entry1); 21 | 22 | r = user_registry_ref_user(®istry, &entry2, 1); 23 | c_assert(r == 0); 24 | c_assert(entry2 == entry1); 25 | 26 | r = user_registry_ref_user(®istry, &entry3, 2); 27 | c_assert(r == 0); 28 | c_assert(entry3 != entry1); 29 | 30 | user_unref(entry1); 31 | user_unref(entry2); 32 | user_unref(entry3); 33 | user_registry_deinit(®istry); 34 | } 35 | 36 | static void test_quota(void) { 37 | UserRegistry registry; 38 | User *entry1, *entry2, *entry3; 39 | UserCharge charge1, charge2, charge3; 40 | int r; 41 | 42 | r = user_registry_init(®istry, NULL, _USER_SLOT_N, (unsigned int[]){ 1024, 1024, 1024, 1024, 1024 }); 43 | c_assert(!r); 44 | 45 | r = user_registry_ref_user(®istry, &entry1, 1); 46 | c_assert(r == 0); 47 | 48 | r = user_registry_ref_user(®istry, &entry2, 2); 49 | c_assert(r == 0); 50 | 51 | r = user_registry_ref_user(®istry, &entry3, 3); 52 | c_assert(r == 0); 53 | 54 | user_charge_init(&charge1); 55 | user_charge_init(&charge2); 56 | user_charge_init(&charge3); 57 | 58 | /* first actor can have exactly 512 bytes */ 59 | r = user_charge(entry1, &charge2, entry2, USER_SLOT_BYTES, 513); 60 | c_assert(r == USER_E_QUOTA); 61 | r = user_charge(entry1, &charge2, entry2, USER_SLOT_BYTES, 512); 62 | c_assert(!r); 63 | r = user_charge(entry1, &charge2, entry2, USER_SLOT_BYTES, 1); 64 | c_assert(r == USER_E_QUOTA); 65 | 66 | /* second actor exactly 170 */ 67 | r = user_charge(entry1, &charge3, entry3, USER_SLOT_BYTES, 171); 68 | c_assert(r == USER_E_QUOTA); 69 | r = user_charge(entry1, &charge3, entry3, USER_SLOT_BYTES, 170); 70 | c_assert(!r); 71 | 72 | /* release the first one and now the second one can have 512 total */ 73 | user_charge_deinit(&charge2); 74 | r = user_charge(entry1, &charge3, entry3, USER_SLOT_BYTES, 343); 75 | c_assert(r == USER_E_QUOTA); 76 | r = user_charge(entry1, &charge3, entry3, USER_SLOT_BYTES, 342); 77 | c_assert(r == 0); 78 | 79 | /* verify self-allocation can access the remaining 512 */ 80 | r = user_charge(entry1, &charge1, NULL, USER_SLOT_BYTES, 513); 81 | c_assert(r == USER_E_QUOTA); 82 | r = user_charge(entry1, &charge1, NULL, USER_SLOT_BYTES, 512); 83 | c_assert(!r); 84 | 85 | user_charge_deinit(&charge3); 86 | user_charge_deinit(&charge2); 87 | user_charge_deinit(&charge1); 88 | user_unref(entry3); 89 | user_unref(entry2); 90 | user_unref(entry1); 91 | user_registry_deinit(®istry); 92 | } 93 | 94 | static void test_overflow(void) { 95 | UserRegistry registry; 96 | User *entry1, *entry2; 97 | UserCharge charge1; 98 | int r; 99 | 100 | r = user_registry_init(®istry, NULL, _USER_SLOT_N, (unsigned int[]){ 1024, 1024, 1024, 1024, 1024 }); 101 | c_assert(!r); 102 | 103 | r = user_registry_ref_user(®istry, &entry1, 1); 104 | c_assert(r == 0); 105 | 106 | r = user_registry_ref_user(®istry, &entry2, 2); 107 | c_assert(r == 0); 108 | 109 | user_charge_init(&charge1); 110 | 111 | /* first actor gets exactly 512 bytes */ 112 | r = user_charge(entry1, &charge1, entry2, USER_SLOT_BYTES, 513); 113 | c_assert(r == USER_E_QUOTA); 114 | r = user_charge(entry1, &charge1, entry2, USER_SLOT_BYTES, 1024); 115 | c_assert(r == USER_E_QUOTA); 116 | r = user_charge(entry1, &charge1, entry2, USER_SLOT_BYTES, 1025); 117 | c_assert(r == USER_E_QUOTA); 118 | r = user_charge(entry1, &charge1, entry2, USER_SLOT_BYTES, 2048); 119 | c_assert(r == USER_E_QUOTA); 120 | r = user_charge(entry1, &charge1, entry2, USER_SLOT_BYTES, (unsigned int)-1); 121 | c_assert(r == USER_E_QUOTA); 122 | r = user_charge(entry1, &charge1, entry2, USER_SLOT_BYTES, 512); 123 | c_assert(!r); 124 | 125 | user_charge_deinit(&charge1); 126 | user_unref(entry2); 127 | user_unref(entry1); 128 | user_registry_deinit(®istry); 129 | } 130 | 131 | int main(int argc, char **argv) { 132 | test_setup(); 133 | test_quota(); 134 | test_overflow(); 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /src/bus/peer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Peers 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "bus/match.h" 12 | #include "bus/name.h" 13 | #include "bus/policy.h" 14 | #include "bus/reply.h" 15 | #include "dbus/connection.h" 16 | 17 | typedef struct Bus Bus; 18 | typedef struct DispatchContext DispatchContext; 19 | typedef struct Peer Peer; 20 | typedef struct PeerRegistry PeerRegistry; 21 | typedef struct Socket Socket; 22 | typedef struct User User; 23 | 24 | enum { 25 | _PEER_E_SUCCESS, 26 | 27 | PEER_E_QUOTA, 28 | 29 | PEER_E_CONNECTION_REFUSED, 30 | 31 | PEER_E_EOF, 32 | PEER_E_PROTOCOL_VIOLATION, 33 | 34 | PEER_E_SEND_DENIED, 35 | PEER_E_RECEIVE_DENIED, 36 | 37 | PEER_E_NAME_RESERVED, 38 | PEER_E_NAME_UNIQUE, 39 | PEER_E_NAME_REFUSED, 40 | PEER_E_NAME_ALREADY_OWNER, 41 | PEER_E_NAME_IN_QUEUE, 42 | PEER_E_NAME_EXISTS, 43 | PEER_E_NAME_NOT_FOUND, 44 | PEER_E_NAME_NOT_OWNER, 45 | 46 | PEER_E_MATCH_INVALID, 47 | PEER_E_MATCH_NOT_FOUND, 48 | 49 | PEER_E_EXPECTED_REPLY_EXISTS, 50 | PEER_E_UNEXPECTED_REPLY, 51 | 52 | PEER_E_UNEXPECTED_FDS, 53 | }; 54 | 55 | struct Peer { 56 | Bus *bus; 57 | User *user; 58 | pid_t pid; 59 | int pid_fd; 60 | gid_t *gids; 61 | size_t n_gids; 62 | char *seclabel; 63 | size_t n_seclabel; 64 | UserCharge charges[3]; 65 | 66 | uint64_t id; 67 | CRBNode registry_node; 68 | CList listener_link; 69 | 70 | Connection connection; 71 | bool registered : 1; 72 | bool monitor : 1; 73 | 74 | PolicySnapshot *policy; 75 | NameOwner owned_names; 76 | MatchRegistry sender_matches; 77 | MatchRegistry name_owner_changed_matches; 78 | MatchOwner owned_matches; 79 | ReplyRegistry replies; 80 | ReplyOwner owned_replies; 81 | }; 82 | 83 | #define PEER_INIT(_x) { \ 84 | .charges = { USER_CHARGE_INIT, USER_CHARGE_INIT, USER_CHARGE_INIT }, \ 85 | .registry_node = C_RBNODE_INIT((_x).registry_node), \ 86 | .listener_link = C_LIST_INIT((_x).listener_link), \ 87 | .connection = CONNECTION_NULL((_x).connection), \ 88 | .owned_names = NAME_OWNER_INIT, \ 89 | .sender_matches = MATCH_REGISTRY_INIT((_x).sender_matches), \ 90 | .name_owner_changed_matches = MATCH_REGISTRY_INIT((_x).name_owner_changed_matches), \ 91 | .owned_matches = MATCH_OWNER_INIT((_x).owned_matches), \ 92 | .replies = REPLY_REGISTRY_INIT, \ 93 | .owned_replies = REPLY_OWNER_INIT((_x).owned_replies), \ 94 | } 95 | 96 | struct PeerRegistry { 97 | CRBTree peer_tree; 98 | uint64_t ids; 99 | size_t n_peers; 100 | size_t n_registered; 101 | }; 102 | 103 | #define PEER_REGISTRY_INIT {} 104 | 105 | int peer_new_with_fd(Peer **peerp, Bus *bus, PolicyRegistry *policy, const char guid[], DispatchContext *dispatcher, int fd); 106 | Peer *peer_free(Peer *peer); 107 | 108 | int peer_dispatch(DispatchFile *file); 109 | int peer_spawn(Peer *peer); 110 | 111 | void peer_register(Peer *peer); 112 | void peer_unregister(Peer *peer); 113 | 114 | bool peer_is_privileged(Peer *peer); 115 | 116 | int peer_request_name(Peer *peer, const char *name, uint32_t flags, NameChange *change); 117 | int peer_release_name(Peer *peer, const char *name, NameChange *change); 118 | void peer_release_name_ownership(Peer *peer, NameOwnership *ownership, NameChange *change); 119 | 120 | int peer_add_match(Peer *peer, const char *rule_string); 121 | int peer_remove_match(Peer *peer, const char *rule_string); 122 | int peer_become_monitor(Peer *peer, MatchOwner *owner); 123 | void peer_stop_monitor(Peer *peer); 124 | void peer_flush_matches(Peer *peer); 125 | 126 | int peer_queue_unicast(PolicySnapshot *sender_policy, NameSet *sender_names, ReplyOwner *sender_replies, User *sender_user, uint64_t sender_id, Peer *receiver, Message *message); 127 | int peer_queue_reply(Peer *sender, const char *destination, uint32_t reply_serial, Message *message); 128 | 129 | void peer_registry_init(PeerRegistry *registry); 130 | void peer_registry_deinit(PeerRegistry *registry); 131 | void peer_registry_flush(PeerRegistry *registry); 132 | Peer *peer_registry_find_peer(PeerRegistry *registry, uint64_t id); 133 | 134 | static inline bool peer_is_registered(Peer *peer) { 135 | return peer->registered; 136 | } 137 | 138 | static inline bool peer_is_monitor(Peer *peer) { 139 | return peer->monitor; 140 | } 141 | 142 | C_DEFINE_CLEANUP(Peer *, peer_free); 143 | -------------------------------------------------------------------------------- /src/dbus/protocol.c: -------------------------------------------------------------------------------- 1 | /* 2 | * DBus Protocol Constants and Definitions 3 | */ 4 | 5 | #include 6 | #include 7 | #include "dbus/protocol.h" 8 | 9 | static bool dbus_validate_name_common(const char *name, size_t n_name, bool namespace) { 10 | bool has_dot = false, dot = true, unique = false; 11 | size_t i; 12 | 13 | if (n_name > 255) 14 | return false; 15 | 16 | if (n_name > 0 && name[0] == ':') { 17 | ++name; 18 | --n_name; 19 | unique = true; 20 | } 21 | 22 | for (i = 0; i < n_name; ++i) { 23 | if (name[i] == '.') { 24 | if (dot) 25 | return false; 26 | 27 | has_dot = true; 28 | dot = true; 29 | } else if (_c_unlikely_(!((name[i] >= 'a' && name[i] <= 'z') || 30 | (name[i] >= 'A' && name[i] <= 'Z') || 31 | (name[i] >= '0' && name[i] <= '9' && (!dot || unique)) || 32 | name[i] == '_' || 33 | name[i] == '-'))) { 34 | return false; 35 | } else { 36 | dot = false; 37 | } 38 | } 39 | 40 | return (has_dot || namespace) && !dot; 41 | } 42 | 43 | /** 44 | * dbus_validate_name() - verify validity of bus name 45 | * @name: name 46 | * @n_name: length of name 47 | * 48 | * This verifies the validity of the passed bus name. 49 | * 50 | * Return: True if @name is a valid bus name, false otherwise. 51 | */ 52 | bool dbus_validate_name(const char *name, size_t n_name) { 53 | return dbus_validate_name_common(name, n_name, false); 54 | } 55 | 56 | /** 57 | * dbus_validate_namespace() - verify validity of bus namespace 58 | * @namespace: namespace 59 | * @n_namespace: length of namespace 60 | * 61 | * This verifies the validity of the passed bus namespace. 62 | * 63 | * Return: True if @namespace is a valid bus namespace, false otherwise. 64 | */ 65 | bool dbus_validate_namespace(const char *namespace, size_t n_namespace) { 66 | return dbus_validate_name_common(namespace, n_namespace, true); 67 | } 68 | 69 | /** 70 | * dbus_validate_interface() - verify validity of interface 71 | * @interface interface 72 | * @n_interface: length of interface 73 | * 74 | * This verifies the validity of the passed interface. 75 | * 76 | * Return: True if @interface is a valid interface, false otherwise. 77 | */ 78 | bool dbus_validate_interface(const char *interface, size_t n_interface) { 79 | bool has_dot = false, dot = true; 80 | size_t i; 81 | 82 | if (n_interface > 255) 83 | return false; 84 | 85 | for (i = 0; i < n_interface; ++i) { 86 | if (interface[i] == '.') { 87 | if (dot) 88 | return false; 89 | 90 | has_dot = true; 91 | dot = true; 92 | } else if (_c_unlikely_(!((interface[i] >= 'a' && interface[i] <= 'z') || 93 | (interface[i] >= 'A' && interface[i] <= 'Z') || 94 | (interface[i] >= '0' && interface[i] <= '9' && !dot) || 95 | interface[i] == '_'))) { 96 | return false; 97 | } else { 98 | dot = false; 99 | } 100 | } 101 | 102 | return has_dot && !dot; 103 | } 104 | 105 | /** 106 | * dbus_validate_member() - verify validity of member 107 | * @member member 108 | * @n_member: length of interface 109 | * 110 | * This verifies the validity of the passed member. 111 | * 112 | * Return: True if @member is a valid member, false otherwise. 113 | */ 114 | bool dbus_validate_member(const char *member, size_t n_member) { 115 | bool first = true; 116 | size_t i; 117 | 118 | if (n_member > 255) 119 | return false; 120 | 121 | for (i = 0; i < n_member; ++i) { 122 | if (_c_unlikely_(!((member[i] >= 'a' && member[i] <= 'z') || 123 | (member[i] >= 'A' && member[i] <= 'Z') || 124 | (member[i] >= '0' && member[i] <= '9' && !first) || 125 | member[i] == '_'))) 126 | return false; 127 | first = false; 128 | } 129 | 130 | return !first; 131 | } 132 | 133 | /** 134 | * dbus_validate_error_name() - verify validity of error_name 135 | * @name: error name 136 | * @n_name: length of error name 137 | * 138 | * This verifies the validity of the passed error name. 139 | * 140 | * Return: True if @name is a valid error name, false otherwise. 141 | */ 142 | bool dbus_validate_error_name(const char *name, size_t n_name) { 143 | return dbus_validate_interface(name, n_name); 144 | } 145 | -------------------------------------------------------------------------------- /src/launch/test-config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test Config Parser 3 | */ 4 | 5 | #undef NDEBUG 6 | #include 7 | #include 8 | #include 9 | #include "launch/config.h" 10 | #include "launch/nss-cache.h" 11 | #include "util/dirwatch.h" 12 | #include "util/misc.h" 13 | 14 | static const char *test_type2str[_CONFIG_NODE_N] = { 15 | [CONFIG_NODE_BUSCONFIG] = "busconfig", 16 | [CONFIG_NODE_USER] = "user", 17 | [CONFIG_NODE_TYPE] = "type", 18 | [CONFIG_NODE_FORK] = "fork", 19 | [CONFIG_NODE_SYSLOG] = "syslog", 20 | [CONFIG_NODE_KEEP_UMASK] = "keep_umask", 21 | [CONFIG_NODE_LISTEN] = "listen", 22 | [CONFIG_NODE_PIDFILE] = "pidfile", 23 | [CONFIG_NODE_INCLUDEDIR] = "includedir", 24 | [CONFIG_NODE_STANDARD_SESSION_SERVICEDIRS] = "standard_session_servicedirs", 25 | [CONFIG_NODE_STANDARD_SYSTEM_SERVICEDIRS] = "standard_system_servicedirs", 26 | [CONFIG_NODE_SERVICEDIR] = "servicedir", 27 | [CONFIG_NODE_SERVICEHELPER] = "servicehelper", 28 | [CONFIG_NODE_AUTH] = "auth", 29 | [CONFIG_NODE_INCLUDE] = "include", 30 | [CONFIG_NODE_POLICY] = "policy", 31 | [CONFIG_NODE_LIMIT] = "limit", 32 | [CONFIG_NODE_SELINUX] = "selinux", 33 | [CONFIG_NODE_APPARMOR] = "apparmor", 34 | [CONFIG_NODE_ALLOW] = "allow", 35 | [CONFIG_NODE_DENY] = "deny", 36 | [CONFIG_NODE_ASSOCIATE] = "associate", 37 | }; 38 | 39 | static int config_memfd(const char *data) { 40 | ssize_t n; 41 | int fd; 42 | 43 | fd = misc_memfd("dbus-broker-test-config", MISC_MFD_NOEXEC_SEAL, 0); 44 | c_assert(fd >= 0); 45 | n = write(fd, data, strlen(data)); 46 | c_assert(n == (ssize_t)strlen(data)); 47 | 48 | return fd; 49 | } 50 | 51 | static int parse_config(ConfigRoot **rootp, const char *path) { 52 | _c_cleanup_(config_parser_deinit) ConfigParser parser = CONFIG_PARSER_NULL(parser); 53 | _c_cleanup_(config_root_freep) ConfigRoot *root = NULL; 54 | _c_cleanup_(nss_cache_deinit) NSSCache nss_cache = NSS_CACHE_INIT; 55 | _c_cleanup_(dirwatch_freep) Dirwatch *dirwatch = NULL; 56 | int r; 57 | 58 | r = dirwatch_new(&dirwatch); 59 | c_assert(!r); 60 | 61 | config_parser_init(&parser); 62 | 63 | r = config_parser_read(&parser, &root, path, &nss_cache, dirwatch); 64 | if (r) 65 | return r; 66 | 67 | *rootp = root; 68 | root = NULL; 69 | return 0; 70 | } 71 | 72 | static int parse_config_inline(ConfigRoot **rootp, const char *data) { 73 | _c_cleanup_(c_closep) int fd = -1; 74 | _c_cleanup_(c_freep) char *path = NULL; 75 | int r; 76 | 77 | fd = config_memfd(data); 78 | r = asprintf(&path, "/proc/self/fd/%d", fd); 79 | c_assert(r > 0); 80 | 81 | return parse_config(rootp, path); 82 | } 83 | 84 | static void print_config(const char *path) { 85 | _c_cleanup_(config_root_freep) ConfigRoot *root = NULL; 86 | ConfigNode *i_node; 87 | int r; 88 | 89 | r = parse_config(&root, path); 90 | c_assert(!r); 91 | 92 | c_list_for_each_entry(i_node, &root->node_list, root_link) { 93 | fprintf(stderr, "<%s>\n", test_type2str[i_node->type]); 94 | } 95 | } 96 | 97 | static void test_config_base(void) { 98 | _c_cleanup_(config_parser_deinit) ConfigParser parser = CONFIG_PARSER_NULL(parser); 99 | 100 | config_parser_init(&parser); 101 | config_parser_deinit(&parser); 102 | } 103 | 104 | static void test_config_sample0(void) { 105 | _c_cleanup_(config_root_freep) ConfigRoot *root = NULL; 106 | const char *data; 107 | int r; 108 | 109 | data = 110 | " \ 111 | \ 113 | \ 114 | \ 115 | \ 116 | \ 117 | \ 118 | \ 119 | "; 120 | 121 | r = parse_config_inline(&root, data); 122 | c_assert(r == CONFIG_E_INVALID); 123 | } 124 | 125 | static void test_config_sample1(void) { 126 | _c_cleanup_(config_root_freep) ConfigRoot *root = NULL; 127 | const char *data; 128 | int r; 129 | 130 | data = 131 | " \ 132 | \ 134 | \ 135 | \ 136 | \ 137 | \ 138 | \ 139 | \ 140 | \ 141 | "; 142 | 143 | r = parse_config_inline(&root, data); 144 | c_assert(r == CONFIG_E_INVALID); 145 | } 146 | 147 | int main(int argc, char **argv) { 148 | if (argc > 1) { 149 | print_config(argv[1]); 150 | return 0; 151 | } 152 | 153 | test_config_base(); 154 | test_config_sample0(); 155 | test_config_sample1(); 156 | 157 | return 0; 158 | } 159 | -------------------------------------------------------------------------------- /src/bus/listener.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Socket Listener 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "bus/bus.h" 11 | #include "bus/listener.h" 12 | #include "bus/peer.h" 13 | #include "bus/policy.h" 14 | #include "util/dispatch.h" 15 | #include "util/error.h" 16 | 17 | static int listener_dispatch(DispatchFile *file) { 18 | Listener *listener = c_container_of(file, Listener, socket_file); 19 | _c_cleanup_(peer_freep) Peer *peer = NULL; 20 | _c_cleanup_(c_closep) int fd = -1; 21 | int r; 22 | 23 | if (!(dispatch_file_events(file) & EPOLLIN)) 24 | return 0; 25 | 26 | fd = accept4(listener->socket_fd, NULL, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK); 27 | if (fd < 0) { 28 | if (errno == EAGAIN) { 29 | /* 30 | * EAGAIN implies there are no pending incoming 31 | * connections. Catch this, clear EPOLLIN and tell the 32 | * caller about it. 33 | */ 34 | dispatch_file_clear(&listener->socket_file, EPOLLIN); 35 | return 0; 36 | } else { 37 | /* 38 | * The linux UDS layer does not return pending errors 39 | * on the child socket (unlike the TCP layer). Hence, 40 | * there are no known errors to check for. 41 | */ 42 | return error_origin(-errno); 43 | } 44 | } 45 | 46 | r = peer_new_with_fd(&peer, listener->bus, listener->policy, listener->guid, file->context, fd); 47 | if (r == PEER_E_QUOTA || r == PEER_E_CONNECTION_REFUSED) 48 | /* 49 | * The user has too many open connections, or a policy disallows it to 50 | * connect. Simply drop this. 51 | */ 52 | return 0; 53 | else if (r) 54 | return error_fold(r); 55 | fd = -1; /* consume fd */ 56 | 57 | c_list_link_tail(&listener->peer_list, &peer->listener_link); 58 | 59 | r = peer_spawn(peer); 60 | if (r) 61 | return error_fold(r); 62 | 63 | r = peer_dispatch(&peer->connection.socket_file); 64 | peer = NULL; 65 | return error_fold(r); 66 | } 67 | 68 | /** 69 | * listener_init_with_fd() - XXX 70 | */ 71 | int listener_init_with_fd(Listener *l, 72 | Bus *bus, 73 | DispatchContext *dispatcher, 74 | int socket_fd, 75 | PolicyRegistry *policy) { 76 | _c_cleanup_(listener_deinitp) Listener *listener = l; 77 | int r; 78 | 79 | *listener = (Listener)LISTENER_NULL(*listener); 80 | listener->bus = bus; 81 | 82 | /* 83 | * Every listener socket needs its own, unique UUID for clients to 84 | * identify it. We simply generate those UUIDs from the bus-uuid, by 85 | * XOR'ing a unique 64bit counter on the lower 64bit, leaving the upper 86 | * 64bit unchanged. 87 | */ 88 | ++bus->listener_ids; 89 | for (size_t i = 0; i < sizeof(listener->guid); ++i) { 90 | listener->guid[i] = bus->guid[i]; 91 | if (i < sizeof(uint64_t)) 92 | listener->guid[i] ^= (bus->listener_ids >> (8 * i)) & 0xff; 93 | } 94 | 95 | r = dispatch_file_init(&listener->socket_file, 96 | dispatcher, 97 | listener_dispatch, 98 | socket_fd, 99 | EPOLLIN, 100 | EPOLLIN); 101 | if (r) 102 | return error_fold(r); 103 | 104 | dispatch_file_select(&listener->socket_file, EPOLLIN); 105 | 106 | listener->socket_fd = socket_fd; 107 | listener->policy = policy; 108 | listener = NULL; 109 | return 0; 110 | } 111 | 112 | /** 113 | * listener_deinit() - XXX 114 | */ 115 | void listener_deinit(Listener *listener) { 116 | c_assert(c_list_is_empty(&listener->peer_list)); 117 | 118 | policy_registry_free(listener->policy); 119 | dispatch_file_deinit(&listener->socket_file); 120 | listener->socket_fd = c_close(listener->socket_fd); 121 | listener->bus = NULL; 122 | } 123 | 124 | /** 125 | * listener_set_policy() - XXX 126 | */ 127 | int listener_set_policy(Listener *listener, PolicyRegistry *registry) { 128 | Peer *peer; 129 | int r; 130 | 131 | c_list_for_each_entry(peer, &listener->peer_list, listener_link) { 132 | PolicySnapshot *policy; 133 | 134 | r = policy_snapshot_new(&policy, registry, peer->seclabel, peer->user->uid, peer->gids, peer->n_gids); 135 | if (r) 136 | return error_fold(r); 137 | 138 | policy_snapshot_free(peer->policy); 139 | peer->policy = policy; 140 | } 141 | 142 | policy_registry_free(listener->policy); 143 | listener->policy = registry; 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /src/launch/policy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * D-Bus Policy Converter 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "launch/config.h" 13 | 14 | typedef struct Policy Policy; 15 | typedef struct PolicyEntries PolicyEntries; 16 | typedef struct PolicyNode PolicyNode; 17 | typedef struct PolicyRecord PolicyRecord; 18 | 19 | #define POLICY_PRIORITY_DEFAULT (UINT64_C(1)) 20 | 21 | struct PolicyRecord { 22 | CList link; 23 | 24 | bool verdict; 25 | uint64_t priority; 26 | 27 | union { 28 | struct { 29 | bool prefix; 30 | const char *name; 31 | } own; 32 | 33 | struct { 34 | const char *name; 35 | const char *path; 36 | const char *interface; 37 | const char *member; 38 | unsigned int type; 39 | unsigned int broadcast; 40 | uint64_t min_fds; 41 | uint64_t max_fds; 42 | } xmit; 43 | 44 | struct { 45 | const char *name; 46 | const char *context; 47 | } selinux; 48 | }; 49 | }; 50 | 51 | #define POLICY_RECORD_INIT_CONNECT(_x) { \ 52 | .link = C_LIST_INIT((_x).link), \ 53 | } 54 | 55 | #define POLICY_RECORD_INIT_OWN(_x) { \ 56 | .link = C_LIST_INIT((_x).link), \ 57 | } 58 | 59 | #define POLICY_RECORD_INIT_XMIT(_x) { \ 60 | .link = C_LIST_INIT((_x).link), \ 61 | } 62 | 63 | #define POLICY_RECORD_INIT_SELINUX(_x) { \ 64 | .link = C_LIST_INIT((_x).link), \ 65 | } 66 | 67 | struct PolicyEntries { 68 | CList connect_list; 69 | CList own_list; 70 | CList send_list; 71 | CList recv_list; 72 | }; 73 | 74 | #define POLICY_ENTRIES_NULL(_x) { \ 75 | .connect_list = C_LIST_INIT((_x).connect_list), \ 76 | .own_list = C_LIST_INIT((_x).own_list), \ 77 | .send_list = C_LIST_INIT((_x).send_list), \ 78 | .recv_list = C_LIST_INIT((_x).recv_list), \ 79 | } 80 | 81 | struct PolicyNode { 82 | uint32_t uidgid; 83 | CRBNode policy_node; 84 | 85 | PolicyEntries entries; 86 | }; 87 | 88 | #define POLICY_NODE_NULL(_x) { \ 89 | .uidgid = -1, \ 90 | .policy_node = C_RBNODE_INIT((_x).policy_node), \ 91 | .entries = POLICY_ENTRIES_NULL((_x).entries), \ 92 | } 93 | 94 | struct Policy { 95 | uint64_t i_priority; 96 | 97 | PolicyEntries default_entries; 98 | PolicyEntries at_console_entries; 99 | PolicyEntries no_console_entries; 100 | 101 | CRBTree uid_tree; 102 | CRBTree gid_tree; 103 | 104 | CList selinux_list; 105 | unsigned int apparmor_mode; 106 | char *bus_type; 107 | }; 108 | 109 | #define POLICY_INIT(_x) { \ 110 | .i_priority = POLICY_PRIORITY_DEFAULT, \ 111 | .default_entries = POLICY_ENTRIES_NULL((_x).default_entries), \ 112 | .at_console_entries = POLICY_ENTRIES_NULL((_x).at_console_entries), \ 113 | .no_console_entries = POLICY_ENTRIES_NULL((_x).no_console_entries), \ 114 | .uid_tree = C_RBTREE_INIT, \ 115 | .gid_tree = C_RBTREE_INIT, \ 116 | .selinux_list = C_LIST_INIT((_x).selinux_list), \ 117 | .apparmor_mode = CONFIG_APPARMOR_ENABLED, \ 118 | .bus_type = NULL, \ 119 | } 120 | 121 | /* records */ 122 | 123 | int policy_record_new_connect(PolicyRecord **recordp); 124 | int policy_record_new_own(PolicyRecord **recordp); 125 | int policy_record_new_xmit(PolicyRecord **recordp); 126 | int policy_record_new_selinux(PolicyRecord **recordp); 127 | PolicyRecord *policy_record_free(PolicyRecord *record); 128 | 129 | C_DEFINE_CLEANUP(PolicyRecord *, policy_record_free); 130 | 131 | /* policy */ 132 | 133 | void policy_init(Policy *policy); 134 | void policy_deinit(Policy *policy); 135 | 136 | int policy_import(Policy *policy, ConfigRoot *root); 137 | void policy_optimize(Policy *policy); 138 | int policy_export(Policy *policy, sd_bus_message *m, uint32_t *at_console_uids, size_t n_at_console_uids); 139 | 140 | C_DEFINE_CLEANUP(Policy *, policy_deinit); 141 | --------------------------------------------------------------------------------