Hadi Chokr [Tue, 28 Apr 2026 10:18:43 +0000 (12:18 +0200)]
useradd: fix btrfs subvolume creation for single-component basedir
dirname() replaces broken stpcpy index arithmetic that produced an
empty string for single-component paths (e.g. /koolhome), causing
statfs to fail and fall back to a regular directory. Use path in
the error message since dirname() modifies btrfs_check in-place,
making it unusable for logging after the call.
Fixes: c1d36a8acb1d (2019-05-04; "Add support for btrfs subvolumes for user homes") Signed-off-by: Hadi Chokr <hadichokr@icloud.com> Reviewed-by: Alejandro Colomar <alx@kernel.org>
tests: add CI user creation to simulate cloud VM environments
Create a 'ci' user in all distribution-specific ansible roles to
simulate cloud VM environments where ID 1000 is already taken. This
ensures that test users start the ID assignment at 1001, making
container tests consistent with cloud CI environments.
tests/system/framework/hosts/shadow.py: fix restore when /home is a mount point
Handle cases where /home is mounted separately by using selective
cleanup instead of attempting to remove the mount point, which causes
"Device or resource busy" errors in cloud environments.
Hadi Chokr [Tue, 21 Apr 2026 07:18:19 +0000 (09:18 +0200)]
useradd(8): fallback to regular dir for BTRFS home on non-BTRFS parent
When the --btrfs-subvolume-home option is used but the parent directory
is not on a BTRFS filesystem, useradd previously failed with an error.
This is too strict; instead, fall back to creating a regular directory
and issue a warning. The subvolume creation is attempted only when the
parent is BTRFS. Otherwise, a regular directory is created and a
syslog(3) warning is logged.
Fixes: 3e8c105 (2026-01-02; "src/useradd: Support config for creating home dirs as Btrfs subvolumes") Co-authored-by: Hadi Chokr <hadichokr@icloud.com> Co-authored-by: Alejandro Colomar <alx@kernel.org> Signed-off-by: Alejandro Colomar <alx@kernel.org>
Tests: Add a new user with supplementary groups using default primary group
This is the transformation to Python of the test located in
`tests/usertools/01/16_useradd_add_user_to_one_group.test` which checks that
`useradd` can add a new user with specific group
Tests: Verify user creation fails when UID exceeds maximum allowed value (2^32 - 1)
This is the transformation to Python of the test located in
`tests/usertools/01/26_useradd_UID_-1.test`
which checks that `useradd` can not add a new user with specific uid
Tests: Add a new user with custom primary group and supplementary groups
This is the transformation to Python of the test located in
`tests/usertools/01/16_useradd_add_user_to_multiple_groups.test`
which checks that `useradd` can add a new user with specific group
Tests: Verify user creation succeeds with large valid UIDs
This is the transformation to Python of the test located in
`tests/usertools/01/25_useradd_specified_large_UID2.test`,
which checks that `useradd` can add a new user with large UID
Pat Riehecky [Fri, 13 Mar 2026 14:25:41 +0000 (09:25 -0500)]
subid: setup deterministic_wrap mode
This adds two new options to /etc/login.defs:
* UNSAFE_SUB_UID_DETERMINISTIC_WRAP
* UNSAFE_SUB_GID_DETERMINISTIC_WRAP
Deterministic subordinate ID allocation ties each user's subid range
directly to their UID, giving consistent, reproducible ranges across all
hosts without a shared database. This property breaks down when the
subordinate ID space is exhausted.
With a UID space that on Linux extends to 2^32-1 and the traditional
per-user subid allocation of 2^16 ranges, a site with a large UID
population could exhaust the subordinate ID space before all user UIDs
are allocated.
UNSAFE_SUB_UID_DETERMINISTIC_WRAP and UNSAFE_SUB_GID_DETERMINISTIC_WRAP
provide an explicit opt-in to modulo (ring-buffer) wrapping as a
predictable last resort. This preserves the deterministic allocation
at the risk of subid overlap.
The UNSAFE_ prefix and the required explicit opt-in are intentional.
Overlapping ranges break namespace isolation and can allow container
escapes and privilege escalation between users whose ranges collide.
These options are appropriate only when all of the following hold:
- Strict subid determinism is require
- The active UID population on the host is small and well-known
- The administrator regularly audits the UID distribution and confirms
no two active users produce overlapping computed ranges
Do not enable these options on hosts with an uncontrolled user population.
Pat Riehecky [Mon, 16 Mar 2026 13:21:30 +0000 (08:21 -0500)]
subid: Add deterministic subid ranges
This adds two new options to /etc/login.defs:
* SUB_UID_DETERMINISTIC
* SUB_GID_DETERMINISTIC
In a lab where users are created ad hoc subids might drift
from one host to the other. If there is a shared home area,
this drift can create some frustration.
Creating subids deterministically provides one type of solution
to this problem. Use of nonconsecutive UIDs will result in blocks
of unused subids.
The manpages provide documentation on how these can be used.
lib/subordinateio.c: find_free_range(): Use id_t instead of u_long
It's the natural type for this API, and it's also more
consistent with its wrappers.
Let's also use literal -1 for the error code, which is safer than
unsigned constants, as -1 is sign-extended to fit whatever unsigned type
we're using.
Revert "strchriscntrl: reject C1 control bytes (0x80-0x9F)"
C1 control bytes are more complicated than that. They're represented as
two bytes in UTF-8.
Commit 19d725da, has issues, rejecting otherwise valid UTF-8 multi-byte
characters.
We could in theory do correct parsing of UTF, possibly parsing the
multi-byte sequences, or translating to wchar_t. However, that would
complicate the source code well beyond what I'd be comfortable with.
Instead, let's revert this, and claim no intention to support UTF-8.
If an admin uses a UTF-8 locale while reading /etc/passwd, that's their
own fault.
aborah-sudo [Tue, 31 Mar 2026 04:23:42 +0000 (09:53 +0530)]
Tests: Add a new user with a specified large UID
This is the transformation to Python of the test located in
`tests/usertools/01/15_useradd_specified_large_UID.test` which
checks that `useradd` can add a new user with large UID
aborah-sudo [Tue, 31 Mar 2026 04:16:02 +0000 (09:46 +0530)]
Tests: Add a new user with an invalid UID
This is the transformation to Python of the test located in
`tests/usertools/01/13_useradd_negative_UID.test`,
`tests/usertools/01/14_useradd_out_of_range_UID.test`
which checks that `useradd` can not add a new user with invalid UID
Iker Pedrosa [Wed, 25 Mar 2026 13:37:01 +0000 (14:37 +0100)]
*/: groupmems(8): Remove program
The utility is redundant for root and effectively broken for regular
users across major distributions, its continued maintenance adds
complexity for little to no benefit.
Pat Riehecky [Tue, 10 Mar 2026 14:47:26 +0000 (09:47 -0500)]
subid: use is_same_user() for owner resolution in find_range
Replace the resolution in find_range with is_same_user(). The
actualy results should be the same.
Previously, find_range performed its own getpwnam() based UID lookup to
handle entries recorded by UID or by username. This duplicated logic
centralized in is_same_user().
is_same_user() resolves ownership by username, UID, and overlapping
entries, making find_range consistent with how ownership is determined
elsewhere.
Reviewed-by: Alejandro Colomar <alx@kernel.org> Signed-off-by: Pat Riehecky <riehecky@fnal.gov>
Pat Riehecky [Tue, 10 Mar 2026 14:42:48 +0000 (09:42 -0500)]
subid: use is_same_user() for owner resolution in range queries
Replace direct owner string comparison in range_exists, get_owner_id,
list_owner_ranges, and new_subid_range with is_same_user().
Previously, ownership checks required a literal match against the owner
field. This created a situation where if entries were recorded by UID and
by username not all would be identified.
is_same_user() resolves ownership by username, UID, and overlapping
entries, making range queries consistent with how ownership is determined
elsewhere.
Fixes: 0a7888b1 (2020-06-07; "Create a new libsubid") CC: Serge Hallyn <serge@hallyn.com> Reviewed-by: Alejandro Colomar <alx@kernel.org> Signed-off-by: Pat Riehecky <riehecky@fnal.gov>
Pat Riehecky [Tue, 10 Mar 2026 14:39:57 +0000 (09:39 -0500)]
subid: Add is_same_user for unified ID lookups
Each identifier is resolved via getpw_uid_or_nam and compared
numerically. Accepts usernames, numeric UIDs, or a mix of both.
Numeric resolution ensures stale entries for deleted users cannot
produce false matches, and handles systems with overlapping UIDs.
We do not perform a streq(3) on the passed strings themselves.
In this way we ensure the user is resolvable on the system
eliminating the ability to return results from deleted users
with stale entries.
Returns false if either identifier cannot be resolved, or if the
resolved UIDs do not match.
Reviewed-by: Alejandro Colomar <alx@kernel.org> Signed-off-by: Pat Riehecky <riehecky@fnal.gov>
aborah-sudo [Wed, 25 Mar 2026 04:19:33 +0000 (09:49 +0530)]
Tests: Add a new user with a specified existing numerical primary group
This is the transformation to Python of the test located in
`tests/usertools/01/07_useradd_numerical_primary_group.test`, which checks that `useradd` can add a new user with valid existing group as primary
* Run vipw on passwd file and add a user
* Run vipw on shadow file and add a user
* Run vipw on group file and add a group
* Run vipw on gshadow file and add a group
tests/system/tests/test_chgpasswd.py: change multiple group passwords interactive
This is the transformation to Python of the test located in
`tests/grouptools/chgpasswd/02_chgpasswd_multiple_groups/chgpasswd.test`,
which checks that `chgpasswd` is able to change the password of multiple
groups in batch.
tests/system/tests/test_gpasswd.py: add user as group administrator
This is the transformation to Python of the test located in
`tests/grouptools/gpasswd/50_gpasswd_change_admin_list/gpasswd.test`,
which checks that `gpasswd` is able to add a user as a group
administrator.
tests/system/tests/test_gpasswd.py: add user to group membership as root user
This is the transformation to Python of the test located in
`tests/grouptools/gpasswd/31_gpasswd_add_user_to_group/gpasswd.test`,
which checks that `gpasswd` is able to add a user to a group membership
running as root.
tests/system/tests/test_chpasswd.py: change multiple user passwords interactive
This is the transformation to Python of the test located in
`tests/usertools/chpasswd/02_chpasswd_multiple_users/chpasswd.test`,
which checks that `chpasswd` is able to change the password of multiple
users in batch.
Iker Pedrosa [Mon, 23 Feb 2026 13:35:28 +0000 (14:35 +0100)]
tests/system/tests/test_passwd.py: lock user password
This is the transformation to Python of the test located in
`tests/passwd/06_passwd_-l_root_lock_account/passwd.test`,
which checks that `passwd` is able to lock a user's password.
tests/system/tests/test_passwd.py: change user password as root in interactive mode
This is the transformation to Python of the test located in
`tests/passwd/18_passwd_root_change_password_user/passwd.test`,
which checks that `passwd` is able to change a user's password running
as root in interactive mode.
aborah-sudo [Mon, 23 Mar 2026 05:12:09 +0000 (10:42 +0530)]
Tests: Add a new user with a specified non-existing primary group
This is the transformation to Python of the test located in
`tests/usertools/01/06_useradd_invalid_named_primary_group.test`, which checks that `useradd` can not add a new user with invalid existing group as primary
aborah-sudo [Tue, 17 Mar 2026 05:22:28 +0000 (10:52 +0530)]
Tests: Add a new user with a valid existing group as primary
This is the transformation to Python of the test located in
`tests/usertools/01/08_useradd_named_primary_group.test`, which checks that `useradd` can add a new user with a valid existing group as primary
aborah-sudo [Fri, 13 Mar 2026 05:33:10 +0000 (11:03 +0530)]
Tests: Add a new user with a specified non-existing gid
This is the transformation to Python of the test located in
`tests/usertools/01/05_useradd_invalid_numeric_primary_group.test`, which checks that `useradd` fails to create a new user with non-existing group
If a new shadow entry is created, the passwd entry's password hash is
moved into shadow file and replaced with an "x". If this happens, update
the passwd file as well, otherwise the "x" is not written to disk.
Verify that sysconf does not return -1. It is not important if it's due
to an error or due to a missing upper boundary. In both cases, fall back
to a constant value.
Verify that sysconf does not return -1. It is not important if it's due
to an error or due to a missing upper boundary. In both cases, fall back
to a constant value.
strchriscntrl: reject C1 control bytes (0x80-0x9F)
glibc's iscntrl() does not classify C1 control bytes as control
characters in any locale. The iscntrl() check was added as part of the
fix for CVE-2023-29383, which blocked C0 control characters, but C1
bytes were never considered.
The byte 0x9B is the C1 encoding of CSI (Control Sequence Introducer),
equivalent to ESC [. On terminals that interpret C1 codes (VTE-based
terminals such as GNOME Terminal, Tilix, Terminator, XFCE Terminal),
an attacker can inject terminal escape sequences into GECOS fields via
chfn. This allows visual spoofing of /etc/passwd output, for example
making a user's UID appear as 0 when viewed with cat.
Explicitly check for bytes in the 0x80-0x9F range in strchriscntrl()
so that valid_field() returns -1 (rejected) instead of 1 (non-ASCII
warning) for these bytes.
Before (unpatched chfn accepts C1 bytes and writes them to /etc/passwd):
If -e 0 or -f 0 are supplied but no shadow entry exists, these flags are
silently ignored. Internally, the supplied values (0) are compared with
static long values which are initially 0 as well. If they are identical,
usermod drops the flags.
This does not match usermod's manual page, which states that a missing
shadow entry is created. This is also true for any other valid command
line values, e.g. -e 1 or -f 1.
aborah-sudo [Wed, 11 Mar 2026 08:27:42 +0000 (13:57 +0530)]
Tests: Add user with existing UID without -o flag
This is the transformation to Python of the test located in
`04_useradd_add_user_with_existing_UID_fail`, which checks that `useradd` fails to create a new user with existing UID without -o flag