diff --git a/backort-useradd-Fix-buffer-overflow-when-using-a-prefix.patch b/backort-useradd-Fix-buffer-overflow-when-using-a-prefix.patch new file mode 100644 index 0000000000000000000000000000000000000000..76ff976d8e3a82de75255aa05bdd35719d739579 --- /dev/null +++ b/backort-useradd-Fix-buffer-overflow-when-using-a-prefix.patch @@ -0,0 +1,29 @@ +From eaebea55a495a56317ed85e959b3599f73c6bdf2 Mon Sep 17 00:00:00 2001 +From: David Michael +Date: Sun, 23 Oct 2022 18:51:33 -0400 +Subject: [PATCH] useradd: Fix buffer overflow when using a prefix + +The buffer length did not count the string's trailing null byte. + +Signed-off-by: David Michael + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/f6f8bcd2a57c06983296485cc028ebdf467ebfd7 + +--- + src/useradd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/useradd.c b/src/useradd.c +index 39a744ee0..7ea0a9c4d 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -2372,7 +2372,7 @@ static void create_mail (void) + if (NULL == spool) { + return; + } +- file = alloca (strlen (prefix) + strlen (spool) + strlen (user_name) + 2); ++ file = alloca (strlen (prefix) + strlen (spool) + strlen (user_name) + 3); + if (prefix[0]) + sprintf (file, "%s/%s/%s", prefix, spool, user_name); + else diff --git a/backport-Added-documentation-around-CREATE_MAIL_SPOOL.patch b/backport-Added-documentation-around-CREATE_MAIL_SPOOL.patch new file mode 100644 index 0000000000000000000000000000000000000000..8cb1e698fe04f36a8666b7c0163313e23c42b618 --- /dev/null +++ b/backport-Added-documentation-around-CREATE_MAIL_SPOOL.patch @@ -0,0 +1,28 @@ +From aff4989d1acf3afc718813144658c295d8d10f20 Mon Sep 17 00:00:00 2001 +From: Andy Zaugg +Date: Mon, 20 Sep 2021 20:41:50 -0700 +Subject: [PATCH] Added documentation around CREATE_MAIL_SPOOL + +Adding documentation aroud the parameter CREATE_MAIL_SPOOL in the +/etc/default/useradd file + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/aff4989d1acf3afc718813144658c295d8d10f20 + +--- + man/login.defs.d/MAIL_DIR.xml | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/man/login.defs.d/MAIL_DIR.xml b/man/login.defs.d/MAIL_DIR.xml +index 60b82d6b1..b5adb888b 100644 +--- a/man/login.defs.d/MAIL_DIR.xml ++++ b/man/login.defs.d/MAIL_DIR.xml +@@ -35,6 +35,8 @@ + The mail spool directory. This is needed to manipulate the mailbox + when its corresponding user account is modified or deleted. If not + specified, a compile-time default is used. ++ The parameter CREATE_MAIL_SPOOL in /etc/default/useradd ++ determines whether the mail spool should be created. + + + diff --git a/backport-Address-minor-compiler-warnings.patch b/backport-Address-minor-compiler-warnings.patch new file mode 100644 index 0000000000000000000000000000000000000000..9913e068e675cda8a4079840bc7fbec80a12e851 --- /dev/null +++ b/backport-Address-minor-compiler-warnings.patch @@ -0,0 +1,46 @@ +From 6cbec2d0aa29d6d25e9eed007ded4e79eb637519 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= +Date: Fri, 5 Aug 2022 17:57:29 +0200 +Subject: [PATCH] Address minor compiler warnings + + copydir.c:666:44: warning: unsigned conversion from 'int' to '__mode_t' {aka 'unsigned int'} changes value from '-4096' to '4294963200' [-Wsign-conversion] + 666 | if ( (mknod (dst, statp->st_mode & ~07777, statp->st_rdev) != 0) + | ^ + + copydir.c:116:1: warning: missing initializer for field 'quote' of 'struct error_context' [-Wmissing-field-initializers] + 116 | }; + | ^ + In file included from copydir.c:27: + /usr/include/attr/error_context.h:30:23: note: 'quote' declared here + 30 | const char *(*quote) (struct error_context *, const char *); + | ^~~~~ + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/6cbec2d0aa29d6d25e9eed007ded4e79eb637519 + +--- + libmisc/copydir.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/libmisc/copydir.c b/libmisc/copydir.c +index 95042187b..e753d7cf0 100644 +--- a/libmisc/copydir.c ++++ b/libmisc/copydir.c +@@ -112,7 +112,7 @@ static void error_acl (unused struct error_context *ctx, const char *fmt, ...) + } + + static struct error_context ctx = { +- error_acl ++ error_acl, NULL, NULL + }; + #endif /* WITH_ACL || WITH_ATTR */ + +@@ -663,7 +663,7 @@ static int copy_special (const char *src, const char *dst, + } + #endif /* WITH_SELINUX */ + +- if ( (mknod (dst, statp->st_mode & ~07777, statp->st_rdev) != 0) ++ if ( (mknod (dst, statp->st_mode & ~07777U, statp->st_rdev) != 0) + || (chown_if_needed (dst, statp, + old_uid, new_uid, old_gid, new_gid) != 0) + #ifdef WITH_ACL diff --git a/backport-Avoid-races-in-chown_tree.patch b/backport-Avoid-races-in-chown_tree.patch new file mode 100644 index 0000000000000000000000000000000000000000..9484aa45a597310e32317fc8629f73027aa72c5e --- /dev/null +++ b/backport-Avoid-races-in-chown_tree.patch @@ -0,0 +1,228 @@ +From e9ae247cb14f977d8881f481488843b10665dba8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= +Date: Fri, 5 Aug 2022 17:57:19 +0200 +Subject: [PATCH] Avoid races in chown_tree() + +Use *at() functions to pin the directory operating in to avoid being +redirected by unprivileged users replacing parts of paths by symlinks to +privileged files. + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/e9ae247cb14f977d8881f481488843b10665dba8 + +--- + libmisc/chowndir.c | 130 +++++++++++++++++---------------------------- + 1 file changed, 49 insertions(+), 81 deletions(-) + +diff --git a/libmisc/chowndir.c b/libmisc/chowndir.c +index 0edc3b609..d31618a56 100644 +--- a/libmisc/chowndir.c ++++ b/libmisc/chowndir.c +@@ -17,45 +17,28 @@ + #include "defines.h" + #include + #include +-/* +- * chown_tree - change ownership of files in a directory tree +- * +- * chown_dir() walks a directory tree and changes the ownership +- * of all files owned by the provided user ID. +- * +- * Only files owned (resp. group-owned) by old_uid (resp. by old_gid) +- * will have their ownership (resp. group-ownership) modified, unless +- * old_uid (resp. old_gid) is set to -1. +- * +- * new_uid and new_gid can be set to -1 to indicate that no owner or +- * group-owner shall be changed. +- */ +-int chown_tree (const char *root, ++#include ++ ++static int chown_tree_at (int at_fd, ++ const char *path, + uid_t old_uid, + uid_t new_uid, + gid_t old_gid, + gid_t new_gid) + { +- char *new_name; +- size_t new_name_len; +- int rc = 0; +- struct dirent *ent; +- struct stat sb; + DIR *dir; ++ const struct dirent *ent; ++ struct stat dir_sb; ++ int dir_fd, rc = 0; + +- new_name = malloc (1024); +- if (NULL == new_name) { ++ dir_fd = openat (at_fd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); ++ if (dir_fd < 0) { + return -1; + } +- new_name_len = 1024; + +- /* +- * Make certain the directory exists. This routine is called +- * directly by the invoker, or recursively. +- */ +- +- if (access (root, F_OK) != 0) { +- free (new_name); ++ dir = fdopendir (dir_fd); ++ if (!dir) { ++ (void) close (dir_fd); + return -1; + } + +@@ -65,68 +48,34 @@ int chown_tree (const char *root, + * recursively. If not, it is checked to see if an ownership + * shall be changed. + */ +- +- dir = opendir (root); +- if (NULL == dir) { +- free (new_name); +- return -1; +- } +- + while ((ent = readdir (dir))) { +- size_t ent_name_len; + uid_t tmpuid = (uid_t) -1; + gid_t tmpgid = (gid_t) -1; ++ struct stat ent_sb; + + /* + * Skip the "." and ".." entries + */ +- + if ( (strcmp (ent->d_name, ".") == 0) + || (strcmp (ent->d_name, "..") == 0)) { + continue; + } + +- /* +- * Make the filename for both the source and the +- * destination files. +- */ +- +- ent_name_len = strlen (root) + strlen (ent->d_name) + 2; +- if (ent_name_len > new_name_len) { +- /*@only@*/char *tmp = realloc (new_name, ent_name_len); +- if (NULL == tmp) { +- rc = -1; +- break; +- } +- new_name = tmp; +- new_name_len = ent_name_len; +- } +- +- (void) snprintf (new_name, new_name_len, "%s/%s", root, ent->d_name); +- +- /* Don't follow symbolic links! */ +- if (LSTAT (new_name, &sb) == -1) { +- continue; ++ rc = fstatat (dirfd(dir), ent->d_name, &ent_sb, AT_SYMLINK_NOFOLLOW); ++ if (rc < 0) { ++ break; + } + +- if (S_ISDIR (sb.st_mode) && !S_ISLNK (sb.st_mode)) { +- ++ if (S_ISDIR (ent_sb.st_mode)) { + /* + * Do the entire subdirectory. + */ +- +- rc = chown_tree (new_name, old_uid, new_uid, +- old_gid, new_gid); ++ rc = chown_tree_at (dirfd(dir), ent->d_name, old_uid, new_uid, old_gid, new_gid); + if (0 != rc) { + break; + } + } +-#ifndef HAVE_LCHOWN +- /* don't use chown (follows symbolic links!) */ +- if (S_ISLNK (sb.st_mode)) { +- continue; +- } +-#endif ++ + /* + * By default, the IDs are not changed (-1). + * +@@ -136,43 +85,62 @@ int chown_tree (const char *root, + * If the file is not group-owned by the group, the + * group-owner is not changed. + */ +- if (((uid_t) -1 == old_uid) || (sb.st_uid == old_uid)) { ++ if (((uid_t) -1 == old_uid) || (ent_sb.st_uid == old_uid)) { + tmpuid = new_uid; + } +- if (((gid_t) -1 == old_gid) || (sb.st_gid == old_gid)) { ++ if (((gid_t) -1 == old_gid) || (ent_sb.st_gid == old_gid)) { + tmpgid = new_gid; + } + if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) { +- rc = LCHOWN (new_name, tmpuid, tmpgid); ++ rc = fchownat (dirfd(dir), ent->d_name, tmpuid, tmpgid, AT_SYMLINK_NOFOLLOW); + if (0 != rc) { + break; + } + } + } + +- free (new_name); +- (void) closedir (dir); +- + /* + * Now do the root of the tree + */ +- +- if ((0 == rc) && (stat (root, &sb) == 0)) { ++ if ((0 == rc) && (fstat (dirfd(dir), &dir_sb) == 0)) { + uid_t tmpuid = (uid_t) -1; + gid_t tmpgid = (gid_t) -1; +- if (((uid_t) -1 == old_uid) || (sb.st_uid == old_uid)) { ++ if (((uid_t) -1 == old_uid) || (dir_sb.st_uid == old_uid)) { + tmpuid = new_uid; + } +- if (((gid_t) -1 == old_gid) || (sb.st_gid == old_gid)) { ++ if (((gid_t) -1 == old_gid) || (dir_sb.st_gid == old_gid)) { + tmpgid = new_gid; + } + if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) { +- rc = LCHOWN (root, tmpuid, tmpgid); ++ rc = fchown (dirfd(dir), tmpuid, tmpgid); + } + } else { + rc = -1; + } + ++ (void) closedir (dir); ++ + return rc; + } + ++/* ++ * chown_tree - change ownership of files in a directory tree ++ * ++ * chown_dir() walks a directory tree and changes the ownership ++ * of all files owned by the provided user ID. ++ * ++ * Only files owned (resp. group-owned) by old_uid (resp. by old_gid) ++ * will have their ownership (resp. group-ownership) modified, unless ++ * old_uid (resp. old_gid) is set to -1. ++ * ++ * new_uid and new_gid can be set to -1 to indicate that no owner or ++ * group-owner shall be changed. ++ */ ++int chown_tree (const char *root, ++ uid_t old_uid, ++ uid_t new_uid, ++ gid_t old_gid, ++ gid_t new_gid) ++{ ++ return chown_tree_at (AT_FDCWD, root, old_uid, new_uid, old_gid, new_gid); ++} diff --git a/backport-Avoid-races-in-copy_tree.patch b/backport-Avoid-races-in-copy_tree.patch new file mode 100644 index 0000000000000000000000000000000000000000..ab2cc178f9c5911a35c18da84e67043151556702 --- /dev/null +++ b/backport-Avoid-races-in-copy_tree.patch @@ -0,0 +1,657 @@ +From faeab50e710131816b261de66141524898c2c487 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= +Date: Fri, 5 Aug 2022 17:57:32 +0200 +Subject: [PATCH] Avoid races in copy_tree() + +Use *at() functions to pin the directory operating in to avoid being +redirected by unprivileged users replacing parts of paths by symlinks to +privileged files. + +Introduce a path_info struct with the full path and dirfd and name +information for *at() functions, since the full path is needed for link +resolution, SELinux label lookup and ACL attributes. + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/faeab50e710131816b261de66141524898c2c487 + +--- + libmisc/copydir.c | 330 ++++++++++++++++++++++++++++++---------------- + 1 file changed, 218 insertions(+), 112 deletions(-) + +diff --git a/libmisc/copydir.c b/libmisc/copydir.c +index e753d7cf0..5605f6fe0 100644 +--- a/libmisc/copydir.c ++++ b/libmisc/copydir.c +@@ -47,40 +47,43 @@ struct link_name { + }; + static /*@exposed@*/struct link_name *links; + +-static int copy_entry (const char *src, const char *dst, ++struct path_info { ++ const char *full_path; ++ int dirfd; ++ const char *name; ++}; ++ ++static int copy_entry (const struct path_info *src, const struct path_info *dst, + bool reset_selinux, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +-static int copy_dir (const char *src, const char *dst, ++static int copy_dir (const struct path_info *src, const struct path_info *dst, + bool reset_selinux, +- const struct stat *statp, const struct timeval mt[], ++ const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); + static /*@null@*/char *readlink_malloc (const char *filename); +-static int copy_symlink (const char *src, const char *dst, ++static int copy_symlink (const struct path_info *src, const struct path_info *dst, + unused bool reset_selinux, +- const struct stat *statp, const struct timeval mt[], ++ const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +-static int copy_hardlink (const char *dst, ++static int copy_hardlink (const struct path_info *dst, + unused bool reset_selinux, + struct link_name *lp); +-static int copy_special (const char *src, const char *dst, ++static int copy_special (const struct path_info *src, const struct path_info *dst, + bool reset_selinux, +- const struct stat *statp, const struct timeval mt[], ++ const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +-static int copy_file (const char *src, const char *dst, ++static int copy_file (const struct path_info *src, const struct path_info *dst, + bool reset_selinux, +- const struct stat *statp, const struct timeval mt[], ++ const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +-static int chown_if_needed (const char *dst, const struct stat *statp, ++static int chownat_if_needed (const struct path_info *dst, const struct stat *statp, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +-static int lchown_if_needed (const char *dst, const struct stat *statp, +- uid_t old_uid, uid_t new_uid, +- gid_t old_gid, gid_t new_gid); + static int fchown_if_needed (int fdst, const struct stat *statp, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +@@ -116,6 +119,57 @@ static struct error_context ctx = { + }; + #endif /* WITH_ACL || WITH_ATTR */ + ++#ifdef WITH_ACL ++static int perm_copy_path(const struct path_info *src, ++ const struct path_info *dst, ++ struct error_context *errctx) ++{ ++ int src_fd, dst_fd, ret; ++ ++ src_fd = openat(src->dirfd, src->name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); ++ if (src_fd < 0) { ++ return -1; ++ } ++ ++ dst_fd = openat(dst->dirfd, dst->name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); ++ if (dst_fd < 0) { ++ (void) close (src_fd); ++ return -1; ++ } ++ ++ ret = perm_copy_fd(src->full_path, src_fd, dst->full_path, dst_fd, errctx); ++ (void) close (src_fd); ++ (void) close (dst_fd); ++ return ret; ++} ++#endif /* WITH_ACL */ ++ ++#ifdef WITH_ATTR ++static int attr_copy_path(const struct path_info *src, ++ const struct path_info *dst, ++ int (*callback) (const char *, struct error_context *), ++ struct error_context *errctx) ++{ ++ int src_fd, dst_fd, ret; ++ ++ src_fd = openat(src->dirfd, src->name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); ++ if (src_fd < 0) { ++ return -1; ++ } ++ ++ dst_fd = openat(dst->dirfd, dst->name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); ++ if (dst_fd < 0) { ++ (void) close (src_fd); ++ return -1; ++ } ++ ++ ret = attr_copy_fd(src->full_path, src_fd, dst->full_path, dst_fd, callback, errctx); ++ (void) close (src_fd); ++ (void) close (dst_fd); ++ return ret; ++} ++#endif /* WITH_ATTR */ ++ + /* + * remove_link - delete a link from the linked list + */ +@@ -188,51 +242,36 @@ static /*@exposed@*/ /*@null@*/struct link_name *check_link (const char *name, c + return NULL; + } + +-/* +- * copy_tree - copy files in a directory tree +- * +- * copy_tree() walks a directory tree and copies ordinary files +- * as it goes. +- * +- * When reset_selinux is enabled, extended attributes (and thus +- * SELinux attributes) are not copied. +- * +- * old_uid and new_uid are used to set the ownership of the copied +- * files. Unless old_uid is set to -1, only the files owned by +- * old_uid have their ownership changed to new_uid. In addition, if +- * new_uid is set to -1, no ownership will be changed. +- * +- * The same logic applies for the group-ownership and +- * old_gid/new_gid. +- */ +-int copy_tree (const char *src_root, const char *dst_root, ++static int copy_tree_impl (const struct path_info *src, const struct path_info *dst, + bool copy_root, bool reset_selinux, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) + { +- int err = 0; ++ int dst_fd, src_fd, err = 0; + bool set_orig = false; +- struct dirent *ent; ++ const struct dirent *ent; + DIR *dir; + + if (copy_root) { + struct stat sb; +- if (access (dst_root, F_OK) == 0) { ++ ++ if ( fstatat (dst->dirfd, dst->name, &sb, 0) == 0 ++ || errno != ENOENT) { + return -1; + } + +- if (lstat (src_root, &sb) == -1) { ++ if (fstatat (src->dirfd, src->name, &sb, AT_SYMLINK_NOFOLLOW) == -1) { + return -1; + } + + if (!S_ISDIR (sb.st_mode)) { + fprintf (log_get_logfd(), + "%s: %s is not a directory", +- log_get_progname(), src_root); ++ log_get_progname(), src->full_path); + return -1; + } + +- return copy_entry (src_root, dst_root, reset_selinux, ++ return copy_entry (src, dst, reset_selinux, + old_uid, new_uid, old_gid, new_gid); + } + +@@ -242,8 +281,14 @@ int copy_tree (const char *src_root, const char *dst_root, + * target is created. It assumes the target directory exists. + */ + +- if ( (access (src_root, F_OK) != 0) +- || (access (dst_root, F_OK) != 0)) { ++ src_fd = openat (src->dirfd, src->name, O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_CLOEXEC); ++ if (src_fd < 0) { ++ return -1; ++ } ++ ++ dst_fd = openat (dst->dirfd, dst->name, O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_CLOEXEC); ++ if (dst_fd < 0) { ++ (void) close (src_fd); + return -1; + } + +@@ -254,14 +299,16 @@ int copy_tree (const char *src_root, const char *dst_root, + * regular files (and directories ...) are copied, and no file + * is made set-ID. + */ +- dir = opendir (src_root); ++ dir = fdopendir (src_fd); + if (NULL == dir) { ++ (void) close (src_fd); ++ (void) close (dst_fd); + return -1; + } + + if (src_orig == NULL) { +- src_orig = src_root; +- dst_orig = dst_root; ++ src_orig = src->full_path; ++ dst_orig = dst->full_path; + set_orig = true; + } + while ((0 == err) && (ent = readdir (dir)) != NULL) { +@@ -274,8 +321,8 @@ int copy_tree (const char *src_root, const char *dst_root, + char *dst_name; + size_t src_len = strlen (ent->d_name) + 2; + size_t dst_len = strlen (ent->d_name) + 2; +- src_len += strlen (src_root); +- dst_len += strlen (dst_root); ++ src_len += strlen (src->full_path); ++ dst_len += strlen (dst->full_path); + + src_name = (char *) malloc (src_len); + dst_name = (char *) malloc (dst_len); +@@ -287,12 +334,22 @@ int copy_tree (const char *src_root, const char *dst_root, + * Build the filename for both the source and + * the destination files. + */ ++ struct path_info src_entry, dst_entry; ++ + (void) snprintf (src_name, src_len, "%s/%s", +- src_root, ent->d_name); ++ src->full_path, ent->d_name); + (void) snprintf (dst_name, dst_len, "%s/%s", +- dst_root, ent->d_name); ++ dst->full_path, ent->d_name); ++ ++ src_entry.full_path = src_name; ++ src_entry.dirfd = dirfd(dir); ++ src_entry.name = ent->d_name; + +- err = copy_entry (src_name, dst_name, ++ dst_entry.full_path = dst_name; ++ dst_entry.dirfd = dst_fd; ++ dst_entry.name = ent->d_name; ++ ++ err = copy_entry (&src_entry, &dst_entry, + reset_selinux, + old_uid, new_uid, + old_gid, new_gid); +@@ -306,6 +363,7 @@ int copy_tree (const char *src_root, const char *dst_root, + } + } + (void) closedir (dir); ++ (void) close (dst_fd); + + if (set_orig) { + src_orig = NULL; +@@ -352,7 +410,7 @@ int copy_tree (const char *src_root, const char *dst_root, + * old_gid) will be modified, unless old_uid (resp. old_gid) is set + * to -1. + */ +-static int copy_entry (const char *src, const char *dst, ++static int copy_entry (const struct path_info *src, const struct path_info *dst, + bool reset_selinux, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) +@@ -360,32 +418,32 @@ static int copy_entry (const char *src, const char *dst, + int err = 0; + struct stat sb; + struct link_name *lp; +- struct timeval mt[2]; ++ struct timespec mt[2]; + +- if (lstat (src, &sb) == -1) { ++ if (fstatat(src->dirfd, src->name, &sb, AT_SYMLINK_NOFOLLOW) == -1) { + /* If we cannot stat the file, do not care. */ + } else { + #ifdef HAVE_STRUCT_STAT_ST_ATIM + mt[0].tv_sec = sb.st_atim.tv_sec; +- mt[0].tv_usec = sb.st_atim.tv_nsec / 1000; ++ mt[0].tv_nsec = sb.st_atim.tv_nsec; + #else /* !HAVE_STRUCT_STAT_ST_ATIM */ + mt[0].tv_sec = sb.st_atime; + # ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC +- mt[0].tv_usec = sb.st_atimensec / 1000; ++ mt[0].tv_nsec = sb.st_atimensec; + # else /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */ +- mt[0].tv_usec = 0; ++ mt[0].tv_nsec = 0; + # endif /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */ + #endif /* !HAVE_STRUCT_STAT_ST_ATIM */ + + #ifdef HAVE_STRUCT_STAT_ST_MTIM + mt[1].tv_sec = sb.st_mtim.tv_sec; +- mt[1].tv_usec = sb.st_mtim.tv_nsec / 1000; ++ mt[1].tv_nsec = sb.st_mtim.tv_nsec; + #else /* !HAVE_STRUCT_STAT_ST_MTIM */ + mt[1].tv_sec = sb.st_mtime; + # ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC +- mt[1].tv_usec = sb.st_mtimensec / 1000; ++ mt[1].tv_nsec = sb.st_mtimensec; + # else /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */ +- mt[1].tv_usec = 0; ++ mt[1].tv_nsec = 0; + # endif /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */ + #endif /* !HAVE_STRUCT_STAT_ST_MTIM */ + +@@ -407,7 +465,7 @@ static int copy_entry (const char *src, const char *dst, + * See if this is a previously copied link + */ + +- else if ((lp = check_link (src, &sb)) != NULL) { ++ else if ((lp = check_link (src->full_path, &sb)) != NULL) { + err = copy_hardlink (dst, reset_selinux, lp); + } + +@@ -446,9 +504,9 @@ static int copy_entry (const char *src, const char *dst, + * + * Return 0 on success, -1 on error. + */ +-static int copy_dir (const char *src, const char *dst, ++static int copy_dir (const struct path_info *src, const struct path_info *dst, + bool reset_selinux, +- const struct stat *statp, const struct timeval mt[], ++ const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) + { +@@ -460,15 +518,15 @@ static int copy_dir (const char *src, const char *dst, + */ + + #ifdef WITH_SELINUX +- if (set_selinux_file_context (dst, S_IFDIR) != 0) { ++ if (set_selinux_file_context (dst->full_path, S_IFDIR) != 0) { + return -1; + } + #endif /* WITH_SELINUX */ +- if ( (mkdir (dst, statp->st_mode) != 0) +- || (chown_if_needed (dst, statp, ++ if ( (mkdirat (dst->dirfd, dst->name, statp->st_mode) != 0) ++ || (chownat_if_needed (dst, statp, + old_uid, new_uid, old_gid, new_gid) != 0) + #ifdef WITH_ACL +- || ( (perm_copy_file (src, dst, &ctx) != 0) ++ || ( (perm_copy_path (src, dst, &ctx) != 0) + && (errno != 0)) + #else /* !WITH_ACL */ + || (chmod (dst, statp->st_mode) != 0) +@@ -482,12 +540,12 @@ static int copy_dir (const char *src, const char *dst, + * additional logic so that no unexpected permissions result. + */ + || ( !reset_selinux +- && (attr_copy_file (src, dst, NULL, &ctx) != 0) ++ && (attr_copy_path (src, dst, NULL, &ctx) != 0) + && (errno != 0)) + #endif /* WITH_ATTR */ +- || (copy_tree (src, dst, false, reset_selinux, ++ || (copy_tree_impl (src, dst, false, reset_selinux, + old_uid, new_uid, old_gid, new_gid) != 0) +- || (utimes (dst, mt) != 0)) { ++ || (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0)) { + err = -1; + } + +@@ -540,9 +598,9 @@ static /*@null@*/char *readlink_malloc (const char *filename) + * + * Return 0 on success, -1 on error. + */ +-static int copy_symlink (const char *src, const char *dst, ++static int copy_symlink (const struct path_info *src, const struct path_info *dst, + unused bool reset_selinux, +- const struct stat *statp, const struct timeval mt[], ++ const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) + { +@@ -560,7 +618,7 @@ static int copy_symlink (const char *src, const char *dst, + * destination directory name. + */ + +- oldlink = readlink_malloc (src); ++ oldlink = readlink_malloc (src->full_path); + if (NULL == oldlink) { + return -1; + } +@@ -580,13 +638,13 @@ static int copy_symlink (const char *src, const char *dst, + } + + #ifdef WITH_SELINUX +- if (set_selinux_file_context (dst, S_IFLNK) != 0) { ++ if (set_selinux_file_context (dst->full_path, S_IFLNK) != 0) { + free (oldlink); + return -1; + } + #endif /* WITH_SELINUX */ +- if ( (symlink (oldlink, dst) != 0) +- || (lchown_if_needed (dst, statp, ++ if ( (symlinkat (oldlink, dst->dirfd, dst->name) != 0) ++ || (chownat_if_needed (dst, statp, + old_uid, new_uid, old_gid, new_gid) != 0)) { + /* FIXME: there are no modes on symlinks, right? + * ACL could be copied, but this would be much more +@@ -600,14 +658,9 @@ static int copy_symlink (const char *src, const char *dst, + } + free (oldlink); + +-#ifdef HAVE_LUTIMES +- /* 2007-10-18: We don't care about +- * exit status of lutimes because +- * it returns ENOSYS on many system +- * - not implemented +- */ +- (void) lutimes (dst, mt); +-#endif /* HAVE_LUTIMES */ ++ if (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0) { ++ return -1; ++ } + + return 0; + } +@@ -619,13 +672,13 @@ static int copy_symlink (const char *src, const char *dst, + * + * Return 0 on success, -1 on error. + */ +-static int copy_hardlink (const char *dst, ++static int copy_hardlink (const struct path_info *dst, + unused bool reset_selinux, + struct link_name *lp) + { + /* FIXME: selinux, ACL, Extended Attributes needed? */ + +- if (link (lp->ln_name, dst) != 0) { ++ if (linkat (AT_FDCWD, lp->ln_name, dst->dirfd, dst->name, 0) != 0) { + return -1; + } + +@@ -649,28 +702,28 @@ static int copy_hardlink (const char *dst, + * + * Return 0 on success, -1 on error. + */ +-static int copy_special (const char *src, const char *dst, ++static int copy_special (const struct path_info *src, const struct path_info *dst, + bool reset_selinux, +- const struct stat *statp, const struct timeval mt[], ++ const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) + { + int err = 0; + + #ifdef WITH_SELINUX +- if (set_selinux_file_context (dst, statp->st_mode & S_IFMT) != 0) { ++ if (set_selinux_file_context (dst->full_path, statp->st_mode & S_IFMT) != 0) { + return -1; + } + #endif /* WITH_SELINUX */ + +- if ( (mknod (dst, statp->st_mode & ~07777U, statp->st_rdev) != 0) +- || (chown_if_needed (dst, statp, ++ if ( (mknodat (dst->dirfd, dst->name, statp->st_mode & ~07777U, statp->st_rdev) != 0) ++ || (chownat_if_needed (dst, statp, + old_uid, new_uid, old_gid, new_gid) != 0) + #ifdef WITH_ACL +- || ( (perm_copy_file (src, dst, &ctx) != 0) ++ || ( (perm_copy_path (src, dst, &ctx) != 0) + && (errno != 0)) + #else /* !WITH_ACL */ +- || (chmod (dst, statp->st_mode & 07777) != 0) ++ || (fchmodat (dst->dirfd, dst->name, statp->st_mode & 07777, AT_SYMLINK_NOFOLLOW) != 0) + #endif /* !WITH_ACL */ + #ifdef WITH_ATTR + /* +@@ -681,10 +734,10 @@ static int copy_special (const char *src, const char *dst, + * additional logic so that no unexpected permissions result. + */ + || ( !reset_selinux +- && (attr_copy_file (src, dst, NULL, &ctx) != 0) ++ && (attr_copy_path (src, dst, NULL, &ctx) != 0) + && (errno != 0)) + #endif /* WITH_ATTR */ +- || (utimes (dst, mt) != 0)) { ++ || (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0)) { + err = -1; + } + +@@ -737,9 +790,9 @@ static ssize_t full_write(int fd, const void *buf, size_t count) { + * + * Return 0 on success, -1 on error. + */ +-static int copy_file (const char *src, const char *dst, ++static int copy_file (const struct path_info *src, const struct path_info *dst, + bool reset_selinux, +- const struct stat *statp, const struct timeval mt[], ++ const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) + { +@@ -747,22 +800,22 @@ static int copy_file (const char *src, const char *dst, + int ifd; + int ofd; + +- ifd = open (src, O_RDONLY|O_NOFOLLOW); ++ ifd = openat (src->dirfd, src->name, O_RDONLY|O_NOFOLLOW|O_CLOEXEC); + if (ifd < 0) { + return -1; + } + #ifdef WITH_SELINUX +- if (set_selinux_file_context (dst, S_IFREG) != 0) { ++ if (set_selinux_file_context (dst->full_path, S_IFREG) != 0) { + (void) close (ifd); + return -1; + } + #endif /* WITH_SELINUX */ +- ofd = open (dst, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_NOFOLLOW | O_CLOEXEC, statp->st_mode & 07777); ++ ofd = openat (dst->dirfd, dst->name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_NOFOLLOW | O_CLOEXEC, statp->st_mode & 07777); + if ( (ofd < 0) + || (fchown_if_needed (ofd, statp, + old_uid, new_uid, old_gid, new_gid) != 0) + #ifdef WITH_ACL +- || ( (perm_copy_fd (src, ifd, dst, ofd, &ctx) != 0) ++ || ( (perm_copy_fd (src->full_path, ifd, dst->full_path, ofd, &ctx) != 0) + && (errno != 0)) + #else /* !WITH_ACL */ + || (fchmod (ofd, statp->st_mode & 07777) != 0) +@@ -776,7 +829,7 @@ static int copy_file (const char *src, const char *dst, + * additional logic so that no unexpected permissions result. + */ + || ( !reset_selinux +- && (attr_copy_fd (src, ifd, dst, ofd, NULL, &ctx) != 0) ++ && (attr_copy_fd (src->full_path, ifd, dst->full_path, ofd, NULL, &ctx) != 0) + && (errno != 0)) + #endif /* WITH_ATTR */ + ) { +@@ -812,23 +865,13 @@ static int copy_file (const char *src, const char *dst, + } + + (void) close (ifd); +- +-#ifdef HAVE_FUTIMES +- if (futimes (ofd, mt) != 0) { +- (void) close (ofd); +- return -1; +- } +-#endif /* HAVE_FUTIMES */ +- + if (close (ofd) != 0) { + return -1; + } + +-#ifndef HAVE_FUTIMES +- if (utimes(dst, mt) != 0) { ++ if (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0) { + return -1; + } +-#endif /* !HAVE_FUTIMES */ + + return err; + } +@@ -863,7 +906,70 @@ static int chown_function ## _if_needed (type_dst dst, \ + return chown_function (dst, tmpuid, tmpgid); \ + } + +-def_chown_if_needed (chown, const char *) +-def_chown_if_needed (lchown, const char *) + def_chown_if_needed (fchown, int) + ++static int chownat_if_needed (const struct path_info *dst, ++ const struct stat *statp, ++ uid_t old_uid, uid_t new_uid, ++ gid_t old_gid, gid_t new_gid) ++{ ++ uid_t tmpuid = (uid_t) -1; ++ gid_t tmpgid = (gid_t) -1; ++ ++ /* Use new_uid if old_uid is set to -1 or if the file was ++ * owned by the user. */ ++ if (((uid_t) -1 == old_uid) || (statp->st_uid == old_uid)) { ++ tmpuid = new_uid; ++ } ++ /* Otherwise, or if new_uid was set to -1, we keep the same ++ * owner. */ ++ if ((uid_t) -1 == tmpuid) { ++ tmpuid = statp->st_uid; ++ } ++ ++ if (((gid_t) -1 == old_gid) || (statp->st_gid == old_gid)) { ++ tmpgid = new_gid; ++ } ++ if ((gid_t) -1 == tmpgid) { ++ tmpgid = statp->st_gid; ++ } ++ ++ return fchownat (dst->dirfd, dst->name, tmpuid, tmpgid, AT_SYMLINK_NOFOLLOW); ++} ++ ++/* ++ * copy_tree - copy files in a directory tree ++ * ++ * copy_tree() walks a directory tree and copies ordinary files ++ * as it goes. ++ * ++ * When reset_selinux is enabled, extended attributes (and thus ++ * SELinux attributes) are not copied. ++ * ++ * old_uid and new_uid are used to set the ownership of the copied ++ * files. Unless old_uid is set to -1, only the files owned by ++ * old_uid have their ownership changed to new_uid. In addition, if ++ * new_uid is set to -1, no ownership will be changed. ++ * ++ * The same logic applies for the group-ownership and ++ * old_gid/new_gid. ++ */ ++int copy_tree (const char *src_root, const char *dst_root, ++ bool copy_root, bool reset_selinux, ++ uid_t old_uid, uid_t new_uid, ++ gid_t old_gid, gid_t new_gid) ++{ ++ const struct path_info src = { ++ .full_path = src_root, ++ .dirfd = AT_FDCWD, ++ .name = src_root ++ }; ++ const struct path_info dst = { ++ .full_path = dst_root, ++ .dirfd = AT_FDCWD, ++ .name = dst_root ++ }; ++ ++ return copy_tree_impl(&src, &dst, copy_root, reset_selinux, ++ old_uid, new_uid, old_gid, new_gid); ++} diff --git a/backport-Avoid-races-in-remove_tree.patch b/backport-Avoid-races-in-remove_tree.patch new file mode 100644 index 0000000000000000000000000000000000000000..3d44b53a72c3f12dcf65339fefeed57a9b06a19a --- /dev/null +++ b/backport-Avoid-races-in-remove_tree.patch @@ -0,0 +1,157 @@ +From f6f8bcd2a57c06983296485cc028ebdf467ebfd7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= +Date: Fri, 5 Aug 2022 17:57:22 +0200 +Subject: [PATCH] Avoid races in remove_tree() + +Use *at() functions to pin the directory operating in to avoid being +redirected by unprivileged users replacing parts of paths by symlinks to +privileged files. + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/f6f8bcd2a57c06983296485cc028ebdf467ebfd7 + +--- + libmisc/remove_tree.c | 87 +++++++++++++++++++------------------------ + 1 file changed, 39 insertions(+), 48 deletions(-) + +diff --git a/libmisc/remove_tree.c b/libmisc/remove_tree.c +index 04bc7fc4a..3d76b95e0 100644 +--- a/libmisc/remove_tree.c ++++ b/libmisc/remove_tree.c +@@ -11,6 +11,7 @@ + + #ident "$Id$" + ++#include + #include + #include + #include +@@ -21,90 +22,80 @@ + #include "prototypes.h" + #include "defines.h" + +-/* +- * remove_tree - delete a directory tree +- * +- * remove_tree() walks a directory tree and deletes all the files +- * and directories. +- * At the end, it deletes the root directory itself. +- */ +- +-int remove_tree (const char *root, bool remove_root) ++static int remove_tree_at (int at_fd, const char *path, bool remove_root) + { +- char *new_name = NULL; +- int err = 0; +- struct dirent *ent; +- struct stat sb; + DIR *dir; ++ const struct dirent *ent; ++ int dir_fd, rc = 0; + +- /* +- * Open the source directory and read each entry. Every file +- * entry in the directory is copied with the UID and GID set +- * to the provided values. As an added security feature only +- * regular files (and directories ...) are copied, and no file +- * is made set-ID. +- */ +- dir = opendir (root); +- if (NULL == dir) { ++ dir_fd = openat (at_fd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); ++ if (dir_fd < 0) { ++ return -1; ++ } ++ ++ dir = fdopendir (dir_fd); ++ if (!dir) { ++ (void) close (dir_fd); + return -1; + } + ++ /* ++ * Open the source directory and delete each entry. ++ */ + while ((ent = readdir (dir))) { +- size_t new_len = strlen (root) + strlen (ent->d_name) + 2; ++ struct stat ent_sb; + + /* + * Skip the "." and ".." entries + */ +- + if (strcmp (ent->d_name, ".") == 0 || + strcmp (ent->d_name, "..") == 0) { + continue; + } + +- /* +- * Make the filename for the current entry. +- */ +- +- free (new_name); +- new_name = (char *) malloc (new_len); +- if (NULL == new_name) { +- err = -1; ++ rc = fstatat (dirfd(dir), ent->d_name, &ent_sb, AT_SYMLINK_NOFOLLOW); ++ if (rc < 0) { + break; + } +- (void) snprintf (new_name, new_len, "%s/%s", root, ent->d_name); +- if (LSTAT (new_name, &sb) == -1) { +- continue; +- } + +- if (S_ISDIR (sb.st_mode)) { ++ if (S_ISDIR (ent_sb.st_mode)) { + /* + * Recursively delete this directory. + */ +- if (remove_tree (new_name, true) != 0) { +- err = -1; ++ if (remove_tree_at (dirfd(dir), ent->d_name, true) != 0) { ++ rc = -1; + break; + } + } else { + /* + * Delete the file. + */ +- if (unlink (new_name) != 0) { +- err = -1; ++ if (unlinkat (dirfd(dir), ent->d_name, 0) != 0) { ++ rc = -1; + break; + } + } + } +- if (NULL != new_name) { +- free (new_name); +- } ++ + (void) closedir (dir); + +- if (remove_root && (0 == err)) { +- if (rmdir (root) != 0) { +- err = -1; ++ if (remove_root && (0 == rc)) { ++ if (unlinkat (at_fd, path, AT_REMOVEDIR) != 0) { ++ rc = -1; + } + } + +- return err; ++ return rc; + } + ++/* ++ * remove_tree - delete a directory tree ++ * ++ * remove_tree() walks a directory tree and deletes all the files ++ * and directories. ++ * At the end, it deletes the root directory itself. ++ */ ++int remove_tree (const char *root, bool remove_root) ++{ ++ return remove_tree_at (AT_FDCWD, root, remove_root); ++} diff --git a/backport-CVE-2013-4235.patch b/backport-CVE-2013-4235.patch new file mode 100644 index 0000000000000000000000000000000000000000..9506bc125067b629e964579d904a53169c0ba673 --- /dev/null +++ b/backport-CVE-2013-4235.patch @@ -0,0 +1,34 @@ +From b4472167c2f5057d56686d3349a9b55fc508efe6 Mon Sep 17 00:00:00 2001 +From: ed neville +Date: Fri, 31 Dec 2021 22:40:13 +0000 +Subject: [PATCH] Adding nofollow to opens + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/b4472167c2f5057d56686d3349a9b55fc508efe6 + +--- + libmisc/copydir.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/libmisc/copydir.c b/libmisc/copydir.c +index f2130bcac..a296d925d 100644 +--- a/libmisc/copydir.c ++++ b/libmisc/copydir.c +@@ -741,7 +741,7 @@ static int copy_file (const char *src, const char *dst, + char buf[1024]; + ssize_t cnt; + +- ifd = open (src, O_RDONLY); ++ ifd = open (src, O_RDONLY|O_NOFOLLOW); + if (ifd < 0) { + return -1; + } +@@ -751,7 +751,7 @@ static int copy_file (const char *src, const char *dst, + return -1; + } + #endif /* WITH_SELINUX */ +- ofd = open (dst, O_WRONLY | O_CREAT | O_TRUNC, statp->st_mode & 07777); ++ ofd = open (dst, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, statp->st_mode & 07777); + if ( (ofd < 0) + || (fchown_if_needed (ofd, statp, + old_uid, new_uid, old_gid, new_gid) != 0) diff --git a/backport-Fail-if-regular-file-pre-exists-in-copy_tree.patch b/backport-Fail-if-regular-file-pre-exists-in-copy_tree.patch new file mode 100644 index 0000000000000000000000000000000000000000..07d09838ac8a3e49e05c57eee27b25bceb2c2141 --- /dev/null +++ b/backport-Fail-if-regular-file-pre-exists-in-copy_tree.patch @@ -0,0 +1,28 @@ +From 1d281273b149f2bb992d893d8ca9ffffddc95cc8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= +Date: Fri, 5 Aug 2022 17:57:26 +0200 +Subject: [PATCH] Fail if regular file pre-exists in copy_tree() + +Similar to the default behavior of mkdir(2), symlink(2), link(2) and +mknod(2). + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/1d281273b149f2bb992d893d8ca9ffffddc95cc8 + +--- + libmisc/copydir.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libmisc/copydir.c b/libmisc/copydir.c +index 648f562a1..90895cfb0 100644 +--- a/libmisc/copydir.c ++++ b/libmisc/copydir.c +@@ -723,7 +723,7 @@ static int copy_file (const char *src, const char *dst, + return -1; + } + #endif /* WITH_SELINUX */ +- ofd = open (dst, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, statp->st_mode & 07777); ++ ofd = open (dst, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_NOFOLLOW | O_CLOEXEC, statp->st_mode & 07777); + if ( (ofd < 0) + || (fchown_if_needed (ofd, statp, + old_uid, new_uid, old_gid, new_gid) != 0) diff --git a/backport-Fix-parentheses-in-configure.ac.patch b/backport-Fix-parentheses-in-configure.ac.patch new file mode 100644 index 0000000000000000000000000000000000000000..bf37375bb29d8757dad276c9d5431f508c60fb50 --- /dev/null +++ b/backport-Fix-parentheses-in-configure.ac.patch @@ -0,0 +1,27 @@ +From 049f9a7f6b320c728a6274299041e360381d7cd5 Mon Sep 17 00:00:00 2001 +From: Andy Zaugg +Date: Tue, 21 Sep 2021 21:51:10 -0700 +Subject: [PATCH] Fix parentheses in configure.ac + +Resolving issue https://github.com/shadow-maint/shadow/issues/419 + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/049f9a7f6b320c728a6274299041e360381d7cd5 + +--- + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index 994836bda..6cbb6bd27 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -345,7 +345,7 @@ if test "$with_sssd" = "yes"; then + [AC_MSG_ERROR([posix_spawn is needed for sssd support])]) + fi + +-AS_IF([test "$with_su" != "no"], AC_DEFINE(WITH_SU, 1, [Build with su])]) ++AS_IF([test "$with_su" != "no"], AC_DEFINE(WITH_SU, 1, [Build with su])) + AM_CONDITIONAL([WITH_SU], [test "x$with_su" != "xno"]) + + dnl Check for some functions in libc first, only if not found check for diff --git a/backport-Handle-malformed-lines-in-hushlogins-file.patch b/backport-Handle-malformed-lines-in-hushlogins-file.patch new file mode 100644 index 0000000000000000000000000000000000000000..49b2d10dc8ad75208fcf98cb114b7f81dc75b22d --- /dev/null +++ b/backport-Handle-malformed-lines-in-hushlogins-file.patch @@ -0,0 +1,34 @@ +From 63a96706b1205f91c4a57de21ac56e996d270ff1 Mon Sep 17 00:00:00 2001 +From: Tobias Stoeckmann +Date: Fri, 29 Oct 2021 19:44:46 +0200 +Subject: [PATCH] Handle malformed lines in hushlogins file. + +If a line in hushlogins file, e.g. /etc/hushlogins, starts with +'\0', then current code performs an out of boundary write. +If the line lacks a newline at the end, then another character is +overridden. + +With strcspn both cases are solved. + +Signed-off-by: Tobias Stoeckmann + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/63a96706b1205f91c4a57de21ac56e996d270ff1 + +--- + libmisc/hushed.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libmisc/hushed.c b/libmisc/hushed.c +index b71b99ce2..3c3adafca 100644 +--- a/libmisc/hushed.c ++++ b/libmisc/hushed.c +@@ -90,7 +90,7 @@ bool hushed (const char *username) + return false; + } + for (found = false; !found && (fgets (buf, (int) sizeof buf, fp) == buf);) { +- buf[strlen (buf) - 1] = '\0'; ++ buf[strcspn (buf, "\n")] = '\0'; + found = (strcmp (buf, pw->pw_shell) == 0) || + (strcmp (buf, pw->pw_name) == 0); + } diff --git a/backport-Improve-child-error-handling.patch b/backport-Improve-child-error-handling.patch new file mode 100644 index 0000000000000000000000000000000000000000..cdebe8d77e2e827a35fe47f12e233958bfab99b0 --- /dev/null +++ b/backport-Improve-child-error-handling.patch @@ -0,0 +1,62 @@ +From 624d57c08caceed306212d24c2147f6273f3fc4b Mon Sep 17 00:00:00 2001 +From: Tobias Stoeckmann +Date: Sun, 14 Nov 2021 12:01:32 +0100 +Subject: [PATCH] Improve child error handling + +Always set SIGCHLD handler to default, even if the caller of vipw has +set SIGCHLD to ignore. If SIGCHLD is ignored no zombie processes would +be created, which in turn could mean that kill is called with an already +recycled pid. + +Proof of Concept: + +1. Compile nochld: + -- + #include + #include + int main(void) { + char *argv[] = { "vipw", NULL }; + signal(SIGCHLD, SIG_IGN); + execvp("vipw", argv); + return 1; + } + -- +2. Run nochld +3. Suspend child vi, which suspends vipw too: +`kill -STOP childpid` +4. Kill vi: +`kill -9 childpid` +5. You can see with ps that childpid is no zombie but disappeared +6. Bring vipw back into foreground +`fg` + +The kill call sends SIGCONT to "childpid" which in turn could have been +already recycled for another process. + +This is definitely not a vulnerability. It would take super user +operations, at which point an attacker would have already elevated +permissions. + +Signed-off-by: Tobias Stoeckmann + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/624d57c08caceed306212d24c2147f6273f3fc4b + +--- + src/vipw.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/vipw.c b/src/vipw.c +index 94185c3df..1a69ef285 100644 +--- a/src/vipw.c ++++ b/src/vipw.c +@@ -349,6 +349,9 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void)) + sigprocmask(SIG_BLOCK, &mask, &omask); + } + ++ /* set SIGCHLD to default for waitpid */ ++ signal(SIGCHLD, SIG_DFL); ++ + for (;;) { + pid = waitpid (pid, &status, WUNTRACED); + if ((pid != -1) && (WIFSTOPPED (status) != 0)) { diff --git a/backport-More-robust-file-content-copy-in-copy_tree.patch b/backport-More-robust-file-content-copy-in-copy_tree.patch new file mode 100644 index 0000000000000000000000000000000000000000..c7ed1f1a1e8afa73b6fe147af10d0d0ca6cdec5d --- /dev/null +++ b/backport-More-robust-file-content-copy-in-copy_tree.patch @@ -0,0 +1,98 @@ +From f606314f0c22fb5d13e5af17a70860d57559e808 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= +Date: Fri, 5 Aug 2022 17:57:27 +0200 +Subject: [PATCH] More robust file content copy in copy_tree() + +Bail out on read(2) failure, continue on EINTR, support short writes and +increase chunk size. + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/f606314f0c22fb5d13e5af17a70860d57559e808 + +--- + libmisc/copydir.c | 58 +++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 54 insertions(+), 4 deletions(-) + +diff --git a/libmisc/copydir.c b/libmisc/copydir.c +index 90895cfb0..95042187b 100644 +--- a/libmisc/copydir.c ++++ b/libmisc/copydir.c +@@ -691,6 +691,42 @@ static int copy_special (const char *src, const char *dst, + return err; + } + ++/* ++ * full_write - write entire buffer ++ * ++ * Write up to count bytes from the buffer starting at buf to the ++ * file referred to by the file descriptor fd. ++ * Retry in case of a short write. ++ * ++ * Returns the number of bytes written on success, -1 on error. ++ */ ++static ssize_t full_write(int fd, const void *buf, size_t count) { ++ ssize_t written = 0; ++ ++ while (count > 0) { ++ ssize_t res; ++ ++ res = write(fd, buf, count); ++ if (res < 0) { ++ if (errno == EINTR) { ++ continue; ++ } ++ ++ return res; ++ } ++ ++ if (res == 0) { ++ break; ++ } ++ ++ written += res; ++ buf = (const unsigned char*)buf + res; ++ count -= (size_t)res; ++ } ++ ++ return written; ++} ++ + /* + * copy_file - copy a file + * +@@ -710,8 +746,6 @@ static int copy_file (const char *src, const char *dst, + int err = 0; + int ifd; + int ofd; +- char buf[1024]; +- ssize_t cnt; + + ifd = open (src, O_RDONLY|O_NOFOLLOW); + if (ifd < 0) { +@@ -753,8 +787,24 @@ static int copy_file (const char *src, const char *dst, + return -1; + } + +- while ((cnt = read (ifd, buf, sizeof buf)) > 0) { +- if (write (ofd, buf, (size_t)cnt) != cnt) { ++ while (true) { ++ char buf[8192]; ++ ssize_t cnt; ++ ++ cnt = read (ifd, buf, sizeof buf); ++ if (cnt < 0) { ++ if (errno == EINTR) { ++ continue; ++ } ++ (void) close (ofd); ++ (void) close (ifd); ++ return -1; ++ } ++ if (cnt == 0) { ++ break; ++ } ++ ++ if (full_write (ofd, buf, (size_t)cnt) < 0) { + (void) close (ofd); + (void) close (ifd); + return -1; diff --git a/backport-Only-free-sgent-if-it-was-initialized.patch b/backport-Only-free-sgent-if-it-was-initialized.patch new file mode 100644 index 0000000000000000000000000000000000000000..ad29342c0027f85eddc4d40963aa84b90696facd --- /dev/null +++ b/backport-Only-free-sgent-if-it-was-initialized.patch @@ -0,0 +1,43 @@ +From 117bc66c6f95fa85ca75ecfdb8fbd3615deca0b6 Mon Sep 17 00:00:00 2001 +From: Michael Vetter +Date: Mon, 20 Sep 2021 11:04:50 +0200 +Subject: [PATCH] Only free sgent if it was initialized + +`sgent` is only initialized in `get_group()` if `is_shadowgrp` is true. +So we should also only attempt to free it if this is actually the case. + +Can otherwise lead to: +``` +free() double free detected in tcache 2 (gpasswd) +``` + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/117bc66c6f95fa85ca75ecfdb8fbd3615deca0b6 + +--- + src/gpasswd.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/src/gpasswd.c b/src/gpasswd.c +index a43d9a590..04bed83d6 100644 +--- a/src/gpasswd.c ++++ b/src/gpasswd.c +@@ -1207,11 +1207,13 @@ int main (int argc, char **argv) + sssd_flush_cache (SSSD_DB_GROUP); + + #ifdef SHADOWGRP +- if (sgent.sg_adm) { +- xfree(sgent.sg_adm); +- } +- if (sgent.sg_mem) { +- xfree(sgent.sg_mem); ++ if (is_shadowgrp) { ++ if (sgent.sg_adm) { ++ xfree(sgent.sg_adm); ++ } ++ if (sgent.sg_mem) { ++ xfree(sgent.sg_mem); ++ } + } + #endif + if (grent.gr_mem) { diff --git a/backport-Require-symlink-support.patch b/backport-Require-symlink-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..c33346534c921040bf65068197d1744720fb1508 --- /dev/null +++ b/backport-Require-symlink-support.patch @@ -0,0 +1,148 @@ +From dab764d0195fc16d1d39330eee8a33e8917826d8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= +Date: Fri, 5 Aug 2022 17:57:24 +0200 +Subject: [PATCH] Require symlink support + +Require lstat(2), lchown(2), S_IFLNK and S_ISLNK from POSIX.1-2001. + +Already unconditionally used in lib/tcbfuncs.c and lib/run_part.c. + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/dab764d0195fc16d1d39330eee8a33e8917826d8 + +--- + configure.ac | 2 +- + lib/commonio.c | 2 -- + lib/defines.h | 16 ---------------- + libmisc/copydir.c | 10 ++-------- + 4 files changed, 3 insertions(+), 27 deletions(-) + +diff --git a/configure.ac b/configure.ac +index b9a2263bb..7e954c29c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -49,7 +49,7 @@ AC_CHECK_HEADER([shadow.h],,[AC_MSG_ERROR([You need a libc with shadow.h])]) + + AC_CHECK_FUNCS(arc4random_buf l64a fchmod fchown fsync futimes \ + getentropy getrandom getspnam getusershell \ +- getutent initgroups lchown lckpwdf lstat lutimes \ ++ getutent initgroups lckpwdf lutimes \ + setgroups updwtmp updwtmpx innetgr getpwnam_r \ + getpwuid_r getgrnam_r getgrgid_r getspnam_r \ + memset_s explicit_bzero) +diff --git a/lib/commonio.c b/lib/commonio.c +index 9e0fde600..80288d644 100644 +--- a/lib/commonio.c ++++ b/lib/commonio.c +@@ -65,7 +65,6 @@ int lrename (const char *old, const char *new) + int res; + char *r = NULL; + +-#if defined(S_ISLNK) + #ifndef __GLIBC__ + char resolved_path[PATH_MAX]; + #endif /* !__GLIBC__ */ +@@ -82,7 +81,6 @@ int lrename (const char *old, const char *new) + new = r; + } + } +-#endif /* S_ISLNK */ + + res = rename (old, new); + +diff --git a/lib/defines.h b/lib/defines.h +index 4a2b90c9e..ee33aa0da 100644 +--- a/lib/defines.h ++++ b/lib/defines.h +@@ -205,22 +205,6 @@ static inline void memzero(void *ptr, size_t size) + # define SEEK_END 2 + #endif + +-#ifndef S_ISLNK +-#define S_ISLNK(x) (0) +-#endif +- +-#if HAVE_LCHOWN +-#define LCHOWN lchown +-#else +-#define LCHOWN chown +-#endif +- +-#if HAVE_LSTAT +-#define LSTAT lstat +-#else +-#define LSTAT stat +-#endif +- + #if HAVE_TERMIOS_H + # include + # define STTY(fd, termio) tcsetattr(fd, TCSANOW, termio) +diff --git a/libmisc/copydir.c b/libmisc/copydir.c +index 2929151db..648f562a1 100644 +--- a/libmisc/copydir.c ++++ b/libmisc/copydir.c +@@ -56,14 +56,12 @@ static int copy_dir (const char *src, const char *dst, + const struct stat *statp, const struct timeval mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +-#ifdef S_IFLNK + static /*@null@*/char *readlink_malloc (const char *filename); + static int copy_symlink (const char *src, const char *dst, + unused bool reset_selinux, + const struct stat *statp, const struct timeval mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +-#endif /* S_IFLNK */ + static int copy_hardlink (const char *dst, + unused bool reset_selinux, + struct link_name *lp); +@@ -223,7 +221,7 @@ int copy_tree (const char *src_root, const char *dst_root, + return -1; + } + +- if (LSTAT (src_root, &sb) == -1) { ++ if (lstat (src_root, &sb) == -1) { + return -1; + } + +@@ -364,7 +362,7 @@ static int copy_entry (const char *src, const char *dst, + struct link_name *lp; + struct timeval mt[2]; + +- if (LSTAT (src, &sb) == -1) { ++ if (lstat (src, &sb) == -1) { + /* If we cannot stat the file, do not care. */ + } else { + #ifdef HAVE_STRUCT_STAT_ST_ATIM +@@ -396,7 +394,6 @@ static int copy_entry (const char *src, const char *dst, + old_uid, new_uid, old_gid, new_gid); + } + +-#ifdef S_IFLNK + /* + * Copy any symbolic links + */ +@@ -405,7 +402,6 @@ static int copy_entry (const char *src, const char *dst, + err = copy_symlink (src, dst, reset_selinux, &sb, mt, + old_uid, new_uid, old_gid, new_gid); + } +-#endif /* S_IFLNK */ + + /* + * See if this is a previously copied link +@@ -498,7 +494,6 @@ static int copy_dir (const char *src, const char *dst, + return err; + } + +-#ifdef S_IFLNK + /* + * readlink_malloc - wrapper for readlink + * +@@ -616,7 +611,6 @@ static int copy_symlink (const char *src, const char *dst, + + return 0; + } +-#endif /* S_IFLNK */ + + /* + * copy_hardlink - copy a hardlink diff --git a/backport-lib-btrfs-avoid-NULL-dereference.patch b/backport-lib-btrfs-avoid-NULL-dereference.patch new file mode 100644 index 0000000000000000000000000000000000000000..54aaedd34bca444e183ae24c2085e6d2f5be5f33 --- /dev/null +++ b/backport-lib-btrfs-avoid-NULL-dereference.patch @@ -0,0 +1,29 @@ +From 54ab542887994f8b6e5411469d6867e3a2e58800 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= +Date: Thu, 26 Jan 2023 21:03:56 +0100 +Subject: [PATCH] lib/btrfs: avoid NULL-dereference + + btrfs.c:42:13: warning: use of NULL 'cmd' where non-null expected [CWE-476] [-Wanalyzer-null-argument] + +Reviewed-by: Alejandro Colomar + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/54ab542887994f8b6e5411469d6867e3a2e58800 + +--- + libmisc/btrfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libmisc/btrfs.c b/libmisc/btrfs.c +index a2563f7c3..03915981c 100644 +--- a/libmisc/btrfs.c ++++ b/libmisc/btrfs.c +@@ -39,7 +39,7 @@ static int run_btrfs_subvolume_cmd(const char *subcmd, const char *arg1, const c + NULL + }; + +- if (access(cmd, X_OK)) { ++ if (!cmd || access(cmd, X_OK)) { + return 1; + } + diff --git a/backport-lib-check-NULL-before-freeing-passwd-data.patch b/backport-lib-check-NULL-before-freeing-passwd-data.patch new file mode 100644 index 0000000000000000000000000000000000000000..8164fff9a1308bb6e8e539c605b961179ec01731 --- /dev/null +++ b/backport-lib-check-NULL-before-freeing-passwd-data.patch @@ -0,0 +1,70 @@ +From d594243fbbdabc73fdee50886f6dd11867b5cfab Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 18 Nov 2021 16:48:26 +0100 +Subject: [PATCH] lib: check NULL before freeing passwd data + +Add an additional NULL check condition in spw_free() and pw_free() to +avoid freeing an already empty pointer. + +Signed-off-by: Iker Pedrosa + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/d594243fbbdabc73fdee50886f6dd11867b5cfab + +--- + lib/pwmem.c | 18 ++++++++++-------- + lib/shadowmem.c | 12 +++++++----- + 2 files changed, 17 insertions(+), 13 deletions(-) + +diff --git a/lib/pwmem.c b/lib/pwmem.c +index 17d2eb219..9f184d5e1 100644 +--- a/lib/pwmem.c ++++ b/lib/pwmem.c +@@ -93,14 +93,16 @@ + + void pw_free (/*@out@*/ /*@only@*/struct passwd *pwent) + { +- free (pwent->pw_name); +- if (pwent->pw_passwd) { +- memzero (pwent->pw_passwd, strlen (pwent->pw_passwd)); +- free (pwent->pw_passwd); ++ if (pwent != NULL) { ++ free (pwent->pw_name); ++ if (pwent->pw_passwd) { ++ memzero (pwent->pw_passwd, strlen (pwent->pw_passwd)); ++ free (pwent->pw_passwd); ++ } ++ free (pwent->pw_gecos); ++ free (pwent->pw_dir); ++ free (pwent->pw_shell); ++ free (pwent); + } +- free (pwent->pw_gecos); +- free (pwent->pw_dir); +- free (pwent->pw_shell); +- free (pwent); + } + +diff --git a/lib/shadowmem.c b/lib/shadowmem.c +index 8989598f8..1d047cc04 100644 +--- a/lib/shadowmem.c ++++ b/lib/shadowmem.c +@@ -79,11 +79,13 @@ + + void spw_free (/*@out@*/ /*@only@*/struct spwd *spent) + { +- free (spent->sp_namp); +- if (NULL != spent->sp_pwdp) { +- memzero (spent->sp_pwdp, strlen (spent->sp_pwdp)); +- free (spent->sp_pwdp); ++ if (spent != NULL) { ++ free (spent->sp_namp); ++ if (NULL != spent->sp_pwdp) { ++ memzero (spent->sp_pwdp, strlen (spent->sp_pwdp)); ++ free (spent->sp_pwdp); ++ } ++ free (spent); + } +- free (spent); + } + diff --git a/backport-libmisc-minimum-id-check-for-system-accounts.patch b/backport-libmisc-minimum-id-check-for-system-accounts.patch new file mode 100644 index 0000000000000000000000000000000000000000..b899cbb2ca5029fd213ef52023c01fbf8ccf4a95 --- /dev/null +++ b/backport-libmisc-minimum-id-check-for-system-accounts.patch @@ -0,0 +1,58 @@ +From d324c6776b3a1d4ac22bced543f72dc5dd366927 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 6 Oct 2022 11:21:18 +0200 +Subject: [PATCH] libmisc: minimum id check for system accounts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The minimum id allocation for system accounts shouldn't be 0 as this is +reserved for root. + +Signed-off-by: Tomáš Mráz +Signed-off-by: Iker Pedrosa + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/d324c6776b3a1d4ac22bced543f72dc5dd366927 + +--- + libmisc/find_new_gid.c | 7 +++++++ + libmisc/find_new_uid.c | 7 +++++++ + 2 files changed, 14 insertions(+) + +diff --git a/libmisc/find_new_gid.c b/libmisc/find_new_gid.c +index 666b61078..65ab5d013 100644 +--- a/libmisc/find_new_gid.c ++++ b/libmisc/find_new_gid.c +@@ -60,6 +60,13 @@ static int get_ranges (bool sys_group, gid_t *min_id, gid_t *max_id, + (unsigned long) *max_id); + return EINVAL; + } ++ /* ++ * Zero is reserved for root and the allocation algorithm does not ++ * work right with it. ++ */ ++ if (*min_id == 0) { ++ *min_id = (gid_t) 1; ++ } + } else { + /* Non-system groups */ + +diff --git a/libmisc/find_new_uid.c b/libmisc/find_new_uid.c +index 322d15ab7..5f7e74b53 100644 +--- a/libmisc/find_new_uid.c ++++ b/libmisc/find_new_uid.c +@@ -60,6 +60,13 @@ static int get_ranges (bool sys_user, uid_t *min_id, uid_t *max_id, + (unsigned long) *max_id); + return EINVAL; + } ++ /* ++ * Zero is reserved for root and the allocation algorithm does not ++ * work right with it. ++ */ ++ if (*min_id == 0) { ++ *min_id = (uid_t) 1; ++ } + } else { + /* Non-system users */ + diff --git a/backport-man-po-Makefile.in-switch-from-xml2po-to-itstool.patch b/backport-man-po-Makefile.in-switch-from-xml2po-to-itstool.patch new file mode 100644 index 0000000000000000000000000000000000000000..4f06e84757de0be8115330f5f89e28e5f695eaee --- /dev/null +++ b/backport-man-po-Makefile.in-switch-from-xml2po-to-itstool.patch @@ -0,0 +1,45 @@ +From 02b200c9aa501ad4e1651c553cea1aaf9e5e0f4f Mon Sep 17 00:00:00 2001 +From: Serge Hallyn +Date: Sat, 14 Aug 2021 14:24:03 -0500 +Subject: [PATCH] man/po/Makefile.in: switch from xml2po to itstool + +xml2po is deprecated. We've previously replaced xml2po with +itstool in man/generate_translations.mak, but there was still +an instance of it that only is exercised for 'make dist'. +Update that one. Now 'make dist' succeeds on a ubuntu focal +or newer host where xml2po is not available. + +Signed-off-by: Serge Hallyn + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/02b200c9aa501ad4e1651c553cea1aaf9e5e0f4f + +--- + man/po/Makefile.in | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/man/po/Makefile.in b/man/po/Makefile.in +index deaa87058..f194957b4 100644 +--- a/man/po/Makefile.in ++++ b/man/po/Makefile.in +@@ -90,9 +90,17 @@ $(DOMAIN).pot-update: $(XMLFILES) $(srcdir)/XMLFILES remove-potcdate.sed + @set -e; tmpdir=`pwd`; \ + echo "cd $(top_srcdir)/man"; \ + cd $(top_srcdir)/man; \ +- echo "xml2po --expand-all-entities -o $$tmpdir/$(DOMAIN).po $(notdir $(XMLFILES))"; \ +- xml2po --expand-all-entities -o $$tmpdir/$(DOMAIN).po $(notdir $(XMLFILES)); \ +- cd $$tmpdir ++ files=""; \ ++ for file in $(notdir $(XMLFILES)); do \ ++ if grep -q SHADOW-CONFIG-HERE $$file ; then \ ++ sed -e 's/^/%config;/' $$file > $$file.out; \ ++ else \ ++ sed -e 's/^\(/\1 [%config;]>/' $$file > $$file.out; \ ++ fi; \ ++ files="$$files $$file.out"; \ ++ done; \ ++ itstool -d -o $$tmpdir/$(DOMAIN).po $$files; \ ++ cd $$tmpdir; \ + test ! -f $(DOMAIN).po || { \ + if test -f $(srcdir)/$(DOMAIN).pot; then \ + sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \ diff --git a/backport-run_parts-for-groupadd-and-groupdel.patch b/backport-run_parts-for-groupadd-and-groupdel.patch new file mode 100644 index 0000000000000000000000000000000000000000..07b30bd00c2072f511f887f982ca11fe61eb3de7 --- /dev/null +++ b/backport-run_parts-for-groupadd-and-groupdel.patch @@ -0,0 +1,125 @@ +From 4e1f674c41724dd96ad2c3a0c02ac9f6666697ba Mon Sep 17 00:00:00 2001 +From: ed neville +Date: Mon, 27 Mar 2023 20:23:03 +0100 +Subject: [PATCH] run_parts for groupadd and groupdel + +run_parts currently exists in useradd and userdel, this commit mirrors +the functionality with groupadd and groupdel + +Hook for group{add,del} to include killing processes that have group +membership that would no longer exist to avoid membership ID reuse. + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/4e1f674c41724dd96ad2c3a0c02ac9f6666697ba + +--- + .../groupdel-pre.d/01-kill_group_procs.sh | 26 +++++++++++++++++++ + src/groupadd.c | 11 ++++++++ + src/groupdel.c | 11 ++++++++ + 3 files changed, 48 insertions(+) + create mode 100644 etc/shadow-maint/groupdel-pre.d/01-kill_group_procs.sh + +diff --git a/etc/shadow-maint/groupdel-pre.d/01-kill_group_procs.sh b/etc/shadow-maint/groupdel-pre.d/01-kill_group_procs.sh +new file mode 100644 +index 000000000..10db52794 +--- /dev/null ++++ b/etc/shadow-maint/groupdel-pre.d/01-kill_group_procs.sh +@@ -0,0 +1,26 @@ ++#!/bin/sh ++ ++PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ++GROUPID=`awk -F: '$1 == "'"${SUBJECT}"'" { print $3 }' /etc/group` ++ ++if [ "${GROUPID}" = "" ]; then ++ exit 0 ++fi ++ ++for status in /proc/*/status; do ++ # either this isn't a process or its already dead since expanding the list ++ [ -f "$status" ] || continue ++ ++ tbuf=${status%/status} ++ pid=${tbuf#/proc/} ++ case "$pid" in ++ "$$") continue;; ++ [0-9]*) :;; ++ *) continue ++ esac ++ ++ grep -q '^Groups:.*\b'"${GROUPID}"'\b.*' "/proc/$pid/status" || continue ++ ++ kill -9 "$pid" || echo "cannot kill $pid" 1>&2 ++done ++ +diff --git a/src/groupadd.c b/src/groupadd.c +index 311421014..2eda1c680 100644 +--- a/src/groupadd.c ++++ b/src/groupadd.c +@@ -34,6 +34,7 @@ + #include "sgroupio.h" + #endif + #include "shadowlog.h" ++#include "run_part.h" + + /* + * exit status values +@@ -603,6 +604,11 @@ int main (int argc, char **argv) + + check_perms (); + ++ if (run_parts ("/etc/shadow-maint/groupadd-pre.d", group_name, ++ "groupadd")) { ++ exit(1); ++ } ++ + #ifdef SHADOWGRP + is_shadow_grp = sgr_file_present (); + #endif +@@ -621,6 +627,11 @@ int main (int argc, char **argv) + + grp_update (); + close_files (); ++ if (run_parts ("/etc/shadow-maint/groupadd-post.d", group_name, ++ "groupadd")) { ++ exit(1); ++ } ++ + + nscd_flush_cache ("group"); + sssd_flush_cache (SSSD_DB_GROUP); +diff --git a/src/groupdel.c b/src/groupdel.c +index fdccf5e15..bae4367ba 100644 +--- a/src/groupdel.c ++++ b/src/groupdel.c +@@ -32,6 +32,7 @@ + #include "sgroupio.h" + #endif + #include "shadowlog.h" ++#include "run_part.h" + /* + * Global variables + */ +@@ -461,6 +462,11 @@ int main (int argc, char **argv) + group_busy (group_id); + } + ++ if (run_parts ("/etc/shadow-maint/groupdel-pre.d", group_name, ++ "groupdel")) { ++ exit(1); ++ } ++ + /* + * Do the hard stuff - open the files, delete the group entries, + * then close and update the files. +@@ -471,6 +477,11 @@ int main (int argc, char **argv) + + close_files (); + ++ if (run_parts ("/etc/shadow-maint/groupdel-post.d", group_name, ++ "groupdel")) { ++ exit(1); ++ } ++ + nscd_flush_cache ("group"); + sssd_flush_cache (SSSD_DB_GROUP); + diff --git a/backport-useradd-check-MLS-enablement-before-setting-serange.patch b/backport-useradd-check-MLS-enablement-before-setting-serange.patch new file mode 100644 index 0000000000000000000000000000000000000000..02d384121dc3958c2cf1e85059757c24c6008b84 --- /dev/null +++ b/backport-useradd-check-MLS-enablement-before-setting-serange.patch @@ -0,0 +1,60 @@ +From 23634d8de7d01ed65bd70e316d4da4fe4d9b370d Mon Sep 17 00:00:00 2001 +From: genBTC +Date: Tue, 23 Aug 2022 10:25:51 -0400 +Subject: [PATCH] useradd: check MLS enablement before setting serange + Resolves: https://github.com/shadow-maint/shadow/issues/552 + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/23634d8de7d01ed65bd70e316d4da4fe4d9b370d + +--- + lib/semanage.c | 29 ++++++++++++++++------------- + 1 file changed, 16 insertions(+), 13 deletions(-) + +diff --git a/lib/semanage.c b/lib/semanage.c +index 54f996238..082a6e8ee 100644 +--- a/lib/semanage.c ++++ b/lib/semanage.c +@@ -122,12 +122,14 @@ static int semanage_user_mod (semanage_handle_t *handle, + goto done; + } + +- ret = semanage_seuser_set_mlsrange (handle, seuser, DEFAULT_SERANGE); +- if (ret != 0) { +- fprintf (shadow_logfd, +- _("Could not set serange for %s\n"), login_name); +- ret = 1; +- goto done; ++ if (semanage_mls_enabled(handle)) { ++ ret = semanage_seuser_set_mlsrange (handle, seuser, DEFAULT_SERANGE); ++ if (ret != 0) { ++ fprintf (shadow_logfd, ++ _("Could not set serange for %s\n"), login_name); ++ ret = 1; ++ goto done; ++ } + } + + ret = semanage_seuser_set_sename (handle, seuser, seuser_name); +@@ -179,13 +181,14 @@ static int semanage_user_add (semanage_handle_t *handle, + goto done; + } + +- ret = semanage_seuser_set_mlsrange (handle, seuser, DEFAULT_SERANGE); +- if (ret != 0) { +- fprintf (shadow_logfd, +- _("Could not set serange for %s\n"), +- login_name); +- ret = 1; +- goto done; ++ if (semanage_mls_enabled(handle)) { ++ ret = semanage_seuser_set_mlsrange (handle, seuser, DEFAULT_SERANGE); ++ if (ret != 0) { ++ fprintf (shadow_logfd, ++ _("Could not set serange for %s\n"), login_name); ++ ret = 1; ++ goto done; ++ } + } + + ret = semanage_seuser_set_sename (handle, seuser, seuser_name); diff --git a/backport-useradd-check-if-subid-range-exists-for-user.patch b/backport-useradd-check-if-subid-range-exists-for-user.patch new file mode 100644 index 0000000000000000000000000000000000000000..c5c03ad98422df289a3f47abc45c93e51033bf15 --- /dev/null +++ b/backport-useradd-check-if-subid-range-exists-for-user.patch @@ -0,0 +1,39 @@ +From e0524e813a3bae2891b33a66f35876841c11cee7 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Mon, 24 Oct 2022 10:46:36 +0200 +Subject: [PATCH] useradd: check if subid range exists for user + +Check if a user already has a subid range before assigning one. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2012929 + +Signed-off-by: Iker Pedrosa + +Conflict: NA +Reference: https://github.com/shadow-maint/shadow/commit/f6f8bcd2a57c06983296485cc028ebdf467ebfd7 + +--- + src/useradd.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/useradd.c b/src/useradd.c +index 7ea0a9c4d..e784d6029 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -2188,14 +2188,14 @@ static void usr_update (unsigned long subuid_count, unsigned long subgid_count) + fail_exit (E_PW_UPDATE); + } + #ifdef ENABLE_SUBIDS +- if (is_sub_uid && ++ if (is_sub_uid && !local_sub_uid_assigned(user_name) && + (sub_uid_add(user_name, sub_uid_start, subuid_count) == 0)) { + fprintf (stderr, + _("%s: failed to prepare the new %s entry\n"), + Prog, sub_uid_dbname ()); + fail_exit (E_SUB_UID_UPDATE); + } +- if (is_sub_gid && ++ if (is_sub_gid && !local_sub_gid_assigned(user_name) && + (sub_gid_add(user_name, sub_gid_start, subgid_count) == 0)) { + fprintf (stderr, + _("%s: failed to prepare the new %s entry\n"), diff --git a/shadow.spec b/shadow.spec index 896d5123424c33b0e082355aea92d4675f662fbb..4054b5c900da5f00c49fa70decef060b50be3f01 100644 --- a/shadow.spec +++ b/shadow.spec @@ -1,6 +1,6 @@ Name: shadow Version: 4.9 -Release: 13 +Release: 14 Epoch: 2 License: BSD and GPLv2+ Summary: Tools for managing accounts and shadow password files @@ -48,30 +48,43 @@ Patch28: backport-libmisc-add-check-fopen-return-value-in-read_random_.patch Patch29: backport-passwd-erase-password-copy-on-all-error-branches.patch Patch30: backport-chpasswd-add-get_salt-for-generating-salt-value.patch Patch31: backport-chpasswd-fix-function-problem-with-R-parameter.patch -Patch32: backport-Fix-off-by-one-mistakes.patch -Patch33: backport-Fix-typos-in-length-calculations.patch -Patch34: backport-Correctly-handle-illegal-system-file-in-tz.patch -Patch35: backport-Explicitly-override-only-newlines.patch -Patch36: backport-Prevent-out-of-boundary-access.patch -Patch37: backport-Added-control-character-check.patch -Patch38: backport-Overhaul-valid_field.patch -Patch39: backport-Read-whole-line-in-yes_or_no.patch -Patch40: backport-commonio-free-removed-database-entries.patch -Patch41: backport-semanage-disconnect-to-free-libsemanage-internals.patch -Patch42: shadow-Remove-encrypted-passwd-for-useradd-gr.patch -Patch43: backport-process_prefix_flag-Drop-privileges.patch -Patch44: backport-chsh-Verify-that-login-shell-path-is-absolute.patch -Patch45: backport-Plug-econf-memory-leaks.patch -Patch46: backport-def_load-avoid-NULL-deref.patch -Patch47: backport-Check-if-crypt_method-null-before-dereferencing.patch -Patch48: backport-usermod-fix-off-by-one-issues.patch -Patch49: backport-gpasswd-1-Fix-password-leak.patch -Patch50: backport-chgpasswd-fix-segfault-in-command-line-options.patch -Patch51: backport-chpasswd-add-IS_CRYPT_METHOD.patch -Patch52: backport-Fix-yescrypt-support.patch -Patch53: backport-newgrp-fix-potential-string-injection.patch -Patch54: backport-script-to-kill-subjects-processes-from-userdel.patch -Patch55: backport-shadow-userdel-add-the-adaptation-to-the-busybox-ps-.patch +Patch32: backport-script-to-kill-subjects-processes-from-userdel.patch +Patch32: backport-Avoid-races-in-chown_tree.patch +Patch33: backport-Avoid-races-in-remove_tree.patch +Patch34: backport-Require-symlink-support.patch +Patch35: backport-Fail-if-regular-file-pre-exists-in-copy_tree.patch +Patch36: backport-More-robust-file-content-copy-in-copy_tree.patch +Patch37: backport-Address-minor-compiler-warnings.patch +Patch38: backport-Avoid-races-in-copy_tree.patch +Patch39: backport-useradd-check-MLS-enablement-before-setting-serange.patch +Patch40: backport-libmisc-minimum-id-check-for-system-accounts.patch +Patch41: backort-useradd-Fix-buffer-overflow-when-using-a-prefix.patch +Patch42: backport-useradd-check-if-subid-range-exists-for-user.patch +Patch43: backport-Fix-off-by-one-mistakes.patch +Patch44: backport-Fix-typos-in-length-calculations.patch +Patch45: backport-Correctly-handle-illegal-system-file-in-tz.patch +Patch46: backport-Explicitly-override-only-newlines.patch +Patch47: backport-Prevent-out-of-boundary-access.patch +Patch48: backport-Added-control-character-check.patch +Patch49: backport-Overhaul-valid_field.patch +Patch50: backport-Read-whole-line-in-yes_or_no.patch +Patch51: backport-run_parts-for-groupadd-and-groupdel.patch +Patch52: backport-commonio-free-removed-database-entries.patch +Patch53: backport-semanage-disconnect-to-free-libsemanage-internals.patch +Patch54: backport-process_prefix_flag-Drop-privileges.patch +Patch55: backport-chsh-Verify-that-login-shell-path-is-absolute.patch +Patch56: backport-Plug-econf-memory-leaks.patch +Patch57: backport-def_load-avoid-NULL-deref.patch +Patch58: backport-Check-if-crypt_method-null-before-dereferencing.patch +Patch59: backport-usermod-fix-off-by-one-issues.patch +Patch60: backport-gpasswd-1-Fix-password-leak.patch +Patch61: backport-chgpasswd-fix-segfault-in-command-line-options.patch +Patch62: backport-chpasswd-add-IS_CRYPT_METHOD.patch +Patch63: backport-Fix-yescrypt-support.patch +Patch64: backport-newgrp-fix-potential-string-injection.patch +Patch65: backport-shadow-userdel-add-the-adaptation-to-the-busybox-ps-.patch +Patch66: shadow-Remove-encrypted-passwd-for-useradd-gr.patch + BuildRequires: gcc, libselinux-devel, audit-libs-devel, libsemanage-devel BuildRequires: libacl-devel, libattr-devel @@ -238,6 +251,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.la %{_mandir}/*/* %changelog +* Tue Nov 17 2023 wangqingsan - 2:4.9-14 +- backport some patches + * Wed Sep 20 2023 wangyunjia - 2:4.9-13 - backport some patches