diff --git a/shadow-4.11.1-newidmap-support-passing-pid-as-fd.patch b/shadow-4.11.1-newidmap-support-passing-pid-as-fd.patch new file mode 100644 index 0000000..c67cc4f --- /dev/null +++ b/shadow-4.11.1-newidmap-support-passing-pid-as-fd.patch @@ -0,0 +1,300 @@ +diff -up shadow-4.11.1/lib/get_pid.c.newidmap-pid-fd shadow-4.11.1/lib/get_pid.c +--- shadow-4.11.1/lib/get_pid.c.newidmap-pid-fd 2022-01-03 01:46:53.000000000 +0100 ++++ shadow-4.11.1/lib/get_pid.c 2023-03-27 12:26:12.542834201 +0200 +@@ -10,6 +10,9 @@ + + #include "prototypes.h" + #include "defines.h" ++#include ++#include ++#include + + int get_pid (const char *pidstr, pid_t *pid) + { +@@ -29,3 +32,69 @@ int get_pid (const char *pidstr, pid_t * + return 1; + } + ++/* ++ * If use passed in fd:4 as an argument, then return the ++ * value '4', the fd to use. ++ * On error, return -1. ++ */ ++int get_pidfd_from_fd(const char *pidfdstr) ++{ ++ long long int val; ++ char *endptr; ++ struct stat st; ++ dev_t proc_st_dev, proc_st_rdev; ++ ++ errno = 0; ++ val = strtoll (pidfdstr, &endptr, 10); ++ if ( ('\0' == *pidfdstr) ++ || ('\0' != *endptr) ++ || (ERANGE == errno) ++ || (/*@+longintegral@*/val != (pid_t)val)/*@=longintegral@*/) { ++ return -1; ++ } ++ ++ if (stat("/proc/self/uid_map", &st) < 0) { ++ return -1; ++ } ++ ++ proc_st_dev = st.st_dev; ++ proc_st_rdev = st.st_rdev; ++ ++ if (fstat(val, &st) < 0) { ++ return -1; ++ } ++ ++ if (st.st_dev != proc_st_dev || st.st_rdev != proc_st_rdev) { ++ return -1; ++ } ++ ++ return (int)val; ++} ++ ++int open_pidfd(const char *pidstr) ++{ ++ int proc_dir_fd; ++ int written; ++ char proc_dir_name[32]; ++ pid_t target; ++ ++ if (get_pid(pidstr, &target) == 0) ++ return -ENOENT; ++ ++ /* max string length is 6 + 10 + 1 + 1 = 18, allocate 32 bytes */ ++ written = snprintf(proc_dir_name, sizeof(proc_dir_name), "/proc/%u/", ++ target); ++ if ((written <= 0) || ((size_t)written >= sizeof(proc_dir_name))) { ++ fprintf(stderr, "snprintf of proc path failed for %u: %s\n", ++ target, strerror(errno)); ++ return -EINVAL; ++ } ++ ++ proc_dir_fd = open(proc_dir_name, O_DIRECTORY); ++ if (proc_dir_fd < 0) { ++ fprintf(stderr, _("Could not open proc directory for target %u: %s\n"), ++ target, strerror(errno)); ++ return -EINVAL; ++ } ++ return proc_dir_fd; ++} +diff -up shadow-4.11.1/lib/prototypes.h.newidmap-pid-fd shadow-4.11.1/lib/prototypes.h +diff -up shadow-4.11.1/man/newgidmap.1.xml.newidmap-pid-fd shadow-4.11.1/man/newgidmap.1.xml +--- shadow-4.11.1/man/newgidmap.1.xml.newidmap-pid-fd 2022-01-03 01:46:53.000000000 +0100 ++++ shadow-4.11.1/man/newgidmap.1.xml 2023-03-27 12:26:12.541834195 +0200 +@@ -116,6 +116,17 @@ + + Note that newgidmap may be used only once for a given process. + ++ ++ Instead of an integer process id, the first argument may be ++ specified as fd:N, where the integer N ++ is the file descriptor number for the calling process's opened ++ file for /proc/[pid[. In this case, ++ newgidmap will use ++ openat2 ++ to open the gid_map file under that ++ directory, avoiding a TOCTTOU in case the process exits and ++ the pid is immediately reused. ++ + + + +diff -up shadow-4.11.1/man/newuidmap.1.xml.newidmap-pid-fd shadow-4.11.1/man/newuidmap.1.xml +--- shadow-4.11.1/man/newuidmap.1.xml.newidmap-pid-fd 2022-01-03 01:46:53.000000000 +0100 ++++ shadow-4.11.1/man/newuidmap.1.xml 2023-03-27 12:26:12.541834195 +0200 +@@ -116,6 +116,17 @@ + + Note that newuidmap may be used only once for a given process. + ++ ++ Instead of an integer process id, the first argument may be ++ specified as fd:N, where the integer N ++ is the file descriptor number for the calling process's opened ++ file for /proc/[pid[. In this case, ++ newuidmap will use ++ openat2 ++ to open the uid_map file under that ++ directory, avoiding a TOCTTOU in case the process exits and ++ the pid is immediately reused. ++ + + + +diff -up shadow-4.11.1/src/newgidmap.c.newidmap-pid-fd shadow-4.11.1/src/newgidmap.c +--- shadow-4.11.1/src/newgidmap.c.newidmap-pid-fd 2022-01-03 01:46:53.000000000 +0100 ++++ shadow-4.11.1/src/newgidmap.c 2023-03-27 12:26:12.541834195 +0200 +@@ -69,7 +69,7 @@ static void verify_ranges(struct passwd + + static void usage(void) + { +- fprintf(stderr, _("usage: %s [ ] ... \n"), Prog); ++ fprintf(stderr, _("usage: %s [] [ ] ... \n"), Prog); + exit(EXIT_FAILURE); + } + +@@ -142,15 +142,12 @@ out: + */ + int main(int argc, char **argv) + { +- char proc_dir_name[32]; + char *target_str; +- pid_t target; + int proc_dir_fd; + int ranges; + struct map_range *mappings; + struct stat st; + struct passwd *pw; +- int written; + bool allow_setgroups = false; + + Prog = Basename (argv[0]); +@@ -167,25 +164,19 @@ int main(int argc, char **argv) + /* Find the process that needs its user namespace + * gid mapping set. + */ +- target_str = argv[1]; +- if (!get_pid(target_str, &target)) +- usage(); + +- /* max string length is 6 + 10 + 1 + 1 = 18, allocate 32 bytes */ +- written = snprintf(proc_dir_name, sizeof(proc_dir_name), "/proc/%u/", +- target); +- if ((written <= 0) || (written >= sizeof(proc_dir_name))) { +- fprintf(stderr, "%s: snprintf of proc path failed: %s\n", +- Prog, strerror(errno)); +- } +- +- proc_dir_fd = open(proc_dir_name, O_DIRECTORY); +- if (proc_dir_fd < 0) { +- fprintf(stderr, _("%s: Could not open proc directory for target %u\n"), +- Prog, target); +- return EXIT_FAILURE; ++ target_str = argv[1]; ++ if (strlen(target_str) > 3 && strncmp(target_str, "fd:", 3) == 0) { ++ /* the user passed in a /proc/pid fd for the process */ ++ target_str = &target_str[3]; ++ proc_dir_fd = get_pidfd_from_fd(target_str); ++ if (proc_dir_fd < 0) ++ usage(); ++ } else { ++ proc_dir_fd = open_pidfd(target_str); ++ if (proc_dir_fd < 0) ++ usage(); + } +- + /* Who am i? */ + pw = get_my_pwent (); + if (NULL == pw) { +@@ -199,8 +190,8 @@ int main(int argc, char **argv) + + /* Get the effective uid and effective gid of the target process */ + if (fstat(proc_dir_fd, &st) < 0) { +- fprintf(stderr, _("%s: Could not stat directory for target %u\n"), +- Prog, target); ++ fprintf(stderr, _("%s: Could not stat directory for process\n"), ++ Prog); + return EXIT_FAILURE; + } + +@@ -212,8 +203,8 @@ int main(int argc, char **argv) + (!getdef_bool("GRANT_AUX_GROUP_SUBIDS") && (getgid() != pw->pw_gid)) || + (pw->pw_uid != st.st_uid) || + (getgid() != st.st_gid)) { +- fprintf(stderr, _( "%s: Target %u is owned by a different user: uid:%lu pw_uid:%lu st_uid:%lu, gid:%lu pw_gid:%lu st_gid:%lu\n" ), +- Prog, target, ++ fprintf(stderr, _( "%s: Target process is owned by a different user: uid:%lu pw_uid:%lu st_uid:%lu, gid:%lu pw_gid:%lu st_gid:%lu\n" ), ++ Prog, + (unsigned long int)getuid(), (unsigned long int)pw->pw_uid, (unsigned long int)st.st_uid, + (unsigned long int)getgid(), (unsigned long int)pw->pw_gid, (unsigned long int)st.st_gid); + return EXIT_FAILURE; +diff -up shadow-4.11.1/src/newuidmap.c.newidmap-pid-fd shadow-4.11.1/src/newuidmap.c +--- shadow-4.11.1/src/newuidmap.c.newidmap-pid-fd 2022-01-03 01:46:53.000000000 +0100 ++++ shadow-4.11.1/src/newuidmap.c 2023-03-27 12:28:03.405496897 +0200 +@@ -64,7 +64,7 @@ static void verify_ranges(struct passwd + + void usage(void) + { +- fprintf(stderr, _("usage: %s [ ] ... \n"), Prog); ++ fprintf(stderr, _("usage: %s [|fd:] [ ] ... \n"), Prog); + exit(EXIT_FAILURE); + } + +@@ -73,15 +73,12 @@ void usage(void) + */ + int main(int argc, char **argv) + { +- char proc_dir_name[32]; + char *target_str; +- pid_t target; + int proc_dir_fd; + int ranges; + struct map_range *mappings; + struct stat st; + struct passwd *pw; +- int written; + + Prog = Basename (argv[0]); + log_set_progname(Prog); +@@ -94,26 +91,20 @@ int main(int argc, char **argv) + if (argc < 2) + usage(); + ++ target_str = argv[1]; + /* Find the process that needs its user namespace + * uid mapping set. + */ +- target_str = argv[1]; +- if (!get_pid(target_str, &target)) +- usage(); +- +- /* max string length is 6 + 10 + 1 + 1 = 18, allocate 32 bytes */ +- written = snprintf(proc_dir_name, sizeof(proc_dir_name), "/proc/%u/", +- target); +- if ((written <= 0) || (written >= sizeof(proc_dir_name))) { +- fprintf(stderr, "%s: snprintf of proc path failed: %s\n", +- Prog, strerror(errno)); +- } +- +- proc_dir_fd = open(proc_dir_name, O_DIRECTORY); +- if (proc_dir_fd < 0) { +- fprintf(stderr, _("%s: Could not open proc directory for target %u\n"), +- Prog, target); +- return EXIT_FAILURE; ++ if (strlen(target_str) > 3 && strncmp(target_str, "fd:", 3) == 0) { ++ /* the user passed in a /proc/pid fd for the process */ ++ target_str = &target_str[3]; ++ proc_dir_fd = get_pidfd_from_fd(target_str); ++ if (proc_dir_fd < 0) ++ usage(); ++ } else { ++ proc_dir_fd = open_pidfd(target_str); ++ if (proc_dir_fd < 0) ++ usage(); + } + + /* Who am i? */ +@@ -129,8 +120,7 @@ int main(int argc, char **argv) + + /* Get the effective uid and effective gid of the target process */ + if (fstat(proc_dir_fd, &st) < 0) { +- fprintf(stderr, _("%s: Could not stat directory for target %u\n"), +- Prog, target); ++ fprintf(stderr, _("%s: Could not stat directory for target process\n"), Prog); + return EXIT_FAILURE; + } + +@@ -142,8 +132,8 @@ int main(int argc, char **argv) + (!getdef_bool("GRANT_AUX_GROUP_SUBIDS") && (getgid() != pw->pw_gid)) || + (pw->pw_uid != st.st_uid) || + (getgid() != st.st_gid)) { +- fprintf(stderr, _( "%s: Target process %u is owned by a different user: uid:%lu pw_uid:%lu st_uid:%lu, gid:%lu pw_gid:%lu st_gid:%lu\n" ), +- Prog, target, ++ fprintf(stderr, _( "%s: Target process is owned by a different user: uid:%lu pw_uid:%lu st_uid:%lu, gid:%lu pw_gid:%lu st_gid:%lu\n" ), ++ Prog, + (unsigned long int)getuid(), (unsigned long int)pw->pw_uid, (unsigned long int)st.st_uid, + (unsigned long int)getgid(), (unsigned long int)pw->pw_gid, (unsigned long int)st.st_gid); + return EXIT_FAILURE; diff --git a/shadow-4.11.1-subordinateio-also-compare-the-owner-ID.patch b/shadow-4.11.1-subordinateio-also-compare-the-owner-ID.patch new file mode 100644 index 0000000..e761de0 --- /dev/null +++ b/shadow-4.11.1-subordinateio-also-compare-the-owner-ID.patch @@ -0,0 +1,108 @@ +From 3ec32f9975f262073f8fbdecd2bfaee4a1d3db48 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Wed, 13 Jul 2022 09:55:14 +0200 +Subject: [PATCH] subordinateio: also compare the owner ID + +IDs already populate /etc/subuid and /etc/subgid files so it's necessary +not only to check for the owner name but also for the owner ID of a +given range. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2093311 + +Signed-off-by: Iker Pedrosa +--- + lib/subordinateio.c | 50 +++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 50 insertions(+) + +diff --git a/lib/subordinateio.c b/lib/subordinateio.c +index 9ca70b8b..6bc45283 100644 +--- a/lib/subordinateio.c ++++ b/lib/subordinateio.c +@@ -17,6 +17,8 @@ + #include + #include + ++#define ID_SIZE 31 ++ + /* + * subordinate_dup: create a duplicate range + * +@@ -745,6 +747,40 @@ gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count) + return start == ULONG_MAX ? (gid_t) -1 : start; + } + ++static bool get_owner_id(const char *owner, enum subid_type id_type, char *id) ++{ ++ struct passwd *pw; ++ struct group *gr; ++ int ret = 0; ++ ++ switch (id_type) { ++ case ID_TYPE_UID: ++ pw = getpwnam(owner); ++ if (pw == NULL) { ++ return false; ++ } ++ ret = snprintf(id, ID_SIZE, "%u", pw->pw_uid); ++ if (ret < 0 || ret >= ID_SIZE) { ++ return false; ++ } ++ break; ++ case ID_TYPE_GID: ++ gr = getgrnam(owner); ++ if (gr == NULL) { ++ return false; ++ } ++ ret = snprintf(id, ID_SIZE, "%u", gr->gr_gid); ++ if (ret < 0 || ret >= ID_SIZE) { ++ return false; ++ } ++ break; ++ default: ++ return false; ++ } ++ ++ return true; ++} ++ + /* + * int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges) + * +@@ -770,6 +806,8 @@ int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_r + enum subid_status status; + int count = 0; + struct subid_nss_ops *h; ++ char id[ID_SIZE]; ++ bool have_owner_id; + + *in_ranges = NULL; + +@@ -798,6 +836,8 @@ int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_r + return -1; + } + ++ have_owner_id = get_owner_id(owner, id_type, id); ++ + commonio_rewind(db); + while ((range = commonio_next(db)) != NULL) { + if (0 == strcmp(range->owner, owner)) { +@@ -808,6 +848,16 @@ int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_r + goto out; + } + } ++ ++ // Let's also compare with the ID ++ if (have_owner_id == true && 0 == strcmp(range->owner, id)) { ++ if (!append_range(&ranges, range, count++)) { ++ free(ranges); ++ ranges = NULL; ++ count = -1; ++ goto out; ++ } ++ } + } + + out: +-- +2.37.3 + diff --git a/shadow-4.11.1-useradd-modify-check-ID-range-for-system-users.patch b/shadow-4.11.1-useradd-modify-check-ID-range-for-system-users.patch new file mode 100644 index 0000000..dead628 --- /dev/null +++ b/shadow-4.11.1-useradd-modify-check-ID-range-for-system-users.patch @@ -0,0 +1,40 @@ +From f1f1678e13aa3ae49bdb139efaa2c5bc53dcfe92 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Tue, 4 Jan 2022 13:06:00 +0100 +Subject: [PATCH] useradd: modify check ID range for system users + +useradd warns that a system user ID less than SYS_UID_MIN is outside the +expected range, even though that ID has been specifically selected with +the "-u" option. + +In my opinion all the user ID's below SYS_UID_MAX are for the system, +thus I change the condition to take that into account. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2004911 + +Signed-off-by: Iker Pedrosa +--- + src/useradd.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/src/useradd.c b/src/useradd.c +index 34376fa5..4c71c38a 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -2409,11 +2409,9 @@ static void check_uid_range(int rflg, uid_t user_id) + uid_t uid_min ; + uid_t uid_max ; + if (rflg) { +- uid_min = (uid_t)getdef_ulong("SYS_UID_MIN",101UL); + uid_max = (uid_t)getdef_ulong("SYS_UID_MAX",getdef_ulong("UID_MIN",1000UL)-1); +- if (uid_min <= uid_max) { +- if (user_id < uid_min || user_id >uid_max) +- fprintf(stderr, _("%s warning: %s's uid %d outside of the SYS_UID_MIN %d and SYS_UID_MAX %d range.\n"), Prog, user_name, user_id, uid_min, uid_max); ++ if (user_id > uid_max) { ++ fprintf(stderr, _("%s warning: %s's uid %d is greater than SYS_UID_MAX %d\n"), Prog, user_name, user_id, uid_max); + } + }else{ + uid_min = (uid_t)getdef_ulong("UID_MIN", 1000UL); +-- +2.37.1 + diff --git a/shadow-4.11.1-useradd-stop-last-fail-log-reset.patch b/shadow-4.11.1-useradd-stop-last-fail-log-reset.patch new file mode 100644 index 0000000..8662672 --- /dev/null +++ b/shadow-4.11.1-useradd-stop-last-fail-log-reset.patch @@ -0,0 +1,42 @@ +From ebf9b232b012725d2be5e750876c7336cf1c37fd Mon Sep 17 00:00:00 2001 +From: David Kalnischkies +Date: Wed, 24 Aug 2022 13:21:01 +0200 +Subject: [PATCH] useradd: Do not reset non-existent data in {last,fail}log + +useradd does not create the files if they don't exist, but if they exist +it will reset user data even if the data did not exist before creating +a hole and an explicitly zero'd data point resulting (especially for +high UIDs) in a lot of zeros ending up in containers and tarballs. +--- + src/useradd.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/useradd.c b/src/useradd.c +index 6eaeb533..39a744ee 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -1996,8 +1996,9 @@ static void faillog_reset (uid_t uid) + struct faillog fl; + int fd; + off_t offset_uid = (off_t) (sizeof fl) * uid; ++ struct stat st; + +- if (access (FAILLOG_FILE, F_OK) != 0) { ++ if (stat (FAILLOG_FILE, &st) != 0 || st.st_size <= offset_uid) { + return; + } + +@@ -2033,8 +2034,9 @@ static void lastlog_reset (uid_t uid) + int fd; + off_t offset_uid = (off_t) (sizeof ll) * uid; + uid_t max_uid; ++ struct stat st; + +- if (access (LASTLOG_FILE, F_OK) != 0) { ++ if (stat (LASTLOG_FILE, &st) != 0 || st.st_size <= offset_uid) { + return; + } + +-- +2.37.3 + diff --git a/shadow-utils.spec b/shadow-utils.spec index c90978b..1d20919 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.11.1 -Release: 2%{?dist} +Release: 6%{?dist} Epoch: 2 License: BSD and GPLv2+ URL: https://github.com/shadow-maint/shadow @@ -45,6 +45,16 @@ Patch12: shadow-4.6-sysugid-min-limit.patch Patch13: shadow-4.8-ignore-login-prompt.patch # https://github.com/shadow-maint/shadow/commit/e101219ad71de11da3fdd1b3ec2620fd1a97b92c Patch14: shadow-4.9-nss-get-shadow-logfd-with-log-get-logfd.patch +# https://github.com/shadow-maint/shadow/commit/f1f1678e13aa3ae49bdb139efaa2c5bc53dcfe92 +Patch15: shadow-4.11.1-useradd-modify-check-ID-range-for-system-users.patch +# https://github.com/shadow-maint/shadow/commit/3ec32f9975f262073f8fbdecd2bfaee4a1d3db48 +Patch16: shadow-4.11.1-subordinateio-also-compare-the-owner-ID.patch +# https://github.com/shadow-maint/shadow/commit/ebf9b232b012725d2be5e750876c7336cf1c37fd +Patch17: shadow-4.11.1-useradd-stop-last-fail-log-reset.patch +# https://github.com/shadow-maint/shadow/commit/6974df39a708abf8bafbdfa2b7827e0f70f874cb +# https://github.com/shadow-maint/shadow/commit/7ff33fae6f9cd79c0e012671c37a172e9a681d0b +# https://github.com/shadow-maint/shadow/commit/05e2adf509ba0e3779dae66a276b86927a8e1e0e +Patch18: shadow-4.11.1-newidmap-support-passing-pid-as-fd.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -119,6 +129,10 @@ Development files for shadow-utils-subid. %patch12 -p1 -b .sysugid-min-limit %patch13 -p1 -b .login-prompt %patch14 -p1 -b .nss-get-shadow-logfd-with-log-get-logfd +%patch15 -p1 -b .useradd-modify-check-ID-range-for-system-users +%patch16 -p1 -b .subordinateio-also-compare-the-owner-ID +%patch17 -p1 -b .useradd-stop-last-fail-log-reset +%patch18 -p1 -b .newidmap-pid-fd iconv -f ISO88591 -t utf-8 doc/HOWTO > doc/HOWTO.utf8 cp -f doc/HOWTO.utf8 doc/HOWTO @@ -292,6 +306,18 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Mon Mar 27 2023 Iker Pedrosa - 2:4.11.1-6 +- newuidmap and newgidmap: support passing pid as fd. Resolves: #2174752 + +* Mon Sep 26 2022 Iker Pedrosa - 2:4.11.1-5 +- useradd: Do not reset non-existent data in {last,fail}log + +* Wed Sep 14 2022 Iker Pedrosa - 2:4.11.1-4 +- subordinateio: also compare the owner ID. Resolves: #2118227 + +* Fri Aug 5 2022 Iker Pedrosa - 2:4.11.1-3 +- useradd: modify check ID range for system users. Resolves: #2093692 + * Thu Feb 10 2022 Iker Pedrosa - 2:4.11.1-2 - Fix explicit subid requirement for subid-devel