Skip to content

Permit Subid storage by username or uid#1573

Merged
hallyn merged 8 commits intoshadow-maint:masterfrom
jcpunk:subid-by-username-or-uid
Mar 30, 2026
Merged

Permit Subid storage by username or uid#1573
hallyn merged 8 commits intoshadow-maint:masterfrom
jcpunk:subid-by-username-or-uid

Conversation

@jcpunk
Copy link
Copy Markdown
Contributor

@jcpunk jcpunk commented Mar 10, 2026

This PR obsoletes #1570

The actual code changes are broken into smaller commits.

Closes: #1554

I'm not sure the tests are well setup or have sufficient coverage.

Comment thread lib/subordinateio.c Outdated
Comment thread lib/subordinateio.c Outdated
Comment thread lib/subordinateio.c Outdated
Comment thread lib/subordinateio.c Outdated
Comment thread lib/subordinateio.c Outdated
Comment thread lib/subordinateio.c Outdated
Comment thread lib/subordinateio.c
Comment thread lib/subordinateio.c
Comment thread lib/subordinateio.c Outdated
Comment thread lib/subordinateio.c Outdated
@jcpunk jcpunk force-pushed the subid-by-username-or-uid branch from 42149b0 to ceac681 Compare March 10, 2026 22:55
Comment thread lib/subordinateio.c Outdated
@jcpunk jcpunk force-pushed the subid-by-username-or-uid branch from ceac681 to 517b0bf Compare March 10, 2026 23:12
Comment thread lib/subordinateio.c
@jcpunk jcpunk force-pushed the subid-by-username-or-uid branch from 517b0bf to 2dced79 Compare March 10, 2026 23:20
Comment thread lib/subordinateio.c Outdated
Comment thread lib/subordinateio.c Outdated
Comment thread lib/subordinateio.c Outdated
Comment thread lib/subordinateio.c Outdated
@jcpunk jcpunk force-pushed the subid-by-username-or-uid branch 3 times, most recently from 23535c7 to 5cf0253 Compare March 11, 2026 00:28
Comment thread lib/subordinateio.c
Comment thread lib/subordinateio.c
Comment thread lib/subordinateio.c Outdated
Comment thread lib/subordinateio.c Outdated
Comment thread lib/subordinateio.c Outdated
@ikerexxe
Copy link
Copy Markdown
Collaborator

Just to make sure I understand the goal of this PR correctly. You'd like to store the user/group ID instead of the username/groupname in /etc/sub*id. File example: 1000:165536:165536 instead of jcpunk:165536:165536. Is my understanding correct?

@jcpunk jcpunk force-pushed the subid-by-username-or-uid branch from 5cf0253 to 2d7e383 Compare March 11, 2026 13:52
@jcpunk jcpunk force-pushed the subid-by-username-or-uid branch from 5f4d0fd to d22e9ca Compare March 13, 2026 01:48
Comment thread lib/subordinateio.c
Copy link
Copy Markdown
Collaborator

@alejandro-colomar alejandro-colomar left a comment

Choose a reason for hiding this comment

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

All patches LGTM. Thanks!

Reviewed-by: Alejandro Colomar <alx@kernel.org>

@jcpunk jcpunk force-pushed the subid-by-username-or-uid branch from d22e9ca to 8478a00 Compare March 13, 2026 14:01
@alejandro-colomar
Copy link
Copy Markdown
Collaborator

Thanks! I'll merge.

@alejandro-colomar
Copy link
Copy Markdown
Collaborator

Hmmm, there's a CI job failing. I've tried re-running it to see if it's just a spurious failure. Let's see.

@alejandro-colomar
Copy link
Copy Markdown
Collaborator

alejandro-colomar commented Mar 13, 2026

Hmmm, the testsuite failure seems consistent. Could you please have a look at the test and see why it's failing?

Cc: @hallyn

@jcpunk
Copy link
Copy Markdown
Contributor Author

jcpunk commented Mar 13, 2026

I'm guess that, because the queries now require the user to resolve to a real identity that the checks for user "foo" are not returning anything.

@jcpunk jcpunk force-pushed the subid-by-username-or-uid branch 2 times, most recently from 060f6f9 to c0dfadc Compare March 13, 2026 21:25
@jcpunk
Copy link
Copy Markdown
Contributor Author

jcpunk commented Mar 13, 2026

Digging a bit deeper, I'm showing results I don't understand...

It appears that pw_a is set to the content of pw_b at the second call to getpw_uid_or_nam within is_same_user???

Here is my diff to make print outs:

--- a/lib/shadow/passwd/getpw.h
+++ b/lib/shadow/passwd/getpw.h
@@ -23,6 +23,14 @@ getpw_uid_or_nam(const char *u)
 {
        uid_t  uid;
 
+printf("AAA: %s\n", u);
+if (get_uid(u, &uid) == 0) {
+getpwuid(uid);
+printf("BBB: %s\n", getpwuid(uid)->pw_name);
+} else {
+printf("CCC: %s\n", getpwnam(u)->pw_name);
+}
+
        return get_uid(u, &uid) == 0 ? getpwuid(uid) : getpwnam(u);
 }
 
diff --git a/lib/subordinateio.c b/lib/subordinateio.c
index 37e5100a..d41cba21 100644
--- a/lib/subordinateio.c
+++ b/lib/subordinateio.c
@@ -151,6 +151,8 @@ is_same_user(const char *a, const char *b)
        const struct passwd  *pw_a;
        const struct passwd  *pw_b;
 
+printf("XXXXX: does names %s == %s?\n", a, b);
+
        pw_a = getpw_uid_or_nam(a);
        if (NULL == pw_a)
                return false;
@@ -159,6 +161,8 @@ is_same_user(const char *a, const char *b)
        if (NULL == pw_b)
                return false;
 
+printf("YYY: does ID %s == %s?\n", pw_a->pw_name, pw_b->pw_name);
+printf("YYY: does ID %i == %i?\n", pw_a->pw_uid, pw_b->pw_uid);
        return pw_a->pw_uid == pw_b->pw_uid;
 }
 
@@ -882,6 +886,7 @@ int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_r
 
        commonio_rewind(db);
        while (NULL != (range = commonio_next(db))) {
+printf("The flag is: %s\n", is_same_user(range->owner, owner) ? "true" : "false");
                if (is_same_user(range->owner, owner)) {
                        ranges = append_range(ranges, range, count++);
                        if (ranges == NULL) {

And I get the following output from the test with my updated ranges:

----------
/tmp/subuidlist
----------------
XXXXX: does names 1000 == nobody?
AAA: 1000
BBB: ubuntu
AAA: nobody
CCC: nobody
YYY: does ID ubuntu == nobody?
YYY: does ID 1000 == 65534?
The flag is: false
XXXXX: does names 1000 == nobody?
AAA: 1000
BBB: ubuntu
AAA: nobody
CCC: nobody
YYY: does ID ubuntu == nobody?
YYY: does ID 1000 == 65534?
XXXXX: does names bin == nobody?
AAA: bin
CCC: bin
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
The flag is: true
XXXXX: does names bin == nobody?
AAA: bin
CCC: bin
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
XXXXX: does names 65534 == nobody?
AAA: 65534
BBB: nobody
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
The flag is: true
XXXXX: does names 65534 == nobody?
AAA: 65534
BBB: nobody
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
XXXXX: does names nobody == nobody?
AAA: nobody
CCC: nobody
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
The flag is: true
XXXXX: does names nobody == nobody?
AAA: nobody
CCC: nobody
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
XXXXX: does names nobody == nobody?
AAA: nobody
CCC: nobody
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
The flag is: true
XXXXX: does names nobody == nobody?
AAA: nobody
CCC: nobody
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
XXXXX: does names root == nobody?
AAA: root
CCC: root
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
The flag is: true
XXXXX: does names root == nobody?
AAA: root
CCC: root
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
XXXXX: does names 0 == nobody?
AAA: 0
BBB: root
AAA: nobody
CCC: nobody
YYY: does ID root == nobody?
YYY: does ID 0 == 65534?
The flag is: false
XXXXX: does names 0 == nobody?
AAA: 0
BBB: root
AAA: nobody
CCC: nobody
YYY: does ID root == nobody?
YYY: does ID 0 == 65534?
0: nobody 190000 10000
1: nobody 200000 10000
2: nobody 300000 10000
3: nobody 400000 10000
4: nobody 500000 1000
---------------
/tmp/subgidlist
-----------------------
XXXXX: does names bin == nobody?
AAA: bin
CCC: bin
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
The flag is: true
XXXXX: does names bin == nobody?
AAA: bin
CCC: bin
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
XXXXX: does names nobody == nobody?
AAA: nobody
CCC: nobody
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
The flag is: true
XXXXX: does names nobody == nobody?
AAA: nobody
CCC: nobody
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
XXXXX: does names 65534 == nobody?
AAA: 65534
BBB: nobody
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
The flag is: true
XXXXX: does names 65534 == nobody?
AAA: 65534
BBB: nobody
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
XXXXX: does names root == nobody?
AAA: root
CCC: root
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
The flag is: true
XXXXX: does names root == nobody?
AAA: root
CCC: root
AAA: nobody
CCC: nobody
YYY: does ID nobody == nobody?
YYY: does ID 65534 == 65534?
XXXXX: does names 0 == nobody?
AAA: 0
BBB: root
AAA: nobody
CCC: nobody
YYY: does ID root == nobody?
YYY: does ID 0 == 65534?
The flag is: false
XXXXX: does names 0 == nobody?
AAA: 0
BBB: root
AAA: nobody
CCC: nobody
YYY: does ID root == nobody?
YYY: does ID 0 == 65534?
0: nobody 190000 10000
1: nobody 200000 10000
2: nobody 210000 10000
3: nobody 500000 1000

Which shows at some point before YYY bin gets swapped in for nobody???

@jcpunk jcpunk force-pushed the subid-by-username-or-uid branch from c0dfadc to 4bafb0a Compare March 13, 2026 22:11
@jcpunk
Copy link
Copy Markdown
Contributor Author

jcpunk commented Mar 13, 2026

Found the fix, the retrieved structure from getpw_uid_or_nam seems to have some reused elements via the getpw* calls...

This resulted in a code change to commit 2 "subid: Add is_same_user for unified ID lookups" and I removed the Reviewed-by tag.

@alejandro-colomar
Copy link
Copy Markdown
Collaborator

Found the fix, the retrieved structure from getpw_uid_or_nam seems to have some reused elements via the getpw* calls...

This resulted in a code change to commit 2 "subid: Add is_same_user for unified ID lookups" and I removed the Reviewed-by tag.

Ahhh, yes, now it makes sense. getpwuid(3) and getpwnam(3) reuse static memory. We should copy the contents.

Thanks!

I'll review the changes.

Comment thread lib/subordinateio.c
Comment on lines 17 to 37
change_config

echo -n "list foo's ranges..."
${build_path}/src/getsubids foo > /tmp/subuidlistout
${build_path}/src/getsubids -g foo > /tmp/subgidlistout
${build_path}/src/getsubids nobody > /tmp/subuidlistout
${build_path}/src/getsubids -g nobody > /tmp/subgidlistout
echo "OK"

echo -n "Check the subuid ranges..."
[ $(wc -l /tmp/subuidlistout | awk '{ print $1 }') -eq 2 ]
grep "0: foo 300000 10000" /tmp/subuidlistout
grep "1: foo 400000 10000" /tmp/subuidlistout
[ $(wc -l /tmp/subuidlistout | awk '{ print $1 }') -eq 3 ]
grep "0: nobody 200000 10000" /tmp/subuidlistout
grep "1: nobody 300000 10000" /tmp/subuidlistout
grep "2: nobody 400000 10000" /tmp/subuidlistout
echo "OK"

echo -n "Check the subgid ranges..."
[ $(wc -l /tmp/subgidlistout | awk '{ print $1 }') -eq 1 ]
grep "0: foo 200000 10000" /tmp/subgidlistout
[ $(wc -l /tmp/subgidlistout | awk '{ print $1 }') -eq 2 ]
grep "0: nobody 200000 10000" /tmp/subgidlistout
grep "1: nobody 210000 10000" /tmp/subgidlistout
echo "OK"

log_status "$0" "SUCCESS"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not too familiar with this part. @ikerexxe , @hallyn , @stoeckmann , would you mind having a look at the last commit?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not really sure what the test is doing. I don't see where nobody's sub*ids come from.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You did. I couldn't include them in the same quote, as they're in different files.

jcpunk added 7 commits March 13, 2026 17:23
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>
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: 0a7888b (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>
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>
They are not active within this commit, but they are fully documented

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Pat Riehecky <riehecky@fnal.gov>
This adds two new options to /etc/login.defs:
* SUB_UID_STORE_BY_UID
* SUB_GID_STORE_BY_UID

They default to 'no' but when set 'yes' the subuid/subgid
entries will be written by uid rather than username.

Closes: #1554

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Pat Riehecky <riehecky@fnal.gov>
There are no longer any callers for get_owner_id so it can be removed.

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Pat Riehecky <riehecky@fnal.gov>
Now that user resolution is required, ensure a resolvable user is used.

Signed-off-by: Pat Riehecky <riehecky@fnal.gov>
@jcpunk
Copy link
Copy Markdown
Contributor Author

jcpunk commented Mar 29, 2026

Just double checking that nothing is waiting on me. I was out of office and kinda lost track.

@alejandro-colomar
Copy link
Copy Markdown
Collaborator

Just double checking that nothing is waiting on me. I was out of office and kinda lost track.

That's correct. I'm waiting on @hallyn and/or @ikerexxe OKing the changes to tests as I don't feel qualified to review them.

@hallyn hallyn merged commit b93e08f into shadow-maint:master Mar 30, 2026
16 checks passed
@jcpunk jcpunk deleted the subid-by-username-or-uid branch March 30, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

usermod flag to write subuid and subgid entries by UID rather than username

4 participants