Commit Graph
86 Commits
Author SHA1 Message Date
Skye Soss 5cfe544a1a namespace-util: don't require privileges when there's nothing to enter
When namespace_enter_full() is called with namespaces that the current
process is already a part of, skip the setns() call on the user
namespace. This allows the function to succeed without CAP_SYS_ADMIN.

Follow-up for 211c3c9d41

(cherry picked from commit 27b1fd3a1e2e6c28bb09ec79cb3dc949a4ba416d)
2026-10-16 17:24:51 +01:00
Daan De Meyer cfd2f7c73e namespace-util: fail if we cannot become root when entering a userns
namespace_enter() tried to become root after joining a user namespace
but ignored failures, so if root wasn't mapped we'd just carry on with
our original UID/GID. Let's make that an error instead.

Dropping supplementary groups stays best effort, as setgroups() might
be denied (e.g. by the nsresourced BPF LSM) even when root is mapped.

Some callers do need to enter namespaces without root, so add
NAMESPACE_ENTER_KEEP_UID_GID_IF_ROOT_UNMAPPED for those. It's used by
systemd-analyze unit-shell and by the credential refresh in the user
manager. Everything else enters containers or namespaces we allocated
ourselves, where root is always mapped.
2026-09-22 13:48:24 +02:00
Yu Watanabe dfe19f1350 namespace-util: introduce network_namespace_is_init() 2026-07-18 05:29:59 +09:00
Michael Vogt bc4dfc24a5 basic: add assert() when doing pointer deref
Lennart reminded me in [1] that we need to add assert() in functions
that do pointer access. For the simple `*p` pointer dereferences
we even have an automatic coccinelle script that ensures that as
part of the automatic code checks.

However for deref in the `p->` style this is not supported right
now and adding it to coccinelle is hard because its too slow for
this kind of check. So I created a (slightly messy) tree-sitter
python script to see how many asserts we are currently missing.

This commit is the result of running it over the `src/basic`
dir and fixing the flagged issues. I plan to tidy it up and
add it to the checks too but this is orthogonal to this commit.

[1] https://github.com/systemd/systemd/pull/42360#discussion_r3426964562
2026-06-25 10:40:21 +02:00
Mike Yuan a53d9a5794 namespace-util: add missing half of the assertion in is_our_namespace() 2026-06-13 17:11:10 +02:00
Daan De Meyer 23f2204718 nsresourced: detect and clean up registry entries for dead user namespaces
The BPF kprobe that fires on user namespace destruction is the only thing
that triggers registry cleanup, so any time it doesn't run — ring buffer
overflow, kprobe missing, fdstore entry dropped outside our cleanup path
— a registry entry is left behind forever.

Stamp each registry entry with the kernel's unique namespace identifier
(NS_GET_ID, kernel ≥ 6.13) at allocation time. At manager startup, after
the existing fdstore→registry sweep, walk the registry and ask the kernel
to look each namespace up by id via open_by_handle_at() on nsfs; if the
lookup returns -ESTALE the namespace is gone and we release the entry.
Old entries written before this change carry no identifier and are left
alone.

Add a namespace_open_by_id() helper for the lookup. The kernel restricts
open_by_handle_at() on nsfs to processes in the initial user namespace,
collapsing both permission denials and dead namespaces onto -ESTALE; the
helper refuses early with -EHOSTDOWN outside the initial user namespace
so callers can tell the two apart.
2026-05-15 18:05:21 +00:00
Daan De Meyer 3c96d1429f namespace-util: Add logging to process_is_owned_by_uid() 2026-04-06 17:13:40 +02:00
Daan De Meyer 6119252f5a sd-bus: Don't fork unnecessarily to connect to container
Let's check if we're already in the right namespaces and call connect()
directly if that's the case. This can easily happen when the machine is
specified as .host or so.
2026-02-17 17:52:20 +01:00
Daan De Meyer 8d11835397 namespace-util: Do is_our_namespace() checks first in namespace_enter()
These checks may rely on /proc on older kernels which we could lose access
to by joining namespaces so let's do all the checks first and then join
namespaces.
2026-02-17 17:52:17 +01:00
Daan De Meyer 3791710f44 namespace-util: Add extra debug logging 2026-02-17 16:44:37 +01:00
Daan De Meyer 211c3c9d41 namespace-util: Merge namespace_enter_delegated() into namespace_enter()
There's no need to pass in a boolean to decide whether we use
namespace_enter_delegated() or not. Instead, we can just check if we
have CAP_SYS_ADMIN in our own user namespace. If we don't, then we have
to insist on a child user namespace being passed in and we have to enter
it first to get CAP_SYS_ADMIN as without CAP_SYS_ADMIN we wouldn't be able
to call setns() in the first place. If we do have CAP_SYS_ADMIN, we can
always enter the other namespaces first before entering the user namespace.

Additionally, we don't fail anymore if we can't reset the UID/GID since a
root user might not always be available in every user namespace we might
enter.
2026-02-16 10:27:02 +01:00
Mike Yuan 1a1c5ab758 namespace-util: introduce namespace_enter_delegated()
Typically when entering a namespace the userns is handled last,
because we assume our process is more privileged than the userns.
However, that assumption no longer holds for user managers, which
have no privilege over initial userns and all other namespaces
are actually owned by the userns unshared first (in executor).
Hence, let's add another flavor namespace_enter_delegated() to
accommodate that use case.
2026-02-10 21:54:11 +01:00
Daan De Meyer f3b81859c2 namespace-util: Fix logging in userns_enter_and_pin() 2026-02-05 14:09:03 +01:00
Daan De Meyer fbd276cb86 tree-wide: Use pidref_wait_for_terminate_and_check()
Let's use the pidref variants for waiting and
checking a process everywhere and get rid of the
non pidref variants.
2025-12-20 15:50:47 +01:00
Mike Yuan 29a823b3b6 process-util: add a flag for retaining dlopen()-ability
While blocking dlopen() in child generally makes sense, it does
also lead to misery - in the very case of extension/cred refreshing
it then enforces loading of libcryptsetup/libacl in pid1, which
are otherwise never used. Let's add a flag to opt out hence, but
still prohibit it in namespace_fork/enter().
2025-12-16 21:07:20 +01:00
Yu Watanabe 3fc2a44043 include: move trivial kernel header wrappers to src/include/override/ 2025-07-11 13:05:46 +09:00
Yu Watanabe 8d29f31bf6 namespace-util,nsresource: explicitly include sched.h
These source files uses symbols provided by sched.h, e.g.
setns(), unshare(), CLONE_NEWNS, and friends, but they do not explicitly
include sched.h. Currently, it is included indirectly via missing_syscall.h,
which is included by e.g. pidfd-util.h.
Let's explicitly include headers that provides symbols used in the code.

This is similar to 4f18ff2e29, but for sched.h.
2025-07-06 13:28:46 +02:00
Yu Watanabe 237bdd2771 basic: move missing_sched.h -> include/sched.h 2025-07-01 12:29:15 +09:00
Yu Watanabe 4f18ff2e29 tree-wide: include unistd.h where necessary
We use symbols provided by unistd.h without including it. E.g.
open(), close(), read(), write(), access(), symlink(), unlink(), rmdir(),
fsync(), syncfs(), lseek(), ftruncate(), fchown(), dup2(), pipe2(),
getuid(), getgid(), gettid(), getppid(), pipe2(), execv(), _exit(),
environ, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, F_OK, and their
friends and variants, so on.

Currently, unistd.h is indirectly included mainly in the following two paths:
- through missing_syscall.h, which is planned to covert to .c file.
- through signal.h -> bits/sigstksz.h, which is new since glibc-2.34.
  Note, signal.h is included by sd-eevent.h. So, many source files
  indirectly include unistd.h if newer glibc is used.

Currently, our baseline on glibc is 2.31. We need to support glibc older
than 2.34, but unfortunately, we do not have any CI environments with
such old glibc. CIFuzz uses glibc-2.31, but it builds only fuzzers, and
many files are even not compiled.
2025-06-30 09:19:15 +02:00
Daan De Meyer 0c15577abe basic + fundamental: Clean up includes
Split out of #37344.
2025-05-25 10:06:07 +02:00
Daan De Meyer 1aabcd7454 basic: Move userns_supported() to namespace-util.h 2025-05-25 10:00:54 +02:00
Mike Yuan 418082596c namespace-util: generalize namespace_type_supported() 2025-04-21 16:54:37 +02:00
Daan De Meyer 93a1f7921a basic: Stop including log.h in macro.h
Now that the necessary functions from log.h have been moved to macro.h,
we can stop including log.h in macro.h. This requires modifying source
files all over the tree to include log.h instead.
2025-04-18 14:19:15 +02:00
Michal Sekletar 8b5e3be88e basic/namespace-util: obtain uid and gid before unsharing user namespace
Getting user and group after unsharing user namespace is too late
because without any mapping, i.e. just after unshare(), we are mapped to
nobody.
2025-04-09 03:06:05 +09:00
Lennart Poettering 783b40bd73 namespace-util: introduce userns_acquire_self_root()
This is a simple helper for creating a userns that just maps the
callers user to UID 0 in the namespace. This can be acquired unpriv,
which makes it useful for various purposes, for example for the logic in
is_idmapping_supported(), hence port it over.

(is_idmapping_supported() used a different mapping before, with the
nobody users, but there's no real reason for that, and we'll use
userns_acquire_self_root() elsewhere soon, where the root mapping is
important).
2025-03-17 16:03:18 +01:00
Lennart Poettering 6431c34b8a namespace-util: make "setgroups" users property writable via userns_acquire()
Unprivileged namespaces are only allowed if the "setgroups" file is set
to "deny" for processes. And we need to write it before writing the
gidmap. Hence add a parameter for that.

Then, also patch all current users to actually enable this. The usecase
generally don't need it (because they don't care about unprivileged
userns), but it doesn't hurt to enable the concept anyway in all current
users (none of them actually runs complex userspace in them, but they
mostly use userns_acquire() for idmapped mounts and similar).

Let's anyway make this option explicit in the function call, to indicate
that the concept exists and is applied.
2025-03-17 16:03:18 +01:00
Yu Watanabe ec32732043 basic: introduce our own sys/mount.h implementation
To resolve conflict with sys/mount.h and linux/mount.h or linux/fs.h.

The conflict between sys/mount.h and linux/mount.h is resolved in
glibc-2.37 (774058d72942249f71d74e7f2b639f77184160a6), but our baseline
is still glibc-2.31. Also, even with the version or newer, still
sys/mount.h conflicts with linux/fs.h, which is included by
linux/btrfs.h.

This introduces our own implementation of sys/mount.h, that can be
simultaneously included with linux/mount.h and linux/fs.h. This also
imports linux/fs.h, linux/mount.h, and several other dependent headers.
The introduced sys/mount.h header itself may not be enough simple, but
by using the header, we can drop most of workarounds in other source files.
2025-03-04 02:24:49 +09:00
Yu Watanabe fe5a1afb67 tree-wide: use _cleanup_hashmap_free_ and friends 2025-01-19 03:48:15 +09:00
Lennart Poettering 6eeeef9f66 process-util: introduce new FORK_FREEZE flag for safe_fork()
Often we want to fork off a process that just hangs until we kill it,
let's add a simple flag to create one of this type, and use it at
various places.
2025-01-16 11:55:21 +01:00
Lennart Poettering 39706728e1 namespace-util: don't reset UID/GIDs in namespace_enter() unless we enter a userns
The reset of UID/GID only really makes sense if we enter a userns, hence
let#s restrict it to that.
2025-01-16 11:26:57 +01:00
Yu Watanabe 132a164d97 Follow-ups for recent namespace PRs (#35923) 2025-01-15 14:10:21 +09:00
Mike Yuan 95a5c658a3 namespace-util: use pidref_namespace_open_by_type() where appropriate
Several logs are dropped, since all callers log loudly already.
2025-01-11 16:06:38 +01:00
Mike Yuan a0e3dbdef5 uid-range: make uid_map_search_root() take UIDRangeUsernsMode 2025-01-11 15:53:15 +01:00
Mike Yuan 8c2c8235a6 namespace-util: introduce userns_enter_and_pin() helper
which generalizes forking a process into userns and freeze()

Addresses https://github.com/systemd/systemd/pull/35833/files#r1905508153
2025-01-11 15:53:14 +01:00
Mike Yuan c7704ecd04 namespace-util: group userns functions together 2025-01-11 15:53:14 +01:00
Lennart Poettering 0b8b13324e namespace-util: port namespace_get_leader() to PidRef 2025-01-10 14:22:49 +01:00
Lennart Poettering 2ade821859 namespace-util: add process_is_owned_by_uid() helper 2025-01-07 23:55:34 +01:00
Lennart Poettering 3d57b6692f namespace-util: add helper to get base UID from userns 2025-01-07 23:55:34 +01:00
Lennart Poettering 5b14690a39 namespace-util: slightly tweak proc_mounted() handling in namespace_is_init()
Let's not sloppily eat up errors here.
2025-01-07 23:51:28 +01:00
Lennart Poettering adcc805929 namespace-util: return recognizable error if namespace_open_by_type() fails because ns type not supported
This makes sure the the codepath that derives an nsfd from a pid works
the same for the pidfd case and the non-pidfd case: if we can verify
that /proc/ is mounted but the /proc/$PID/ns/ files are missing, we can
assume the ns type is not supported by the kernel. Hence return the same
ENOPKG error in this case as we already do in the pidfd ioctl based
codepath.
2025-01-07 23:51:28 +01:00
Mike Yuan 47f64104d1 process-util: port pidref_get_uid() and pidref_is_my_child() to pidfd helpers 2025-01-04 17:48:22 +01:00
Mike Yuan 92b8e5e72f namespace-util: introduce pidref_in_same_namespace() 2025-01-04 17:08:01 +01:00
Mike Yuan a33f691374 process-util: move namespace_get_leader() to namespace-util
This allows us to drop the hack for recursive includes.
2025-01-04 17:08:00 +01:00
Mike Yuan 4cad58788b namespace-util: hook pidref_namespace_open() up with pidfd_get_namespace()
Supersedes #35308
2025-01-04 17:08:00 +01:00
Mike Yuan 2f7cd6c70a namespace-util: correct assertion in namespace_is_init()
Follow-up for 18ead2b03d
2025-01-04 17:07:59 +01:00
Mike Yuan 07610cafcf namespace-util: modernize fd_is_namespace() and is_our_namespace()
- Make fd_is_namespace() take NamespaceType
- Drop support for kernel without NS_GET_NSTYPE (< 4.11)
- Port is_our_namespace() to namespace_open_by_type()
  (preparation for later commits, where the latter
  would go by pidfd if available, avoiding procfs)
2025-01-04 17:07:59 +01:00
Yu Watanabe 1775337a36 namespace-util: refuse remote pidref in pidref_namespace_open() 2025-01-04 17:07:59 +01:00
Zbigniew Jędrzejewski-Szmek 22996a3393 basic/namespace-util: fix double logging after fork failure
[   10.056930] (journald)[104]: Failed to fork off '(sd-mkuserns)': Invalid argument
[   10.063727] systemd[1]: systemd-modules-load.service: About to execute: /usr/lib/systemd/systemd-modules-load
[   10.071148] (journald)[104]: Failed to fork process (sd-mkuserns): Invalid argument

safe_fork_full() already logs at debug level, so the caller shouldn't.
2024-12-02 11:51:23 +01:00
Zbigniew Jędrzejewski-Szmek afb368951c pid1: assume user namespaces are unavailable if we get -EINVAL from clone()
As reported in https://github.com/systemd/systemd/issues/35400,
on riscv64, with Linux version 6.6.51-linux4microchip+fpga-2024.09, we get:

[   10.063727] systemd[1]: systemd-modules-load.service: About to execute: /usr/lib/systemd/systemd-modules-load
[   10.071148] (journald)[104]: Failed to fork process (sd-mkuserns): Invalid argument

Fixes https://github.com/systemd/systemd/issues/35400.

'r' is used to make the repeated checks shorter. Without that, the long variable
name is distracting.
2024-12-02 11:30:06 +01:00
Zbigniew Jędrzejewski-Szmek d293fade24 Check inode number to see if we are in init namespace (#35306)
This is a more comprehensive fix compared to #35273. Also adds a minimal
test only.

Based on Luca's #35273 but generalizes the code a bit.

In v258 we really should get rid of the old heuristics around userns and
cgroupns detection, but given we are late in the v257 cycle this keeps
them in.
2024-11-25 14:13:36 +01:00