lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <7ad4218faaf60b632329c86b819b4615b6c6f121.1659003817.git.noodles@fb.com>
Date:   Thu, 28 Jul 2022 14:09:22 +0000
From:   Jonathan McDowell <noodles@...com>
To:     "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
        "linux-fsdevel@...r.kernel.org" <linux-fsdevel@...r.kernel.org>,
        "linux-integrity@...r.kernel.org" <linux-integrity@...r.kernel.org>,
        "linux-security-module@...r.kernel.org" 
        <linux-security-module@...r.kernel.org>
CC:     Alexander Viro <viro@...iv.linux.org.uk>,
        Mimi Zohar <zohar@...ux.ibm.com>,
        Dmitry Kasatkin <dmitry.kasatkin@...il.com>,
        James Morris <jmorris@...ei.org>,
        "Serge E. Hallyn" <serge@...lyn.com>,
        Matthew Garrett <mjg59@...f.ucam.org>,
        Dmitrii Potoskuev <dpotoskuev@...com>
Subject: [RFC PATCH v2 3/7] lib/cpio: use non __init filesystem related
 functions

In preparation for making the cpio functions generally available rather
than just at init make sure we're using versions of the filesystem
related functions that aren't in the __init section. Remove functions
only used by us from fs/init.c while folding into the cpio code
directly.

Signed-off-by: Jonathan McDowell <noodles@...com>
---
v2:
- Fold in directory EEXIST checking from patch 4
- Add EEXIST checking for device nodes (kernel test reboot boot test)
---
 fs/init.c                     | 101 ---------------------
 fs/internal.h                 |   4 -
 include/linux/fs.h            |   4 +
 include/linux/init_syscalls.h |   6 --
 lib/cpio.c                    | 162 +++++++++++++++++++++++++++++-----
 5 files changed, 145 insertions(+), 132 deletions(-)

diff --git a/fs/init.c b/fs/init.c
index 5c36adaa9b44..a946ad672dee 100644
--- a/fs/init.c
+++ b/fs/init.c
@@ -79,37 +79,6 @@ int __init init_chroot(const char *filename)
 	return error;
 }
 
-int __init init_chown(const char *filename, uid_t user, gid_t group, int flags)
-{
-	int lookup_flags = (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
-	struct path path;
-	int error;
-
-	error = kern_path(filename, lookup_flags, &path);
-	if (error)
-		return error;
-	error = mnt_want_write(path.mnt);
-	if (!error) {
-		error = chown_common(&path, user, group);
-		mnt_drop_write(path.mnt);
-	}
-	path_put(&path);
-	return error;
-}
-
-int __init init_chmod(const char *filename, umode_t mode)
-{
-	struct path path;
-	int error;
-
-	error = kern_path(filename, LOOKUP_FOLLOW, &path);
-	if (error)
-		return error;
-	error = chmod_common(&path, mode);
-	path_put(&path);
-	return error;
-}
-
 int __init init_eaccess(const char *filename)
 {
 	struct path path;
@@ -163,58 +132,6 @@ int __init init_mknod(const char *filename, umode_t mode, unsigned int dev)
 	return error;
 }
 
-int __init init_link(const char *oldname, const char *newname)
-{
-	struct dentry *new_dentry;
-	struct path old_path, new_path;
-	struct user_namespace *mnt_userns;
-	int error;
-
-	error = kern_path(oldname, 0, &old_path);
-	if (error)
-		return error;
-
-	new_dentry = kern_path_create(AT_FDCWD, newname, &new_path, 0);
-	error = PTR_ERR(new_dentry);
-	if (IS_ERR(new_dentry))
-		goto out;
-
-	error = -EXDEV;
-	if (old_path.mnt != new_path.mnt)
-		goto out_dput;
-	mnt_userns = mnt_user_ns(new_path.mnt);
-	error = may_linkat(mnt_userns, &old_path);
-	if (unlikely(error))
-		goto out_dput;
-	error = security_path_link(old_path.dentry, &new_path, new_dentry);
-	if (error)
-		goto out_dput;
-	error = vfs_link(old_path.dentry, mnt_userns, new_path.dentry->d_inode,
-			 new_dentry, NULL);
-out_dput:
-	done_path_create(&new_path, new_dentry);
-out:
-	path_put(&old_path);
-	return error;
-}
-
-int __init init_symlink(const char *oldname, const char *newname)
-{
-	struct dentry *dentry;
-	struct path path;
-	int error;
-
-	dentry = kern_path_create(AT_FDCWD, newname, &path, 0);
-	if (IS_ERR(dentry))
-		return PTR_ERR(dentry);
-	error = security_path_symlink(&path, dentry, oldname);
-	if (!error)
-		error = vfs_symlink(mnt_user_ns(path.mnt), path.dentry->d_inode,
-				    dentry, oldname);
-	done_path_create(&path, dentry);
-	return error;
-}
-
 int __init init_unlink(const char *pathname)
 {
 	return do_unlinkat(AT_FDCWD, getname_kernel(pathname));
@@ -239,24 +156,6 @@ int __init init_mkdir(const char *pathname, umode_t mode)
 	return error;
 }
 
-int __init init_rmdir(const char *pathname)
-{
-	return do_rmdir(AT_FDCWD, getname_kernel(pathname));
-}
-
-int __init init_utimes(char *filename, struct timespec64 *ts)
-{
-	struct path path;
-	int error;
-
-	error = kern_path(filename, 0, &path);
-	if (error)
-		return error;
-	error = vfs_utimes(&path, ts);
-	path_put(&path);
-	return error;
-}
-
 int __init init_dup(struct file *file)
 {
 	int fd;
diff --git a/fs/internal.h b/fs/internal.h
index 87e96b9024ce..c57d5f0aa731 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -60,9 +60,6 @@ extern int filename_lookup(int dfd, struct filename *name, unsigned flags,
 			   struct path *path, struct path *root);
 extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
 			   const char *, unsigned int, struct path *);
-int do_rmdir(int dfd, struct filename *name);
-int do_unlinkat(int dfd, struct filename *name);
-int may_linkat(struct user_namespace *mnt_userns, struct path *link);
 int do_renameat2(int olddfd, struct filename *oldname, int newdfd,
 		 struct filename *newname, unsigned int flags);
 int do_mkdirat(int dfd, struct filename *name, umode_t mode);
@@ -132,7 +129,6 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small);
 int chmod_common(const struct path *path, umode_t mode);
 int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group,
 		int flag);
-int chown_common(const struct path *path, uid_t user, gid_t group);
 extern int vfs_open(const struct path *, struct file *);
 
 /*
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 9ad5e3520fae..1cb51a54799b 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2636,11 +2636,15 @@ static inline struct file *file_clone_open(struct file *file)
 	return dentry_open(&file->f_path, file->f_flags, file->f_cred);
 }
 extern int filp_close(struct file *, fl_owner_t id);
+extern int chown_common(const struct path *path, uid_t user, gid_t group);
 
+extern int do_rmdir(int dfd, struct filename *name);
+extern int do_unlinkat(int dfd, struct filename *name);
 extern struct filename *getname_flags(const char __user *, int, int *);
 extern struct filename *getname_uflags(const char __user *, int);
 extern struct filename *getname(const char __user *);
 extern struct filename *getname_kernel(const char *);
+extern int may_linkat(struct user_namespace *mnt_userns, struct path *link);
 extern void putname(struct filename *name);
 
 extern int finish_open(struct file *file, struct dentry *dentry,
diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h
index 92045d18cbfc..196030cd958d 100644
--- a/include/linux/init_syscalls.h
+++ b/include/linux/init_syscalls.h
@@ -5,15 +5,9 @@ int __init init_mount(const char *dev_name, const char *dir_name,
 int __init init_umount(const char *name, int flags);
 int __init init_chdir(const char *filename);
 int __init init_chroot(const char *filename);
-int __init init_chown(const char *filename, uid_t user, gid_t group, int flags);
-int __init init_chmod(const char *filename, umode_t mode);
 int __init init_eaccess(const char *filename);
 int __init init_stat(const char *filename, struct kstat *stat, int flags);
 int __init init_mknod(const char *filename, umode_t mode, unsigned int dev);
-int __init init_link(const char *oldname, const char *newname);
-int __init init_symlink(const char *oldname, const char *newname);
 int __init init_unlink(const char *pathname);
 int __init init_mkdir(const char *pathname, umode_t mode);
-int __init init_rmdir(const char *pathname);
-int __init init_utimes(char *filename, struct timespec64 *ts);
 int __init init_dup(struct file *file);
diff --git a/lib/cpio.c b/lib/cpio.c
index 5d150939704f..9a0120c638db 100644
--- a/lib/cpio.c
+++ b/lib/cpio.c
@@ -3,8 +3,9 @@
 #include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/init.h>
-#include <linux/init_syscalls.h>
 #include <linux/list.h>
+#include <linux/namei.h>
+#include <linux/security.h>
 #include <linux/slab.h>
 
 static ssize_t __init xwrite(struct cpio_context *ctx, struct file *file,
@@ -92,18 +93,25 @@ static void __init free_hash(struct cpio_context *ctx)
 }
 
 #ifdef CONFIG_INITRAMFS_PRESERVE_MTIME
-static void __init do_utime(char *filename, time64_t mtime)
+static void __init do_utime_path(const struct path *path, time64_t mtime)
 {
 	struct timespec64 t[2] = { { .tv_sec = mtime }, { .tv_sec = mtime } };
 
-	init_utimes(filename, t);
+	vfs_utimes(path, t);
 }
 
-static void __init do_utime_path(const struct path *path, time64_t mtime)
+static int __init do_utime(char *filename, time64_t mtime)
 {
-	struct timespec64 t[2] = { { .tv_sec = mtime }, { .tv_sec = mtime } };
+	struct path path;
+	int error;
 
-	vfs_utimes(path, t);
+	error = kern_path(filename, 0, &path);
+	if (error)
+		return error;
+	do_utime_path(&path, mtime);
+	path_put(&path);
+
+	return error;
 }
 
 static int __init dir_add(struct cpio_context *ctx, const char *name, time64_t mtime)
@@ -133,12 +141,31 @@ static void __init dir_utime(struct cpio_context *ctx)
 	}
 }
 #else
-static void __init do_utime(char *filename, time64_t mtime) {}
+static int __init do_utime(char *filename, time64_t mtime) { return 0; }
 static void __init do_utime_path(const struct path *path, time64_t mtime) {}
 static int __init dir_add(struct cpio_context *ctx, const char *name, time64_t mtime) { return 0; }
 static void __init dir_utime(struct cpio_context *ctx) {}
 #endif
 
+static int __init cpio_chown(const char *filename, uid_t user, gid_t group,
+			     int flags)
+{
+	int lookup_flags = (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
+	struct path path;
+	int error;
+
+	error = kern_path(filename, lookup_flags, &path);
+	if (error)
+		return error;
+	error = mnt_want_write(path.mnt);
+	if (!error) {
+		error = chown_common(&path, user, group);
+		mnt_drop_write(path.mnt);
+	}
+	path_put(&path);
+	return error;
+}
+
 /* cpio header parsing */
 
 static void __init parse_header(struct cpio_context *ctx, char *s)
@@ -269,27 +296,67 @@ static int __init do_reset(struct cpio_context *ctx)
 	return 1;
 }
 
-static void __init clean_path(char *path, umode_t fmode)
+static void __init clean_path(char *pathname, umode_t fmode)
 {
+	struct path path;
 	struct kstat st;
+	int error;
 
-	if (!init_stat(path, &st, AT_SYMLINK_NOFOLLOW) &&
-	    (st.mode ^ fmode) & S_IFMT) {
+	error = kern_path(pathname, 0, &path);
+	if (error)
+		return;
+	error = vfs_getattr(&path, &st, STATX_BASIC_STATS, AT_NO_AUTOMOUNT);
+	path_put(&path);
+	if (error)
+		return;
+
+	if ((st.mode ^ fmode) & S_IFMT) {
 		if (S_ISDIR(st.mode))
-			init_rmdir(path);
+			do_rmdir(AT_FDCWD, getname_kernel(pathname));
 		else
-			init_unlink(path);
+			do_unlinkat(AT_FDCWD, getname_kernel(pathname));
 	}
 }
 
 static int __init maybe_link(struct cpio_context *ctx)
 {
+	struct dentry *new_dentry;
+	struct path old_path, new_path;
+	struct user_namespace *mnt_userns;
+	int error;
+
 	if (ctx->nlink >= 2) {
 		char *old = find_link(ctx, ctx->major, ctx->minor, ctx->ino,
 				ctx->mode, ctx->collected);
 		if (old) {
 			clean_path(ctx->collected, 0);
-			return (init_link(old, ctx->collected) < 0) ? -1 : 1;
+
+			error = kern_path(old, 0, &old_path);
+			if (error)
+				return error;
+
+			new_dentry = kern_path_create(AT_FDCWD, ctx->collected, &new_path, 0);
+			error = PTR_ERR(new_dentry);
+			if (IS_ERR(new_dentry))
+				goto out;
+
+			error = -EXDEV;
+			if (old_path.mnt != new_path.mnt)
+				goto out_dput;
+			mnt_userns = mnt_user_ns(new_path.mnt);
+			error = may_linkat(mnt_userns, &old_path);
+			if (unlikely(error))
+				goto out_dput;
+			error = security_path_link(old_path.dentry, &new_path, new_dentry);
+			if (error)
+				goto out_dput;
+			error = vfs_link(old_path.dentry, mnt_userns, new_path.dentry->d_inode,
+					 new_dentry, NULL);
+out_dput:
+			done_path_create(&new_path, new_dentry);
+out:
+			path_put(&old_path);
+			return (error < 0) ? error : 1;
 		}
 	}
 	return 0;
@@ -297,6 +364,10 @@ static int __init maybe_link(struct cpio_context *ctx)
 
 static int __init do_name(struct cpio_context *ctx)
 {
+	struct dentry *dentry;
+	struct path path;
+	int error;
+
 	ctx->state = CPIO_SKIPIT;
 	ctx->next_state = CPIO_RESET;
 	if (strcmp(ctx->collected, "TRAILER!!!") == 0) {
@@ -325,16 +396,48 @@ static int __init do_name(struct cpio_context *ctx)
 			ctx->state = CPIO_COPYFILE;
 		}
 	} else if (S_ISDIR(ctx->mode)) {
-		init_mkdir(ctx->collected, ctx->mode);
-		init_chown(ctx->collected, ctx->uid, ctx->gid, 0);
-		init_chmod(ctx->collected, ctx->mode);
+		dentry = kern_path_create(AT_FDCWD, ctx->collected, &path, LOOKUP_DIRECTORY);
+
+		if (!IS_ERR(dentry)) {
+			error = security_path_mkdir(&path, dentry, ctx->mode);
+			if (!error)
+				error = vfs_mkdir(mnt_user_ns(path.mnt),
+						  path.dentry->d_inode,
+						  dentry, ctx->mode);
+			done_path_create(&path, dentry);
+		} else {
+			error = PTR_ERR(dentry);
+		}
+
+		if (error && error != -EEXIST)
+			return error;
+
+		cpio_chown(ctx->collected, ctx->uid, ctx->gid, 0);
 		dir_add(ctx, ctx->collected, ctx->mtime);
 	} else if (S_ISBLK(ctx->mode) || S_ISCHR(ctx->mode) ||
 		   S_ISFIFO(ctx->mode) || S_ISSOCK(ctx->mode)) {
 		if (maybe_link(ctx) == 0) {
-			init_mknod(ctx->collected, ctx->mode, ctx->rdev);
-			init_chown(ctx->collected, ctx->uid, ctx->gid, 0);
-			init_chmod(ctx->collected, ctx->mode);
+			if (S_ISFIFO(ctx->mode) || S_ISSOCK(ctx->mode))
+				ctx->rdev = 0;
+
+			dentry = kern_path_create(AT_FDCWD, ctx->collected, &path, 0);
+			if (!IS_ERR(dentry)) {
+				error = security_path_mknod(&path, dentry, ctx->mode,
+							    ctx->rdev);
+				if (!error)
+					error = vfs_mknod(mnt_user_ns(path.mnt),
+							  path.dentry->d_inode,
+							  dentry, ctx->mode,
+							  new_decode_dev(ctx->rdev));
+				done_path_create(&path, dentry);
+			} else {
+				error = PTR_ERR(dentry);
+			}
+
+			if (error && error != -EEXIST)
+				return error;
+
+			cpio_chown(ctx->collected, ctx->uid, ctx->gid, 0);
 			do_utime(ctx->collected, ctx->mtime);
 		}
 	}
@@ -373,10 +476,27 @@ static int __init do_copy(struct cpio_context *ctx)
 
 static int __init do_symlink(struct cpio_context *ctx)
 {
+	struct dentry *dentry;
+	struct path path;
+	int error;
+
 	ctx->collected[N_ALIGN(ctx->name_len) + ctx->body_len] = '\0';
 	clean_path(ctx->collected, 0);
-	init_symlink(ctx->collected + N_ALIGN(ctx->name_len), ctx->collected);
-	init_chown(ctx->collected, ctx->uid, ctx->gid, AT_SYMLINK_NOFOLLOW);
+
+	dentry = kern_path_create(AT_FDCWD, ctx->collected, &path, 0);
+	if (IS_ERR(dentry))
+		return PTR_ERR(dentry);
+	error = security_path_symlink(&path, dentry,
+				      ctx->collected + N_ALIGN(ctx->name_len));
+	if (!error)
+		error = vfs_symlink(mnt_user_ns(path.mnt), path.dentry->d_inode,
+				    dentry,
+				    ctx->collected + N_ALIGN(ctx->name_len));
+	done_path_create(&path, dentry);
+	if (error)
+		return error;
+
+	cpio_chown(ctx->collected, ctx->uid, ctx->gid, AT_SYMLINK_NOFOLLOW);
 	do_utime(ctx->collected, ctx->mtime);
 	ctx->state = CPIO_SKIPIT;
 	ctx->next_state = CPIO_RESET;
-- 
2.30.2

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ