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 1103de40c15a36a6c02a4e119b80b47f34e66246 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' capabilities to be modified. This depends on the libcap library on the target device. If libcap is missing, set_cap() will be a stub, and mctpd will emit a warning when it starts. Because mctpd is an important process and may recieve hostile imputs, this implementation has followed the principle of least privilege. All capabilities except CAP_NET_BIND_SERVICE and CAP_NET_ADMIN are dropped when the process starts. Each capability is only effective while it's needed, and CAP_NET_BIND_SERVICE is dropped entirely after it is used. This means that the daemon cannot be given additinal capabilities if they are needed for reading config files or other conveniences. Any additional privileges will immediately be dropped. This is intended to prevent mctpd accidentally performing dangerous operations due to unexpected privilege. libcap has been added as a possible dependency to the meson build and the 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 | 12 ++++++++++++ meson.build | 10 ++++++++-- src/mctpd.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/docs/mctpd.md b/docs/mctpd.md index 236e1a54..5f5f8f11 100644 --- a/docs/mctpd.md +++ b/docs/mctpd.md @@ -503,3 +503,15 @@ 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 all other capabilities, +only keeping the necessary capabilities in the effective set as needed. + +Because `mctpd` is must be a privileged process that is exposed to potentially hostile inputs, +it shouldn't be given more capabilities than necessary. +`mctpd` will emit a warning while starting on target platforms that do not have `libcap`. \ No newline at end of file diff --git a/meson.build b/meson.build index 622978da..14643cf7 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('MCTPD_MANAGE_CAPABILITIES', + libcap.found(), + description: 'Require mctpd to minimise its capabilities (emits warning on start if false)') + 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..f2bb1111 100644 --- a/src/mctpd.c +++ b/src/mctpd.c @@ -9,6 +9,10 @@ #define _GNU_SOURCE #include "config.h" +#if MCTPD_MANAGE_CAPABILITIES +#include +#endif + #include #include #include @@ -6342,6 +6346,39 @@ static int endpoint_allocate_eids(struct peer *peer) return 0; } +int set_cap(char *str) +{ +#if MCTPD_MANAGE_CAPABILITIES + cap_t cap = cap_from_text(str); + int rc; + + if (!cap) { + rc = -errno; + warnx("Failed allocating capability state: %s, %s %d", str, + strerror(-rc), rc); + return rc; + } + + rc = cap_set_proc(cap); + if (rc < 0) { + rc = -errno; + warnx("Could not set capabilites: %s, %s %d", str, + strerror(-rc), rc); + return rc; + } + + rc = cap_free(cap); + if (rc < 0) { + rc = -errno; + warnx("Failed freeing capability state"); + return rc; + } + return 0; +#else + return 0; +#endif +} + int main(int argc, char **argv) { struct ctx ctxi = { 0 }, *ctx = &ctxi; @@ -6349,6 +6386,14 @@ int main(int argc, char **argv) setlinebuf(stdout); +#if !MCTPD_MANAGE_CAPABILITIES + warnx("mctpd has been compiled without capability management and will not drop capabilities"); +#endif + + rc = set_cap("CAP_NET_BIND_SERVICE=p CAP_NET_ADMIN=p"); + if (rc < 0) + return 1; + setup_config_defaults(ctx); setup_ctrl_cmd_defaults(ctx); @@ -6390,6 +6435,10 @@ int main(int argc, char **argv) if (rc < 0) return 1; + rc = set_cap("CAP_NET_BIND_SERVICE=pe CAP_NET_ADMIN=p"); + if (rc < 0) + return 1; + // TODO add net argument? rc = listen_control_msg(ctx, MCTP_NET_ANY); if (rc < 0) { @@ -6397,6 +6446,10 @@ int main(int argc, char **argv) return 1; } + rc = set_cap("CAP_NET_ADMIN=pe"); + if (rc < 0) + return 1; + // All setup must be complete by here, we might immediately // get requests from waiting clients. rc = request_dbus(ctx);