diff options
author | Purushottam Choudhary <purushottamchoudhary29@gmail.com> | 2022-01-21 18:37:33 +0530 |
---|---|---|
committer | Steve Sakoman <steve@sakoman.com> | 2022-02-07 04:40:13 -1000 |
commit | b7f79fbf23488b954987dfc4aa867e42bdce7fee (patch) | |
tree | 9596857b6bffcc867dd4a7094eb6fdf2edcfc4d1 /meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch | |
parent | dc1aa22cf7287f574e32920cf9fdd4342d171ed1 (diff) | |
download | openembedded-core-b7f79fbf23488b954987dfc4aa867e42bdce7fee.tar.gz |
systemd: Fix CVE-2021-3997
Add patches to fix CVE-2021-3997.
Add additional below mentioned patches which are
required to fix CVE:
1. rm-rf-optionally-fsync-after-removing-directory-tree.patch
2. rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch
Link: http://archive.ubuntu.com/ubuntu/pool/main/s/systemd/systemd_245.4-4ubuntu3.15.debian.tar.xz
Signed-off-by: Purushottam Choudhary <purushottam.choudhary@kpit.com>
Signed-off-by: Purushottam Choudhary <purushottamchoudhary29@gmail.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
Diffstat (limited to 'meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch')
-rw-r--r-- | meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch b/meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch new file mode 100644 index 0000000000..f80e6433c6 --- /dev/null +++ b/meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch @@ -0,0 +1,318 @@ +Backport of the following upstream commit: +From 96906b22417c65d70933976e0ee920c70c9113a4 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering <lennart@poettering.net> +Date: Tue, 26 Jan 2021 16:30:06 +0100 +Subject: [PATCH] rm-rf: refactor rm_rf_children(), split out body of directory + iteration loop + +This splits out rm_rf_children_inner() as body of the loop. We can use +that to implement rm_rf_child() for deleting one specific entry in a +directory. + +Upstream-Status: Backport [http://archive.ubuntu.com/ubuntu/pool/main/s/systemd/systemd_245.4-4ubuntu3.15.debian.tar.xz] +Signed-off-by: Purushottam Choudhary <Purushottam.Choudhary@kpit.com> +--- + src/basic/rm-rf.c | 223 ++++++++++++++++++++++++++------------------- + src/basic/rm-rf.h | 3 +- + 2 files changed, 131 insertions(+), 95 deletions(-) + +--- a/src/basic/rm-rf.c ++++ b/src/basic/rm-rf.c +@@ -19,138 +19,153 @@ + #include "stat-util.h" + #include "string-util.h" + ++/* We treat tmpfs/ramfs + cgroupfs as non-physical file sytems. cgroupfs is similar to tmpfs in a way after ++ * all: we can create arbitrary directory hierarchies in it, and hence can also use rm_rf() on it to remove ++ * those again. */ + static bool is_physical_fs(const struct statfs *sfs) { + return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs); + } + +-int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { ++static int rm_rf_children_inner( ++ int fd, ++ const char *fname, ++ int is_dir, ++ RemoveFlags flags, ++ const struct stat *root_dev) { ++ ++ struct stat st; ++ int r; ++ ++ assert(fd >= 0); ++ assert(fname); ++ ++ if (is_dir < 0 || (is_dir > 0 && (root_dev || (flags & REMOVE_SUBVOLUME)))) { ++ ++ r = fstatat(fd, fname, &st, AT_SYMLINK_NOFOLLOW); ++ if (r < 0) ++ return r; ++ ++ is_dir = S_ISDIR(st.st_mode); ++ } ++ ++ if (is_dir) { ++ _cleanup_close_ int subdir_fd = -1; ++ int q; ++ ++ /* if root_dev is set, remove subdirectories only if device is same */ ++ if (root_dev && st.st_dev != root_dev->st_dev) ++ return 0; ++ ++ /* Stop at mount points */ ++ r = fd_is_mount_point(fd, fname, 0); ++ if (r < 0) ++ return r; ++ if (r > 0) ++ return 0; ++ ++ if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { ++ ++ /* This could be a subvolume, try to remove it */ ++ ++ r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); ++ if (r < 0) { ++ if (!IN_SET(r, -ENOTTY, -EINVAL)) ++ return r; ++ ++ /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */ ++ } else ++ /* It was a subvolume, done. */ ++ return 1; ++ } ++ ++ subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); ++ if (subdir_fd < 0) ++ return -errno; ++ ++ /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file system type ++ * again for each directory */ ++ q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); ++ ++ r = unlinkat(fd, fname, AT_REMOVEDIR); ++ if (r < 0) ++ return r; ++ if (q < 0) ++ return q; ++ ++ return 1; ++ ++ } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { ++ r = unlinkat(fd, fname, 0); ++ if (r < 0) ++ return r; ++ ++ return 1; ++ } ++ ++ return 0; ++} ++ ++int rm_rf_children( ++ int fd, ++ RemoveFlags flags, ++ const struct stat *root_dev) { ++ + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int ret = 0, r; +- struct statfs sfs; + + assert(fd >= 0); + + /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed +- * fd, in all cases, including on failure.. */ ++ * fd, in all cases, including on failure. */ ++ ++ d = fdopendir(fd); ++ if (!d) { ++ safe_close(fd); ++ return -errno; ++ } + + if (!(flags & REMOVE_PHYSICAL)) { ++ struct statfs sfs; + +- r = fstatfs(fd, &sfs); +- if (r < 0) { +- safe_close(fd); ++ if (fstatfs(dirfd(d), &sfs) < 0) + return -errno; + } + + if (is_physical_fs(&sfs)) { +- /* We refuse to clean physical file systems with this call, +- * unless explicitly requested. This is extra paranoia just +- * to be sure we never ever remove non-state data. */ ++ /* We refuse to clean physical file systems with this call, unless explicitly ++ * requested. This is extra paranoia just to be sure we never ever remove non-state ++ * data. */ ++ + _cleanup_free_ char *path = NULL; + + (void) fd_get_path(fd, &path); +- log_error("Attempted to remove disk file system under \"%s\", and we can't allow that.", +- strna(path)); +- +- safe_close(fd); +- return -EPERM; ++ return log_error_errno(SYNTHETIC_ERRNO(EPERM), ++ "Attempted to remove disk file system under \"%s\", and we can't allow that.", ++ strna(path)); + } + } + +- d = fdopendir(fd); +- if (!d) { +- safe_close(fd); +- return errno == ENOENT ? 0 : -errno; +- } +- + FOREACH_DIRENT_ALL(de, d, return -errno) { +- bool is_dir; +- struct stat st; ++ int is_dir; + + if (dot_or_dot_dot(de->d_name)) + continue; + +- if (de->d_type == DT_UNKNOWN || +- (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) { +- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { +- if (ret == 0 && errno != ENOENT) +- ret = -errno; +- continue; +- } +- +- is_dir = S_ISDIR(st.st_mode); +- } else +- is_dir = de->d_type == DT_DIR; +- +- if (is_dir) { +- _cleanup_close_ int subdir_fd = -1; +- +- /* if root_dev is set, remove subdirectories only if device is same */ +- if (root_dev && st.st_dev != root_dev->st_dev) +- continue; +- +- subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); +- if (subdir_fd < 0) { +- if (ret == 0 && errno != ENOENT) +- ret = -errno; +- continue; +- } +- +- /* Stop at mount points */ +- r = fd_is_mount_point(fd, de->d_name, 0); +- if (r < 0) { +- if (ret == 0 && r != -ENOENT) +- ret = r; +- +- continue; +- } +- if (r > 0) +- continue; +- +- if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { +- +- /* This could be a subvolume, try to remove it */ +- +- r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); +- if (r < 0) { +- if (!IN_SET(r, -ENOTTY, -EINVAL)) { +- if (ret == 0) +- ret = r; +- +- continue; +- } +- +- /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */ +- } else +- /* It was a subvolume, continue. */ +- continue; +- } +- +- /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file +- * system type again for each directory */ +- r = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); +- if (r < 0 && ret == 0) +- ret = r; +- +- if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { +- if (ret == 0 && errno != ENOENT) +- ret = -errno; +- } +- +- } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { +- +- if (unlinkat(fd, de->d_name, 0) < 0) { +- if (ret == 0 && errno != ENOENT) +- ret = -errno; +- } +- } ++ is_dir = ++ de->d_type == DT_UNKNOWN ? -1 : ++ de->d_type == DT_DIR; ++ ++ r = rm_rf_children_inner(dirfd(d), de->d_name, is_dir, flags, root_dev); ++ if (r < 0 && r != -ENOENT && ret == 0) ++ ret = r; + } ++ + return ret; + } + + int rm_rf(const char *path, RemoveFlags flags) { + int fd, r; +- struct statfs s; + + assert(path); + +@@ -195,9 +210,10 @@ + if (FLAGS_SET(flags, REMOVE_ROOT)) { + + if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) { ++ struct statfs s; ++ + if (statfs(path, &s) < 0) + return -errno; +- + if (is_physical_fs(&s)) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), + "Attempted to remove files from a disk file system under \"%s\", refusing.", +@@ -225,3 +241,22 @@ + + return r; + } ++ ++int rm_rf_child(int fd, const char *name, RemoveFlags flags) { ++ ++ /* Removes one specific child of the specified directory */ ++ ++ if (fd < 0) ++ return -EBADF; ++ ++ if (!filename_is_valid(name)) ++ return -EINVAL; ++ ++ if ((flags & (REMOVE_ROOT|REMOVE_MISSING_OK)) != 0) /* Doesn't really make sense here, we are not supposed to remove 'fd' anyway */ ++ return -EINVAL; ++ ++ if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME)) ++ return -EINVAL; ++ ++ return rm_rf_children_inner(fd, name, -1, flags, NULL); ++} +--- a/src/basic/rm-rf.h ++++ b/src/basic/rm-rf.h +@@ -13,7 +13,8 @@ + REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */ + } RemoveFlags; + +-int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev); ++int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev); ++int rm_rf_child(int fd, const char *name, RemoveFlags flags); + int rm_rf(const char *path, RemoveFlags flags); + + /* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */ |