Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion conf/mctpd-dbus.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,33 @@
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy context="default">
<deny send_destination="au.com.codeconstruct.MCTP1">

<allow receive_sender="au.com.codeconstruct.MCTP1"/>

<allow send_destination="au.com.codeconstruct.MCTP1"
send_interface="org.freedesktop.DBus.Propeties"
send_member="Get"/>

<allow send_destination="au.com.codeconstruct.MCTP1"
send_interface="org.freedesktop.DBus.Propeties"
send_member="GetAll"/>

<allow send_destination="au.com.codeconstruct.MCTP1"
send_interface="org.freedesktop.DBus.Introspectable"/>

<allow send_destination="au.com.codeconstruct.MCTP1"
send_interface="org.freedesktop.DBus.ObjectManager"/>
</policy>
<policy user="mctpd">
<allow own="au.com.codeconstruct.MCTP1"/>
</policy>
<policy group="mctp-admin">
<allow send_destination="au.com.codeconstruct.MCTP1"/>
</policy>
<policy user="root">
<allow own="au.com.codeconstruct.MCTP1"/>
<allow send_destination="au.com.codeconstruct.MCTP1"/>
<allow receive_sender="au.com.codeconstruct.MCTP1"/>
</policy>
</busconfig>
2 changes: 2 additions & 0 deletions conf/mctpd.service
Original file line number Diff line number Diff line change
Expand Up @@ -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
11 changes: 11 additions & 0 deletions docs/mctpd.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
10 changes: 8 additions & 2 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand All @@ -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',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slight preference to name this according to the dependency it is representing (ie., HAVE_LIBCAP), rather than as a user-provided option - as these last two MCTPD_ conf parameters are doing.

@SafetyInObscurity SafetyInObscurity Jul 8, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of what the macro meant for the code's behaviour, rather than what conditions allowed it.

Figured some users might not want to include the functionality even if libcap was available.

I probably should have added it as a configurable option.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to make it a config option; if libcap is available we should use it.

Although it's possible to provide an option for things like this, it's not always desirable. In this case, there's a fairly obvious ideal choice, so we take that. If someone has a use-case for something else, we can incorporate that into a future change. Since we don't have details of that use case now though, no need to cater for it.

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',
Expand Down Expand Up @@ -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'],
Expand All @@ -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],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be nice to prevent the test-mctpd process from attempting to alter capabilities - its ops->bind() doesn't really do a bind(), as it's a mock implementation, and so it shouldn't have any capabilities in the first place. We could do the caps change as a separate op, but that seems pretty heavy-handed.

Any ideas to handle that?

[I am assuming that the absence of CAP_NET_BIND_SERVICE in the first place should not be an issue for the drop.]

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there should be any trouble from trying to set the capabilities to their current value.

If you really wanted to prevent it from trying to change capabilities during testing, there should be ways to have MCTPD_MANAGE_CAPABILITIES set to 0 for the test.

Is there anything specific you're worried about?

c_args: ['-DOPS_SD_EVENT=1'],
)
endif
Expand Down
55 changes: 55 additions & 0 deletions src/mctpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#define _GNU_SOURCE
#include "config.h"

#if MCTPD_MANAGE_CAPABILITIES
#include <sys/capability.h>
#endif

#include <assert.h>
#include <systemd/sd-bus-vtable.h>
#include <time.h>
Expand Down Expand Up @@ -6342,6 +6346,50 @@ static int endpoint_allocate_eids(struct peer *peer)
return 0;
}

#if MCTPD_MANAGE_CAPABILITIES

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;
Expand Down Expand Up @@ -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);
Expand Down
Loading