From 3cd2da693e085a3b4403a632555fadca412697b2 Mon Sep 17 00:00:00 2001 From: James Lee Date: Tue, 30 Jun 2026 09:50:02 +0800 Subject: [PATCH 1/2] mcptd: Replace root in suggested configs Allow mctpd, which should be an unprivileged user, to run the daemon with minimum capabilities. mctpd needs CAP_NET_BIND_SERVICE and CAP_NET_ADMIN, so those have been added to AmbientCapabilities in the service config. The mctpd user has been allowed to own the au.com.codeconstruct.MCTP1 object as well as root. root also retains the right to own the object, meaning the config remains valid if the daemon is run as root. Unprivileged users can only access properties and are prohibited from configuring the network. Root and users in the mctp-admin group are permitted to interact with any method the daemon provides. For some setups, it may be useful to create a group with permissions to use the RegisterTypeSupport or RegisterVDMTypeSupport methods so that programs may register support without being given permission to modify the network characteristics. Signed-off-by: James Lee --- conf/mctpd-dbus.conf | 26 +++++++++++++++++++++++++- conf/mctpd.service | 2 ++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/conf/mctpd-dbus.conf b/conf/mctpd-dbus.conf index e97bfe00..4ee74497 100644 --- a/conf/mctpd-dbus.conf +++ b/conf/mctpd-dbus.conf @@ -2,9 +2,33 @@ + + + + + + + + + + + + + + + + + + + - diff --git a/conf/mctpd.service b/conf/mctpd.service index 62410c15..7f96c25e 100644 --- a/conf/mctpd.service +++ b/conf/mctpd.service @@ -8,6 +8,8 @@ After=mctp-local.target Type=dbus BusName=au.com.codeconstruct.MCTP1 ExecStart=/usr/sbin/mctpd +User=mctpd +AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN [Install] WantedBy=mctp.target From f43dd6c9e4ed2e4c4740387ba0eafac7c90b6785 Mon Sep 17 00:00:00 2001 From: James Lee Date: Wed, 1 Jul 2026 13:17:39 +0800 Subject: [PATCH 2/2] mctpd: Allow mctpd to manage its capabilities. Added the set_cap() function to mctpd, allowing the process to drop CAP_NET_BIND_SERVICE when it is no longer needed. This requires libcap on the target device. If libcap is missing drop_bind_cap() will be a stub, and mctpd will emit a warning when it starts. Because mctpd requires additional capabilities and may recieve hostile inputs, dropping the capability when it is no longer needed is useful to increase safety. libcap has been added as a possible dependency to the meson build and mcpd now uses the MCTPD_MANAGING_CAPABILITIES macro to determine if libcap is available. mctpd.md has been updated to reflect the new capability management. Signed-off-by: James Lee --- docs/mctpd.md | 11 +++++++++++ meson.build | 10 ++++++++-- src/mctpd.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/docs/mctpd.md b/docs/mctpd.md index 236e1a54..c5b8af6a 100644 --- a/docs/mctpd.md +++ b/docs/mctpd.md @@ -503,3 +503,14 @@ match = { path = "/devices/pci0000:00/0000:00:08.3/*" } ``` Paths have the `/sys` prefix stripped. + +## Capabilities + +`mctpd` requires the `CAP_NET_BIND_SERVICE` and `CAP_NET_ADMIN` capabilites to run. +If the service is not running as root these should be added to the `AmbientCapabilities` +setting in the service's config. +If the target platform has the `libcap` library available, +the `mctpd` process will drop `CAP_NET_BIND_SERVICE` when it is no longer needed. + +Because `mctpd` may be exposed to potentially hostile inputs, +it shouldn't be given more capabilities than necessary. \ No newline at end of file diff --git a/meson.build b/meson.build index 622978da..6fa9afd6 100644 --- a/meson.build +++ b/meson.build @@ -16,6 +16,8 @@ add_project_arguments('-Wno-unused-parameter', language : 'c') libsystemd = dependency('libsystemd', version: '>=247', required: false) +libcap = dependency('libcap', required: false) + conf = configuration_data() conf.set10('HAVE_LINUX_MCTP_H', cc.has_header('linux/mctp.h'), @@ -37,6 +39,10 @@ conf.set10('MCTPD_WRITABLE_CONNECTIVITY', get_option('unsafe-writable-connectivity'), description: 'Allow writes to the Connectivity member of the au.com.codeconstruct.MCTP.Endpoint1 interface on endpoint objects') +conf.set10('HAVE_LIBCAP', + libcap.found(), + description: 'Require mctpd to minimise its capabilities') + conf.set_quoted('MCTPD_CONF_FILE_DEFAULT', join_paths(get_option('prefix'), get_option('sysconfdir'), 'mctpd.conf'), description: 'Default configuration file path', @@ -89,7 +95,7 @@ if libsystemd.found() sources: [ 'src/mctpd.c', ] + netlink_sources + util_sources + ops_sources, - dependencies: [libsystemd, toml_dep], + dependencies: [libsystemd, toml_dep, libcap], install: true, install_dir: get_option('sbindir'), c_args: ['-DOPS_SD_EVENT=1'], @@ -100,7 +106,7 @@ if libsystemd.found() 'src/mctpd.c', ] + test_ops_sources + netlink_sources + util_sources, include_directories: include_directories('src'), - dependencies: [libsystemd, toml_dep], + dependencies: [libsystemd, toml_dep, libcap], c_args: ['-DOPS_SD_EVENT=1'], ) endif diff --git a/src/mctpd.c b/src/mctpd.c index c9a41dbf..79254f9a 100644 --- a/src/mctpd.c +++ b/src/mctpd.c @@ -9,6 +9,10 @@ #define _GNU_SOURCE #include "config.h" +#if HAVE_LIBCAP +#include +#endif + #include #include #include @@ -6342,6 +6346,50 @@ static int endpoint_allocate_eids(struct peer *peer) return 0; } +#if HAVE_LIBCAP + +static int drop_bind_cap(void) +{ + cap_flag_t flags[] = { CAP_EFFECTIVE, CAP_INHERITABLE, CAP_PERMITTED }; + cap_value_t bind = CAP_NET_BIND_SERVICE; + cap_t cap; + size_t i; + int rc; + + cap = cap_get_proc(); + if (!cap) + return -errno; + + for (i = 0; i < ARRAY_SIZE(flags); i++) { + rc = cap_set_flag(cap, flags[i], 1, &bind, CAP_CLEAR); + if (rc < 0) { + rc = -errno; + cap_free(cap); + return rc; + } + } + + rc = cap_set_proc(cap); + if (rc < 0) { + rc = -errno; + cap_free(cap); + return rc; + } + + rc = cap_free(cap); + if (rc < 0) + return -errno; + + return 0; +} +#else + +static int drop_bind_cap(void) +{ + return 0; +} +#endif + int main(int argc, char **argv) { struct ctx ctxi = { 0 }, *ctx = &ctxi; @@ -6397,6 +6445,13 @@ int main(int argc, char **argv) return 1; } + rc = drop_bind_cap(); + if (rc < 0) { + warnx("Error dropping capabilities, returned %s %d", + strerror(-rc), rc); + return 1; + } + // All setup must be complete by here, we might immediately // get requests from waiting clients. rc = request_dbus(ctx);