mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-26 09:57:26 +03:00
Merge pull request #7530 from poettering/uid-gid-fixes
various uid/gid fixes
This commit is contained in:
commit
b1891d2aaf
10
TODO
10
TODO
@ -33,7 +33,8 @@ Features:
|
||||
systemd-journald writes to /var/log/journal, which could be useful when we
|
||||
doing disk usage calculations and so on.
|
||||
|
||||
* taint systemd if the overflowuid/overflowgid is not 65534
|
||||
* taint systemd if the overflowuid/overflowgid is not 65534, and if there are
|
||||
fewer than 65536 users assigned to the system.
|
||||
|
||||
* deprecate PermissionsStartOnly= and RootDirectoryStartOnly= in favour of the ExecStart= prefix chars
|
||||
|
||||
@ -77,16 +78,9 @@ Features:
|
||||
* beef up pam_systemd to take unit file settings such as cgroups properties as
|
||||
parameters
|
||||
|
||||
* export UID ranges nspawns's --private-user and DynamicUser= uses in
|
||||
the systemd.pc pkg-config file, the same way we already expose the system
|
||||
user boundary there
|
||||
|
||||
* a new "systemd-analyze security" tool outputting a checklist of security
|
||||
features a service does and does not implement
|
||||
|
||||
* Whenever we check a UID against the system UID range, also check for the
|
||||
dynamic UID range
|
||||
|
||||
* maybe hook of xfs/ext4 quotactl() with services? i.e. automatically manage
|
||||
the quota of a the user indicated in User= via unit file settings, like the
|
||||
other resource management concepts. Would mix nicely with DynamicUser=1. Or
|
||||
|
242
UIDS-GIDS.md
Normal file
242
UIDS-GIDS.md
Normal file
@ -0,0 +1,242 @@
|
||||
# Users, Groups, UIDs and GIDs on `systemd` systems
|
||||
|
||||
Here's a summary of the requirements `systemd` (and Linux) make on UID/GID
|
||||
assignments and their ranges.
|
||||
|
||||
Note that while in theory UIDs and GIDs are orthogonal concepts they really
|
||||
aren't IRL. With that in mind, when we discuss UIDs below it should be assumed
|
||||
that whatever we say about UIDs applies to GIDs in mostly the same way, and all
|
||||
the special assignments and ranges for UIDs always have mostly the same
|
||||
validity for GIDs too.
|
||||
|
||||
## Special Linux UIDs
|
||||
|
||||
In theory, the range of the C type `uid_t` is 32bit wide on Linux,
|
||||
i.e. 0…4294967295. However, four UIDs are special on Linux:
|
||||
|
||||
1. 0 → The `root` super-user
|
||||
|
||||
2. 65534 → The `nobody` UID, also called the "overflow" UID or similar. It's
|
||||
where various subsystems map unmappable users to, for example NFS or user
|
||||
namespacing. (The latter can be changed with a sysctl during runtime, but
|
||||
that's not supported on `systemd`. If you do change it you void your
|
||||
warranty.) Because Fedora is a bit confused the `nobody` user is called
|
||||
`nfsnobody` there (and they have a different `nobody` user at UID 99). I
|
||||
hope this will be corrected eventually though. (Also, some distributions
|
||||
call the `nobody` group `nogroup`. I wish they didn't.)
|
||||
|
||||
3. 4294967295, aka "32bit `(uid_t) -1`" → This UID is not a valid user ID, as
|
||||
setresuid(), chown() and friends treat -1 as a special request to not change
|
||||
the UID of the process/file. This UID is hence not available for assignment
|
||||
to users in the user database.
|
||||
|
||||
4. 65535, aka "16bit `(uid_t) -1`" → Once upon a time `uid_t` used to be 16bit, and
|
||||
programs compiled for that would hence assume that `(uid_t) -1` is 65535. This
|
||||
UID is hence not usable either.
|
||||
|
||||
The `nss-systemd` glibc NSS module will synthesize user database records for
|
||||
the UIDs 0 and 65534 if the system user database doesn't list them. This means
|
||||
that any system where this module is enabled works to some minimal level
|
||||
without `/etc/passwd`.
|
||||
|
||||
## Special Distribution UID ranges
|
||||
|
||||
Distributions generally split the available UID range in two:
|
||||
|
||||
1. 1…999 → System users. These are users that do not map to actual "human"
|
||||
users, but are used as security identities for system daemons, to implement
|
||||
privilege separation and run system daemons with minimal privileges.
|
||||
|
||||
2. 1000…65533 and 65536…4294967294 → Everything else, i.e. regular (human) users.
|
||||
|
||||
Note that most distributions allow changing the boundary between system and
|
||||
regular users, even during runtime as user configuration. Moreover, some older
|
||||
systems placed the boundary at 499/500, or even 99/100. In `systemd`, the
|
||||
boundary is configurable only during compilation time, as this should be a
|
||||
decision for distribution builders, not for users. Moreover, we strongly
|
||||
discourage downstreams to change the boundary from the upstream default of
|
||||
999/1000.
|
||||
|
||||
Also note that programs such as `adduser` tend to allocate from a subset of the
|
||||
available regular user range only, usually 1000..60000. And it's also usually
|
||||
user-configurable, too.
|
||||
|
||||
Note that systemd requires that system users and groups are resolvable without
|
||||
networking available — a requirement that is not made for regular users. This
|
||||
means regular users may be stored in remote LDAP or NIS databases, but system
|
||||
users may not (except when there's a consistent local cache kept, that is
|
||||
available during earliest boot, including in the initial RAM disk).
|
||||
|
||||
## Special `systemd` GIDs
|
||||
|
||||
`systemd` defines no special UIDs beyond what Linux already defines (see
|
||||
above). However, it does define some special group/GID assignments, which are
|
||||
primarily used for `systemd-udevd`'s device management. The precise list of the
|
||||
currently defined groups is found in this `sysusers.d` snippet:
|
||||
[basic.conf](https://raw.githubusercontent.com/systemd/systemd/master/sysusers.d/basic.conf.in)
|
||||
|
||||
It's strongly recommended that downstream distributions include these groups in
|
||||
their default group databases.
|
||||
|
||||
Note that the actual GID numbers assigned to these groups do not have to be
|
||||
constant beyond a specific system. There's one exception however: the `tty`
|
||||
group must have the GID 5. That's because it must be encoded in the `devpts`
|
||||
mount parameters during earliest boot, at a time where NSS lookups are not
|
||||
possible. (Note that the actual GID can be changed during `systemd` build time,
|
||||
but downstreams are strongly advised against doing that.)
|
||||
|
||||
## Special `systemd` UID ranges
|
||||
|
||||
`systemd` defines a number of special UID ranges:
|
||||
|
||||
1. 61184…65519 → UIDs for dynamic users are allocated from this range (see the
|
||||
`DynamicUser=` documentation in
|
||||
[`systemd.exec(5)`](https://www.freedesktop.org/software/systemd/man/systemd.exec.html)). This
|
||||
range has been chosen so that it is below the 16bit boundary (i.e. below
|
||||
65535), in order to provide compatibility with container environments that
|
||||
assign a 64K range of UIDs to containers using user namespacing. This range
|
||||
is above the 60000 boundary, so that its allocations are unlikely to be
|
||||
affected by `adduser` allocations (see above). And we leave some room
|
||||
upwards for other purposes. (And if you wonder why precisely these numbers:
|
||||
if you write them in hexadecimal, they might make more sense: 0xEF00 and
|
||||
0xFFEF). The `nss-systemd` module will synthesize user records implicitly
|
||||
for all currently allocated dynamic users from this range. Thus, NSS-based
|
||||
user record resolving works correctly without those users being in
|
||||
`/etc/passwd`.
|
||||
|
||||
2. 524288…1879048191 → UID range for `systemd-nspawn`'s automatic allocation of
|
||||
per-container UID ranges. When the `--private-users=pick` switch is used (or
|
||||
`-U`) then it will automatically find a so far unused 16bit subrange of this
|
||||
range and assign it to the container. The range is picked so that the upper
|
||||
16bit of the 32bit UIDs are constant for all users of the container, while
|
||||
the lower 16bit directly encode the 65536 UIDs assigned to the
|
||||
container. This mode of allocation means that the upper 16bit of any UID
|
||||
assigned to a container are kind of a "container ID", while the lower 16bit
|
||||
directly expose the container's own UID numbers. If you wonder why precisely
|
||||
these numbers, consider them in hexadecimal: 0x00080000…0x6FFFFFFF. This
|
||||
range is above the 16bit boundary. Moreover it's below the 31bit boundary,
|
||||
as some broken code (specifically: the kernel's `devpts` file system)
|
||||
erroneously considers UIDs signed integers, and hence can't deal with values
|
||||
above 2^31. The `nss-mymachines` glibc NSS module will synthesize user
|
||||
database records for all UIDs assigned to a running container from this
|
||||
range.
|
||||
|
||||
Note for both allocation ranges: when an UID allocation takes place NSS is
|
||||
checked for collisions first, and a different UID is picked if an entry is
|
||||
found. Thus, the user database is used as synchronization mechanism to ensure
|
||||
exclusive ownership of UIDs and UID ranges. To ensure compatibility with other
|
||||
subsystems allocating from the same ranges it is hence essential that they
|
||||
ensure that whatever they pick shows up in the user/group databases, either by
|
||||
providing an NSS module, or by adding entries directly to `/etc/passwd` and
|
||||
`/etc/group`. For performance reasons, do note that `systemd-nspawn` will only
|
||||
do an NSS check for the first UID of the range it allocates, not all 65536 of
|
||||
them. Also note that while the allocation logic is operating, the glibc
|
||||
`lckpwdf()` user database lock is taken, in order to make this logic race-free.
|
||||
|
||||
## Figuring out the system's UID boundaries
|
||||
|
||||
The most important boundaries of the local system may be queried with
|
||||
`pkg-config`:
|
||||
|
||||
```
|
||||
$ pkg-config --variable=systemuidmax systemd
|
||||
999
|
||||
$ pkg-config --variable=dynamicuidmin systemd
|
||||
61184
|
||||
$ pkg-config --variable=dynamicuidmax systemd
|
||||
65519
|
||||
$ pkg-config --variable=containeruidbasemin systemd
|
||||
524288
|
||||
$ pkg-config --variable=containeruidbasemax systemd
|
||||
1878982656
|
||||
```
|
||||
|
||||
(Note that the latter encodes the maximum UID *base* `systemd-nspawn` might
|
||||
pick — given that 64K UIDs are assigned to each container according to this
|
||||
allocation logic, the maximum UID used for this range is hence
|
||||
1878982656+65535=1879048191.)
|
||||
|
||||
Note that systemd does not make any of these values runtime-configurable. All
|
||||
these boundaries are chosen during build time. That said, the system UID/GID
|
||||
boundary is traditionally configured in /etc/login.defs, though systemd won't
|
||||
look there during runtime.
|
||||
|
||||
## Considerations for container managers
|
||||
|
||||
If you hack on a container manager, and wonder how and how many UIDs best to
|
||||
assign to your containers, here are a few recommendations:
|
||||
|
||||
1. Definitely, don't assign less than 65536 UIDs/GIDs. After all the `nobody`
|
||||
user has magic properties, and hence should be available in your container, and
|
||||
given that it's assigned the UID 65534, you should really cover the full 16bit
|
||||
range in your container. Note that systemd will — as mentioned — synthesize
|
||||
user records for the `nobody` user, and assumes its availability in various
|
||||
other parts of its codebase, too, hence assigning fewer users means you lose
|
||||
compatibility with running systemd code inside your container. And most likely
|
||||
other packages make similar restrictions.
|
||||
|
||||
2. While it's fine to assign more than 65536 UIDs/GIDs to a container, there's
|
||||
most likely not much value in doing so, as Linux distributions won't use the
|
||||
higher ranges by default (as mentioned neither `adduser` nor `systemd`'s
|
||||
dynamic user concept allocate from above the 16bit range). Unless you actively
|
||||
care for nested containers, it's hence probably a good idea to allocate exactly
|
||||
65536 UIDs per container, and neither less nor more. A pretty side-effect is
|
||||
that by doing so, you expose the same number of UIDs per container as Linux 2.2
|
||||
supported for the whole system, back in the days.
|
||||
|
||||
3. Consider allocating UID ranges for containers so that the first UID you
|
||||
assign has the lower 16bits all set to zero. That way, the upper 16bits become
|
||||
a container ID of some kind, while the lower 16bits directly encode the
|
||||
internal container UID. This is the way `systemd-nspawn` allocates UID ranges
|
||||
(see above). Following this allocation logic ensures best compability with
|
||||
`systemd-nspawn` and all other container managers following the scheme, as it
|
||||
is sufficient then to check NSS for the first UID you pick regarding conflicts,
|
||||
as that's what they do, too. Moreover, it makes `chown()`ing container file
|
||||
system trees nicely robust to interruptions: as the external UID encodes the
|
||||
internal UID in a fixed way, it's very easy to adjust the container's base UID
|
||||
without the need to know the original base UID: to change the container base,
|
||||
just mask away the upper 16bit, and insert the upper 16bit of the new container
|
||||
base instead. Here are the easy conversions to derive the internal UID, the
|
||||
external UID, and the container base UID from each other:
|
||||
|
||||
```
|
||||
INTERNAL_UID = EXTERNAL_UID & 0x0000FFFF
|
||||
CONTAINER_BASE_UID = EXTERNAL_UID & 0xFFFF0000
|
||||
EXTERNAL_UID = INTERNAL_UID | CONTAINER_BASE_UID
|
||||
```
|
||||
|
||||
4. When picking a UID range for containers, make sure to check NSS first, with
|
||||
a simple `getpwuid()` call: if there's already a user record for the first UID
|
||||
you want to pick, then it's already in use: pick a different one. Wrap that
|
||||
call in a `lckpwdf()` + `ulckpwdf()` pair, to make allocation
|
||||
race-free. Provide an NSS module that makes all UIDs you end up taking show up
|
||||
in the user database, and make sure that the NSS module returns up-to-date
|
||||
information before you release the lock, so that other system components can
|
||||
safely use the NSS user database as allocation check, too. Note that if you
|
||||
follow this scheme no changes to `/etc/passwd` need to be made, thus minimizing
|
||||
the artifacts the container manager persistently leaves in the system.
|
||||
|
||||
## Summary
|
||||
|
||||
| UID/GID | Purpose | Defined By | Listed in |
|
||||
|-----------------------|-----------------------|---------------|-------------------------------|
|
||||
| 0 | `root` user | Linux | `/etc/passwd` + `nss-systemd` |
|
||||
| 1…4 | System users | Distributions | `/etc/passwd` |
|
||||
| 5 | `tty` group | `systemd` | `/etc/passwd` |
|
||||
| 6…999 | System users | Distributions | `/etc/passwd` |
|
||||
| 1000…60000 | Regular users | Distributions | `/etc/passwd` + LDAP/NIS/… |
|
||||
| 60001…61183 | Unused | | |
|
||||
| 61184…65519 | Dynamic service users | `systemd` | `nss-systemd` |
|
||||
| 65520…65533 | Unused | | |
|
||||
| 65534 | `nobody` user | Linux | `/etc/passwd` + `nss-systemd` |
|
||||
| 65535 | 16bit `(uid_t) -1` | Linux | |
|
||||
| 65536…524287 | Unused | | |
|
||||
| 524288…1879048191 | Container UID ranges | `systemd` | `nss-mymachines` |
|
||||
| 1879048192…4294967294 | Unused | | |
|
||||
| 4294967295 | 32bit `(uid_t) -1` | Linux | |
|
||||
|
||||
Note that "Unused" in the table above doesn't meant that these ranges are
|
||||
really unused. It just means that these ranges have no well-established
|
||||
pre-defined purposes between Linux, generic low-level distributions and
|
||||
`systemd`. There might very well be other packages that allocate from these
|
||||
ranges.
|
69
meson.build
69
meson.build
@ -614,9 +614,6 @@ conf.set('SYSTEM_UID_MAX', system_uid_max)
|
||||
substs.set('systemuidmax', system_uid_max)
|
||||
message('maximum system UID is @0@'.format(system_uid_max))
|
||||
|
||||
conf.set_quoted('NOBODY_USER_NAME', get_option('nobody-user'))
|
||||
conf.set_quoted('NOBODY_GROUP_NAME', get_option('nobody-group'))
|
||||
|
||||
system_gid_max = get_option('system-gid-max')
|
||||
if system_gid_max == ''
|
||||
system_gid_max = run_command(
|
||||
@ -629,6 +626,64 @@ conf.set('SYSTEM_GID_MAX', system_gid_max)
|
||||
substs.set('systemgidmax', system_gid_max)
|
||||
message('maximum system GID is @0@'.format(system_gid_max))
|
||||
|
||||
dynamic_uid_min = get_option('dynamic-uid-min').to_int()
|
||||
dynamic_uid_max = get_option('dynamic-uid-max').to_int()
|
||||
conf.set('DYNAMIC_UID_MIN', dynamic_uid_min)
|
||||
conf.set('DYNAMIC_UID_MAX', dynamic_uid_max)
|
||||
substs.set('dynamicuidmin', dynamic_uid_min)
|
||||
substs.set('dynamicuidmax', dynamic_uid_max)
|
||||
|
||||
container_uid_base_min = get_option('container-uid-base-min').to_int()
|
||||
container_uid_base_max = get_option('container-uid-base-max').to_int()
|
||||
conf.set('CONTAINER_UID_BASE_MIN', container_uid_base_min)
|
||||
conf.set('CONTAINER_UID_BASE_MAX', container_uid_base_max)
|
||||
substs.set('containeruidbasemin', container_uid_base_min)
|
||||
substs.set('containeruidbasemax', container_uid_base_max)
|
||||
|
||||
nobody_user = get_option('nobody-user')
|
||||
nobody_group = get_option('nobody-group')
|
||||
|
||||
getent_result = run_command('getent', 'passwd', '65534')
|
||||
if getent_result.returncode() == 0
|
||||
name = getent_result.stdout().split(':')[0]
|
||||
if name != nobody_user
|
||||
message('WARNING:\n' +
|
||||
' The local user with the UID 65534 does not match the configured user name "@0@" of the nobody user (its name is @1@).\n'.format(nobody_user, name) +
|
||||
' Your build will result in an user table setup that is incompatible with the local system.')
|
||||
endif
|
||||
endif
|
||||
id_result = run_command('id', '-u', nobody_user)
|
||||
if id_result.returncode() == 0
|
||||
id = id_result.stdout().to_int()
|
||||
if id != 65534
|
||||
message('WARNING:\n' +
|
||||
' The local user with the configured user name "@0@" of the nobody user does not have UID 65534 (it has @1@).\n'.format(nobody_user, id) +
|
||||
' Your build will result in an user table setup that is incompatible with the local system.')
|
||||
endif
|
||||
endif
|
||||
|
||||
getent_result = run_command('getent', 'group', '65534')
|
||||
if getent_result.returncode() == 0
|
||||
name = getent_result.stdout().split(':')[0]
|
||||
if name != nobody_group
|
||||
message('WARNING:\n' +
|
||||
' The local group with the GID 65534 does not match the configured group name "@0@" of the nobody group (its name is @1@).\n'.format(nobody_group, name) +
|
||||
' Your build will result in an group table setup that is incompatible with the local system.')
|
||||
endif
|
||||
endif
|
||||
id_result = run_command('id', '-g', nobody_group)
|
||||
if id_result.returncode() == 0
|
||||
id = id_result.stdout().to_int()
|
||||
if id != 65534
|
||||
message('WARNING:\n' +
|
||||
' The local group with the configured group name "@0@" of the nobody group does not have UID 65534 (it has @1@).\n'.format(nobody_group, id) +
|
||||
' Your build will result in an group table setup that is incompatible with the local system.')
|
||||
endif
|
||||
endif
|
||||
|
||||
conf.set_quoted('NOBODY_USER_NAME', nobody_user)
|
||||
conf.set_quoted('NOBODY_GROUP_NAME', nobody_group)
|
||||
|
||||
tty_gid = get_option('tty-gid')
|
||||
conf.set('TTY_GID', tty_gid)
|
||||
substs.set('TTY_GID', tty_gid)
|
||||
@ -2506,12 +2561,16 @@ status = [
|
||||
'users GID: @0@'.format(users_gid),
|
||||
'maximum system UID: @0@'.format(system_uid_max),
|
||||
'maximum system GID: @0@'.format(system_gid_max),
|
||||
'minimum dynamic UID: @0@'.format(dynamic_uid_min),
|
||||
'maximum dynamic UID: @0@'.format(dynamic_uid_max),
|
||||
'minimum container UID base: @0@'.format(container_uid_base_min),
|
||||
'maximum container UID base: @0@'.format(container_uid_base_max),
|
||||
'/dev/kvm access mode: @0@'.format(get_option('dev-kvm-mode')),
|
||||
'render group access mode: @0@'.format(get_option('group-render-mode')),
|
||||
'certificate root directory: @0@'.format(get_option('certificate-root')),
|
||||
'support URL: @0@'.format(support_url),
|
||||
'nobody user name: @0@'.format(get_option('nobody-user')),
|
||||
'nobody group name: @0@'.format(get_option('nobody-group')),
|
||||
'nobody user name: @0@'.format(nobody_user),
|
||||
'nobody group name: @0@'.format(nobody_group),
|
||||
'fallback hostname: @0@'.format(get_option('fallback-hostname')),
|
||||
'symbolic gateway hostnames: @0@'.format(', '.join(gateway_hostnames)),
|
||||
|
||||
|
@ -147,6 +147,18 @@ option('system-uid-max', type : 'string',
|
||||
description : 'maximum system UID')
|
||||
option('system-gid-max', type : 'string',
|
||||
description : 'maximum system GID')
|
||||
option('dynamic-uid-min', type : 'string',
|
||||
description : 'minimum dynamic UID',
|
||||
value : '61184') # That's → 0x0000EF00 in hex
|
||||
option('dynamic-uid-max', type : 'string',
|
||||
description : 'maximum dynamic UID',
|
||||
value : '65519') # That's → 0x0000FFEF in hex
|
||||
option('container-uid-base-min', type : 'string',
|
||||
description : 'minimum container UID base',
|
||||
value : '524288') # That's → 0x00080000 in hex
|
||||
option('container-uid-base-max', type : 'string',
|
||||
description : 'maximum container UID base',
|
||||
value : '1878982656') # That's → 0x6FFF0000 in hex
|
||||
option('tty-gid', type : 'string',
|
||||
description : 'the numeric GID of the "tty" group',
|
||||
value : '5')
|
||||
|
36
mkosi.build
36
mkosi.build
@ -28,7 +28,41 @@ export LC_CTYPE=en_US.UTF-8
|
||||
|
||||
sysvinit_path=`realpath /etc/init.d`
|
||||
|
||||
[ -f "$BUILDDIR"/build.ninja ] || meson "$BUILDDIR" -D "sysvinit-path=$sysvinit_path" -D default-hierarchy=unified -D man=false
|
||||
nobody_user=`id -u -n 65534 2> /dev/null`
|
||||
if [ "$nobody_user" != "" ] ; then
|
||||
# Validate that we can translate forth and back
|
||||
if [ "`id -u $nobody_user`" != 65534 ] ; then
|
||||
nobody_user=""
|
||||
fi
|
||||
fi
|
||||
if [ "$nobody_user" = "" ] ; then
|
||||
if id -u nobody 2> /dev/null ; then
|
||||
# The "nobody" user is defined already for something else, pick the Fedora name
|
||||
nobody_user=nfsnobody
|
||||
else
|
||||
# The "nobody" user name is free, use it
|
||||
nobody_user=nobody
|
||||
fi
|
||||
fi
|
||||
|
||||
nobody_group=`id -g -n 65534 2> /dev/null`
|
||||
if [ "$nobody_group" != "" ] ; then
|
||||
# Validate that we can translate forth and back
|
||||
if [ "`id -g $nobody_group`" != 65534 ] ; then
|
||||
nobody_group=""
|
||||
fi
|
||||
fi
|
||||
if [ "$nobody_group" = "" ] ; then
|
||||
if id -u nobody 2> /dev/null ; then
|
||||
# The "nobody" group is defined already for something else, pick the Fedora name
|
||||
nobody_group=nfsnobody
|
||||
else
|
||||
# The "nobody" group name is free, use it
|
||||
nobody_group=nobody
|
||||
fi
|
||||
fi
|
||||
|
||||
[ -f "$BUILDDIR"/build.ninja ] || meson "$BUILDDIR" -D "sysvinit-path=$sysvinit_path" -D default-hierarchy=unified -D man=false -D "nobody-user=$nobody_user" -D "nobody-group=$nobody_group"
|
||||
ninja -C "$BUILDDIR" all
|
||||
[ "$WITH_TESTS" = 0 ] || ninja -C "$BUILDDIR" test || ( RET="$?" ; cat "$BUILDDIR"/meson-logs/testlog.txt ; exit "$RET" )
|
||||
ninja -C "$BUILDDIR" install
|
||||
|
@ -117,15 +117,14 @@ int get_user_creds(
|
||||
assert(username);
|
||||
assert(*username);
|
||||
|
||||
/* We enforce some special rules for uid=0: in order to avoid
|
||||
* NSS lookups for root we hardcode its data. */
|
||||
/* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode
|
||||
* their user record data. */
|
||||
|
||||
if (streq(*username, "root") || streq(*username, "0")) {
|
||||
if (STR_IN_SET(*username, "root", "0")) {
|
||||
*username = "root";
|
||||
|
||||
if (uid)
|
||||
*uid = 0;
|
||||
|
||||
if (gid)
|
||||
*gid = 0;
|
||||
|
||||
@ -138,6 +137,23 @@ int get_user_creds(
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) {
|
||||
*username = NOBODY_USER_NAME;
|
||||
|
||||
if (uid)
|
||||
*uid = UID_NOBODY;
|
||||
if (gid)
|
||||
*gid = GID_NOBODY;
|
||||
|
||||
if (home)
|
||||
*home = "/";
|
||||
|
||||
if (shell)
|
||||
*shell = "/sbin/nologin";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parse_uid(*username, &u) >= 0) {
|
||||
errno = 0;
|
||||
p = getpwuid(u);
|
||||
@ -218,7 +234,7 @@ int get_group_creds(const char **groupname, gid_t *gid) {
|
||||
/* We enforce some special rules for gid=0: in order to avoid
|
||||
* NSS lookups for root we hardcode its data. */
|
||||
|
||||
if (streq(*groupname, "root") || streq(*groupname, "0")) {
|
||||
if (STR_IN_SET(*groupname, "root", "0")) {
|
||||
*groupname = "root";
|
||||
|
||||
if (gid)
|
||||
@ -227,6 +243,15 @@ int get_group_creds(const char **groupname, gid_t *gid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) {
|
||||
*groupname = NOBODY_GROUP_NAME;
|
||||
|
||||
if (gid)
|
||||
*gid = GID_NOBODY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parse_gid(*groupname, &id) >= 0) {
|
||||
errno = 0;
|
||||
g = getgrgid(id);
|
||||
@ -258,6 +283,8 @@ char* uid_to_name(uid_t uid) {
|
||||
/* Shortcut things to avoid NSS lookups */
|
||||
if (uid == 0)
|
||||
return strdup("root");
|
||||
if (uid == UID_NOBODY)
|
||||
return strdup(NOBODY_USER_NAME);
|
||||
|
||||
if (uid_is_valid(uid)) {
|
||||
long bufsize;
|
||||
@ -296,6 +323,8 @@ char* gid_to_name(gid_t gid) {
|
||||
|
||||
if (gid == 0)
|
||||
return strdup("root");
|
||||
if (gid == GID_NOBODY)
|
||||
return strdup(NOBODY_GROUP_NAME);
|
||||
|
||||
if (gid_is_valid(gid)) {
|
||||
long bufsize;
|
||||
@ -387,7 +416,7 @@ int get_home_dir(char **_h) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Hardcode home directory for root to avoid NSS */
|
||||
/* Hardcode home directory for root and nobody to avoid NSS */
|
||||
u = getuid();
|
||||
if (u == 0) {
|
||||
h = strdup("/root");
|
||||
@ -397,6 +426,14 @@ int get_home_dir(char **_h) {
|
||||
*_h = h;
|
||||
return 0;
|
||||
}
|
||||
if (u == UID_NOBODY) {
|
||||
h = strdup("/");
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
*_h = h;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check the database... */
|
||||
errno = 0;
|
||||
@ -434,7 +471,7 @@ int get_shell(char **_s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Hardcode home directory for root to avoid NSS */
|
||||
/* Hardcode shell for root and nobody to avoid NSS */
|
||||
u = getuid();
|
||||
if (u == 0) {
|
||||
s = strdup("/bin/sh");
|
||||
@ -444,6 +481,14 @@ int get_shell(char **_s) {
|
||||
*_s = s;
|
||||
return 0;
|
||||
}
|
||||
if (u == UID_NOBODY) {
|
||||
s = strdup("/sbin/nologin");
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
*_s = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check the database... */
|
||||
errno = 0;
|
||||
|
@ -60,17 +60,25 @@ int take_etc_passwd_lock(const char *root);
|
||||
#define UID_INVALID ((uid_t) -1)
|
||||
#define GID_INVALID ((gid_t) -1)
|
||||
|
||||
/* Let's pick a UIDs within the 16bit range, so that we are compatible with containers using 16bit
|
||||
* user namespacing. At least on Fedora normal users are allocated until UID 60000, hence do not
|
||||
* allocate from below this. Also stay away from the upper end of the range as that is often used
|
||||
* for overflow/nobody users. */
|
||||
#define DYNAMIC_UID_MIN ((uid_t) UINT32_C(0x0000EF00))
|
||||
#define DYNAMIC_UID_MAX ((uid_t) UINT32_C(0x0000FFEF))
|
||||
#define UID_NOBODY ((uid_t) 65534U)
|
||||
#define GID_NOBODY ((gid_t) 65534U)
|
||||
|
||||
static inline bool uid_is_dynamic(uid_t uid) {
|
||||
return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX;
|
||||
}
|
||||
|
||||
static inline bool gid_is_dynamic(gid_t gid) {
|
||||
return uid_is_dynamic((uid_t) gid);
|
||||
}
|
||||
|
||||
static inline bool uid_is_system(uid_t uid) {
|
||||
return uid <= SYSTEM_UID_MAX;
|
||||
}
|
||||
|
||||
static inline bool gid_is_system(gid_t gid) {
|
||||
return gid <= SYSTEM_GID_MAX;
|
||||
}
|
||||
|
||||
/* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer
|
||||
* NULL is special */
|
||||
#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
|
||||
|
@ -29,6 +29,10 @@ modulesloaddir=@modulesloaddir@
|
||||
catalogdir=@catalogdir@
|
||||
systemuidmax=@systemuidmax@
|
||||
systemgidmax=@systemgidmax@
|
||||
dynamicuidmin=@dynamicuidmin@
|
||||
dynamicuidmax=@dynamicuidmax@
|
||||
containeruidbasemin=@containeruidbasemin@
|
||||
containeruidbasemax=@containeruidbasemax@
|
||||
|
||||
Name: systemd
|
||||
Description: systemd System and Service Manager
|
||||
|
@ -165,7 +165,7 @@ static int fix_acl(int fd, uid_t uid) {
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
if (uid <= SYSTEM_UID_MAX)
|
||||
if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY)
|
||||
return 0;
|
||||
|
||||
/* Make sure normal users can read (but not write or delete)
|
||||
|
@ -248,7 +248,7 @@ static void server_add_acls(JournalFile *f, uid_t uid) {
|
||||
assert(f);
|
||||
|
||||
#if HAVE_ACL
|
||||
if (uid <= SYSTEM_UID_MAX)
|
||||
if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY)
|
||||
return;
|
||||
|
||||
r = add_acls_for_user(f->fd, uid);
|
||||
@ -406,7 +406,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
|
||||
if (s->runtime_journal)
|
||||
return s->runtime_journal;
|
||||
|
||||
if (uid <= SYSTEM_UID_MAX || uid_is_dynamic(uid))
|
||||
if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY)
|
||||
return s->system_journal;
|
||||
|
||||
r = sd_id128_get_machine(&machine);
|
||||
|
@ -617,7 +617,7 @@ int user_finalize(User *u) {
|
||||
* cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because
|
||||
* a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up,
|
||||
* and do it only for normal users. */
|
||||
if (u->manager->remove_ipc && u->uid > SYSTEM_UID_MAX) {
|
||||
if (u->manager->remove_ipc && !uid_is_system(u->uid)) {
|
||||
k = clean_ipc_by_uid(u->uid);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "string-table.h"
|
||||
#include "terminal-util.h"
|
||||
#include "unit-name.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
|
||||
Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
|
||||
@ -656,7 +657,7 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
|
||||
if (uid_base != 0)
|
||||
return -ENXIO;
|
||||
/* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */
|
||||
if (uid_range < (uid_t) 65534U)
|
||||
if (uid_range < UID_NOBODY)
|
||||
return -ENXIO;
|
||||
|
||||
/* If there's more than one line, then we don't support this mapping. */
|
||||
|
@ -21,12 +21,6 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/* Note that devpts's gid= parameter parses GIDs as signed values, hence we stay away from the upper half of the 32bit
|
||||
* UID range here. We leave a bit of room at the lower end and a lot of room at the upper end, so that other subsystems
|
||||
* may have their own allocation ranges too. */
|
||||
#define UID_SHIFT_PICK_MIN ((uid_t) UINT32_C(0x00080000))
|
||||
#define UID_SHIFT_PICK_MAX ((uid_t) UINT32_C(0x6FFF0000))
|
||||
|
||||
/* While we are chmod()ing a directory tree, we set the top-level UID base to this "busy" base, so that we can always
|
||||
* recognize trees we are were chmod()ing recursively and got interrupted in */
|
||||
#define UID_BUSY_BASE ((uid_t) UINT32_C(0xFFFE0000))
|
||||
|
@ -2862,7 +2862,7 @@ static int uid_shift_pick(uid_t *shift, LockFile *ret_lock_file) {
|
||||
if (--n_tries <= 0)
|
||||
return -EBUSY;
|
||||
|
||||
if (candidate < UID_SHIFT_PICK_MIN || candidate > UID_SHIFT_PICK_MAX)
|
||||
if (candidate < CONTAINER_UID_BASE_MIN || candidate > CONTAINER_UID_BASE_MAX)
|
||||
goto next;
|
||||
if ((candidate & UINT32_C(0xFFFF)) != 0)
|
||||
goto next;
|
||||
@ -2904,7 +2904,7 @@ static int uid_shift_pick(uid_t *shift, LockFile *ret_lock_file) {
|
||||
} else
|
||||
random_bytes(&candidate, sizeof(candidate));
|
||||
|
||||
candidate = (candidate % (UID_SHIFT_PICK_MAX - UID_SHIFT_PICK_MIN)) + UID_SHIFT_PICK_MIN;
|
||||
candidate = (candidate % (CONTAINER_UID_BASE_MAX - CONTAINER_UID_BASE_MIN)) + CONTAINER_UID_BASE_MIN;
|
||||
candidate &= (uid_t) UINT32_C(0xFFFF0000);
|
||||
}
|
||||
}
|
||||
|
@ -480,7 +480,7 @@ enum nss_status _nss_mymachines_getpwnam_r(
|
||||
|
||||
pwd->pw_name = buffer;
|
||||
pwd->pw_uid = mapped;
|
||||
pwd->pw_gid = 65534; /* nobody */
|
||||
pwd->pw_gid = GID_NOBODY;
|
||||
pwd->pw_gecos = buffer;
|
||||
pwd->pw_passwd = (char*) "*"; /* locked */
|
||||
pwd->pw_dir = (char*) "/";
|
||||
@ -557,7 +557,7 @@ enum nss_status _nss_mymachines_getpwuid_r(
|
||||
|
||||
pwd->pw_name = buffer;
|
||||
pwd->pw_uid = uid;
|
||||
pwd->pw_gid = 65534; /* nobody */
|
||||
pwd->pw_gid = GID_NOBODY;
|
||||
pwd->pw_gecos = buffer;
|
||||
pwd->pw_passwd = (char*) "*"; /* locked */
|
||||
pwd->pw_dir = (char*) "/";
|
||||
|
@ -47,8 +47,8 @@ static const struct passwd root_passwd = {
|
||||
static const struct passwd nobody_passwd = {
|
||||
.pw_name = (char*) NOBODY_USER_NAME,
|
||||
.pw_passwd = (char*) "*", /* locked */
|
||||
.pw_uid = 65534,
|
||||
.pw_gid = 65534,
|
||||
.pw_uid = UID_NOBODY,
|
||||
.pw_gid = GID_NOBODY,
|
||||
.pw_gecos = (char*) "User Nobody",
|
||||
.pw_dir = (char*) "/",
|
||||
.pw_shell = (char*) "/sbin/nologin",
|
||||
@ -63,7 +63,7 @@ static const struct group root_group = {
|
||||
|
||||
static const struct group nobody_group = {
|
||||
.gr_name = (char*) NOBODY_GROUP_NAME,
|
||||
.gr_gid = 65534,
|
||||
.gr_gid = GID_NOBODY,
|
||||
.gr_passwd = (char*) "*", /* locked */
|
||||
.gr_mem = (char*[]) { NULL },
|
||||
};
|
||||
@ -251,7 +251,7 @@ enum nss_status _nss_systemd_getpwuid_r(
|
||||
}
|
||||
}
|
||||
|
||||
if (uid <= SYSTEM_UID_MAX)
|
||||
if (!uid_is_dynamic(uid))
|
||||
goto not_found;
|
||||
|
||||
if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
|
||||
@ -463,7 +463,7 @@ enum nss_status _nss_systemd_getgrgid_r(
|
||||
}
|
||||
}
|
||||
|
||||
if (gid <= SYSTEM_GID_MAX)
|
||||
if (!gid_is_dynamic(gid))
|
||||
goto not_found;
|
||||
|
||||
if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
|
||||
@ -500,7 +500,6 @@ enum nss_status _nss_systemd_getgrgid_r(
|
||||
|
||||
direct_lookup:
|
||||
if (bypass > 0) {
|
||||
|
||||
r = direct_lookup_uid(gid, &direct);
|
||||
if (r == -ENOENT)
|
||||
goto not_found;
|
||||
|
@ -157,7 +157,7 @@ static int condition_test_user(Condition *c) {
|
||||
return id == getuid() || id == geteuid();
|
||||
|
||||
if (streq("@system", c->parameter))
|
||||
return getuid() <= SYSTEM_UID_MAX || geteuid() <= SYSTEM_UID_MAX;
|
||||
return uid_is_system(getuid()) || uid_is_system(geteuid());
|
||||
|
||||
username = getusername_malloc();
|
||||
if (!username)
|
||||
|
@ -391,7 +391,7 @@ static void test_condition_test_user(void) {
|
||||
assert_se(condition);
|
||||
r = condition_test(condition);
|
||||
log_info("ConditionUser=@system → %i", r);
|
||||
if (getuid() < SYSTEM_UID_MAX || geteuid() < SYSTEM_UID_MAX)
|
||||
if (uid_is_system(getuid()) || uid_is_system(geteuid()))
|
||||
assert_se(r > 0);
|
||||
else
|
||||
assert_se(r == 0);
|
||||
|
@ -324,7 +324,6 @@ static void test_exec_systemcallfilter_system(Manager *m) {
|
||||
log_notice("Seccomp not available, skipping %s", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (getpwnam("nobody"))
|
||||
test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED);
|
||||
else if (getpwnam("nfsnobody"))
|
||||
@ -348,8 +347,10 @@ static void test_exec_group(Manager *m) {
|
||||
test(m, "exec-group.service", 0, CLD_EXITED);
|
||||
else if (getgrnam("nfsnobody"))
|
||||
test(m, "exec-group-nfsnobody.service", 0, CLD_EXITED);
|
||||
else if (getgrnam("nogroup"))
|
||||
test(m, "exec-group-nogroup.service", 0, CLD_EXITED);
|
||||
else
|
||||
log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody group: %m", __func__);
|
||||
log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody/nogroup group: %m", __func__);
|
||||
}
|
||||
|
||||
static void test_exec_supplementarygroups(Manager *m) {
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "string-util.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
#include "path-util.h"
|
||||
|
||||
static void test_uid_to_name_one(uid_t uid, const char *name) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
@ -144,17 +145,51 @@ static void test_valid_home(void) {
|
||||
assert_se(valid_home("/home/foo"));
|
||||
}
|
||||
|
||||
static void test_get_user_creds_one(const char *id, const char *name, uid_t uid, gid_t gid, const char *home, const char *shell) {
|
||||
const char *rhome;
|
||||
const char *rshell;
|
||||
uid_t ruid;
|
||||
gid_t rgid;
|
||||
|
||||
assert_se(get_user_creds(&id, &ruid, &rgid, &rhome, &rshell) >= 0);
|
||||
assert_se(streq_ptr(id, name));
|
||||
assert_se(ruid == uid);
|
||||
assert_se(rgid == gid);
|
||||
assert_se(path_equal(rhome, home));
|
||||
assert_se(path_equal(rshell, shell));
|
||||
}
|
||||
|
||||
static void test_get_group_creds_one(const char *id, const char *name, gid_t gid) {
|
||||
gid_t rgid;
|
||||
|
||||
assert_se(get_group_creds(&id, &rgid) >= 0);
|
||||
assert_se(streq_ptr(id, name));
|
||||
assert_se(rgid == gid);
|
||||
}
|
||||
|
||||
int main(int argc, char*argv[]) {
|
||||
|
||||
test_uid_to_name_one(0, "root");
|
||||
test_uid_to_name_one(UID_NOBODY, NOBODY_USER_NAME);
|
||||
test_uid_to_name_one(0xFFFF, "65535");
|
||||
test_uid_to_name_one(0xFFFFFFFF, "4294967295");
|
||||
|
||||
test_gid_to_name_one(0, "root");
|
||||
test_gid_to_name_one(GID_NOBODY, NOBODY_GROUP_NAME);
|
||||
test_gid_to_name_one(TTY_GID, "tty");
|
||||
test_gid_to_name_one(0xFFFF, "65535");
|
||||
test_gid_to_name_one(0xFFFFFFFF, "4294967295");
|
||||
|
||||
test_get_user_creds_one("root", "root", 0, 0, "/root", "/bin/sh");
|
||||
test_get_user_creds_one("0", "root", 0, 0, "/root", "/bin/sh");
|
||||
test_get_user_creds_one(NOBODY_USER_NAME, NOBODY_USER_NAME, UID_NOBODY, GID_NOBODY, "/", "/sbin/nologin");
|
||||
test_get_user_creds_one("65534", NOBODY_USER_NAME, UID_NOBODY, GID_NOBODY, "/", "/sbin/nologin");
|
||||
|
||||
test_get_group_creds_one("root", "root", 0);
|
||||
test_get_group_creds_one("0", "root", 0);
|
||||
test_get_group_creds_one(NOBODY_GROUP_NAME, NOBODY_GROUP_NAME, GID_NOBODY);
|
||||
test_get_group_creds_one("65534", NOBODY_GROUP_NAME, GID_NOBODY);
|
||||
|
||||
test_parse_uid();
|
||||
test_uid_ptr();
|
||||
|
||||
|
@ -68,6 +68,7 @@ test_data_files = '''
|
||||
test-execute/exec-environment.service
|
||||
test-execute/exec-environmentfile.service
|
||||
test-execute/exec-group-nfsnobody.service
|
||||
test-execute/exec-group-nogroup.service
|
||||
test-execute/exec-group.service
|
||||
test-execute/exec-ignoresigpipe-no.service
|
||||
test-execute/exec-ignoresigpipe-yes.service
|
||||
|
7
test/test-execute/exec-group-nogroup.service
Normal file
7
test/test-execute/exec-group-nogroup.service
Normal file
@ -0,0 +1,7 @@
|
||||
[Unit]
|
||||
Description=Test for Group
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "nogroup"'
|
||||
Type=oneshot
|
||||
Group=nogroup
|
Loading…
x
Reference in New Issue
Block a user