5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 1) // SPDX-License-Identifier: GPL-2.0-only
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 2) #include <linux/fs.h>
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 3) #include <linux/fs_struct.h>
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 4) #include <linux/kernel_read_file.h>
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 5) #include <linux/security.h>
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 6) #include <linux/vmalloc.h>
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 7)
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 8) /**
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 9) * kernel_read_file() - read file contents into a kernel buffer
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 10) *
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 11) * @file file to read from
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 12) * @offset where to start reading from (see below).
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 13) * @buf pointer to a "void *" buffer for reading into (if
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 14) * *@buf is NULL, a buffer will be allocated, and
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 15) * @buf_size will be ignored)
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 16) * @buf_size size of buf, if already allocated. If @buf not
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 17) * allocated, this is the largest size to allocate.
885352881f11f (Kees Cook 2020-10-02 10:38:19 -0700 18) * @file_size if non-NULL, the full size of @file will be
885352881f11f (Kees Cook 2020-10-02 10:38:19 -0700 19) * written here.
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 20) * @id the kernel_read_file_id identifying the type of
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 21) * file contents being read (for LSMs to examine)
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 22) *
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 23) * @offset must be 0 unless both @buf and @file_size are non-NULL
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 24) * (i.e. the caller must be expecting to read partial file contents
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 25) * via an already-allocated @buf, in at most @buf_size chunks, and
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 26) * will be able to determine when the entire file was read by
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 27) * checking @file_size). This isn't a recommended way to read a
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 28) * file, though, since it is possible that the contents might
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 29) * change between calls to kernel_read_file().
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 30) *
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 31) * Returns number of bytes read (no single read will be bigger
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 32) * than INT_MAX), or negative on error.
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 33) *
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 34) */
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 35) int kernel_read_file(struct file *file, loff_t offset, void **buf,
885352881f11f (Kees Cook 2020-10-02 10:38:19 -0700 36) size_t buf_size, size_t *file_size,
885352881f11f (Kees Cook 2020-10-02 10:38:19 -0700 37) enum kernel_read_file_id id)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 38) {
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 39) loff_t i_size, pos;
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 40) size_t copied;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 41) void *allocated = NULL;
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 42) bool whole_file;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 43) int ret;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 44)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 45) if (offset != 0 && (!*buf || !file_size))
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 46) return -EINVAL;
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 47)
113eeb517780a (Kees Cook 2020-10-02 10:38:18 -0700 48) if (!S_ISREG(file_inode(file)->i_mode))
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 49) return -EINVAL;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 50)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 51) ret = deny_write_access(file);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 52) if (ret)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 53) return ret;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 54)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 55) i_size = i_size_read(file_inode(file));
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 56) if (i_size <= 0) {
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 57) ret = -EINVAL;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 58) goto out;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 59) }
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 60) /* The file is too big for sane activities. */
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 61) if (i_size > INT_MAX) {
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 62) ret = -EFBIG;
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 63) goto out;
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 64) }
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 65) /* The entire file cannot be read in one buffer. */
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 66) if (!file_size && offset == 0 && i_size > buf_size) {
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 67) ret = -EFBIG;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 68) goto out;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 69) }
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 70)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 71) whole_file = (offset == 0 && i_size <= buf_size);
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 72) ret = security_kernel_read_file(file, id, whole_file);
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 73) if (ret)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 74) goto out;
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 75)
885352881f11f (Kees Cook 2020-10-02 10:38:19 -0700 76) if (file_size)
885352881f11f (Kees Cook 2020-10-02 10:38:19 -0700 77) *file_size = i_size;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 78)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 79) if (!*buf)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 80) *buf = allocated = vmalloc(i_size);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 81) if (!*buf) {
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 82) ret = -ENOMEM;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 83) goto out;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 84) }
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 85)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 86) pos = offset;
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 87) copied = 0;
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 88) while (copied < buf_size) {
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 89) ssize_t bytes;
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 90) size_t wanted = min_t(size_t, buf_size - copied,
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 91) i_size - pos);
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 92)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 93) bytes = kernel_read(file, *buf + copied, wanted, &pos);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 94) if (bytes < 0) {
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 95) ret = bytes;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 96) goto out_free;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 97) }
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 98)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 99) if (bytes == 0)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 100) break;
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 101) copied += bytes;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 102) }
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 103)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 104) if (whole_file) {
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 105) if (pos != i_size) {
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 106) ret = -EIO;
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 107) goto out_free;
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 108) }
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 109)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 110) ret = security_kernel_post_read_file(file, *buf, i_size, id);
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 111) }
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 112)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 113) out_free:
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 114) if (ret < 0) {
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 115) if (allocated) {
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 116) vfree(*buf);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 117) *buf = NULL;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 118) }
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 119) }
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 120)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 121) out:
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 122) allow_write_access(file);
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 123) return ret == 0 ? copied : ret;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 124) }
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 125) EXPORT_SYMBOL_GPL(kernel_read_file);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 126)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 127) int kernel_read_file_from_path(const char *path, loff_t offset, void **buf,
885352881f11f (Kees Cook 2020-10-02 10:38:19 -0700 128) size_t buf_size, size_t *file_size,
885352881f11f (Kees Cook 2020-10-02 10:38:19 -0700 129) enum kernel_read_file_id id)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 130) {
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 131) struct file *file;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 132) int ret;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 133)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 134) if (!path || !*path)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 135) return -EINVAL;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 136)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 137) file = filp_open(path, O_RDONLY, 0);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 138) if (IS_ERR(file))
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 139) return PTR_ERR(file);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 140)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 141) ret = kernel_read_file(file, offset, buf, buf_size, file_size, id);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 142) fput(file);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 143) return ret;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 144) }
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 145) EXPORT_SYMBOL_GPL(kernel_read_file_from_path);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 146)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 147) int kernel_read_file_from_path_initns(const char *path, loff_t offset,
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 148) void **buf, size_t buf_size,
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 149) size_t *file_size,
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 150) enum kernel_read_file_id id)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 151) {
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 152) struct file *file;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 153) struct path root;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 154) int ret;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 155)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 156) if (!path || !*path)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 157) return -EINVAL;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 158)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 159) task_lock(&init_task);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 160) get_fs_root(init_task.fs, &root);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 161) task_unlock(&init_task);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 162)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 163) file = file_open_root(root.dentry, root.mnt, path, O_RDONLY, 0);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 164) path_put(&root);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 165) if (IS_ERR(file))
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 166) return PTR_ERR(file);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 167)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 168) ret = kernel_read_file(file, offset, buf, buf_size, file_size, id);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 169) fput(file);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 170) return ret;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 171) }
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 172) EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 173)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 174) int kernel_read_file_from_fd(int fd, loff_t offset, void **buf,
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 175) size_t buf_size, size_t *file_size,
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 176) enum kernel_read_file_id id)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 177) {
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 178) struct fd f = fdget(fd);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 179) int ret = -EBADF;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 180)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 181) if (!f.file)
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 182) goto out;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 183)
0fa8e08464877 (Kees Cook 2020-10-02 10:38:25 -0700 184) ret = kernel_read_file(f.file, offset, buf, buf_size, file_size, id);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 185) out:
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 186) fdput(f);
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 187) return ret;
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 188) }
5287b07f6d7cc (Kees Cook 2020-10-02 10:38:16 -0700 189) EXPORT_SYMBOL_GPL(kernel_read_file_from_fd);