blob: e21a76001fff89856680ab37fab406f03a80dcb5 [file] [log] [blame]
Kees Cook5287b072020-10-02 10:38:16 -07001// SPDX-License-Identifier: GPL-2.0-only
2#include <linux/fs.h>
3#include <linux/fs_struct.h>
4#include <linux/kernel_read_file.h>
5#include <linux/security.h>
6#include <linux/vmalloc.h>
7
Kees Cook113eeb52020-10-02 10:38:18 -07008/**
9 * kernel_read_file() - read file contents into a kernel buffer
10 *
11 * @file file to read from
12 * @buf pointer to a "void *" buffer for reading into (if
13 * *@buf is NULL, a buffer will be allocated, and
14 * @buf_size will be ignored)
15 * @buf_size size of buf, if already allocated. If @buf not
16 * allocated, this is the largest size to allocate.
17 * @id the kernel_read_file_id identifying the type of
18 * file contents being read (for LSMs to examine)
19 *
20 * Returns number of bytes read (no single read will be bigger
21 * than INT_MAX), or negative on error.
22 *
23 */
Kees Cookf7a4f682020-10-02 10:38:17 -070024int kernel_read_file(struct file *file, void **buf,
Kees Cook113eeb52020-10-02 10:38:18 -070025 size_t buf_size, enum kernel_read_file_id id)
Kees Cook5287b072020-10-02 10:38:16 -070026{
27 loff_t i_size, pos;
28 ssize_t bytes = 0;
29 void *allocated = NULL;
30 int ret;
31
Kees Cook113eeb52020-10-02 10:38:18 -070032 if (!S_ISREG(file_inode(file)->i_mode))
Kees Cook5287b072020-10-02 10:38:16 -070033 return -EINVAL;
34
35 ret = deny_write_access(file);
36 if (ret)
37 return ret;
38
39 ret = security_kernel_read_file(file, id);
40 if (ret)
41 goto out;
42
43 i_size = i_size_read(file_inode(file));
44 if (i_size <= 0) {
45 ret = -EINVAL;
46 goto out;
47 }
Kees Cook113eeb52020-10-02 10:38:18 -070048 if (i_size > INT_MAX || i_size > buf_size) {
Kees Cook5287b072020-10-02 10:38:16 -070049 ret = -EFBIG;
50 goto out;
51 }
52
53 if (!*buf)
54 *buf = allocated = vmalloc(i_size);
55 if (!*buf) {
56 ret = -ENOMEM;
57 goto out;
58 }
59
60 pos = 0;
61 while (pos < i_size) {
62 bytes = kernel_read(file, *buf + pos, i_size - pos, &pos);
63 if (bytes < 0) {
64 ret = bytes;
65 goto out_free;
66 }
67
68 if (bytes == 0)
69 break;
70 }
71
72 if (pos != i_size) {
73 ret = -EIO;
74 goto out_free;
75 }
76
77 ret = security_kernel_post_read_file(file, *buf, i_size, id);
Kees Cook5287b072020-10-02 10:38:16 -070078
79out_free:
80 if (ret < 0) {
81 if (allocated) {
82 vfree(*buf);
83 *buf = NULL;
84 }
85 }
86
87out:
88 allow_write_access(file);
Kees Cookf7a4f682020-10-02 10:38:17 -070089 return ret == 0 ? pos : ret;
Kees Cook5287b072020-10-02 10:38:16 -070090}
91EXPORT_SYMBOL_GPL(kernel_read_file);
92
Kees Cookf7a4f682020-10-02 10:38:17 -070093int kernel_read_file_from_path(const char *path, void **buf,
Kees Cook113eeb52020-10-02 10:38:18 -070094 size_t buf_size, enum kernel_read_file_id id)
Kees Cook5287b072020-10-02 10:38:16 -070095{
96 struct file *file;
97 int ret;
98
99 if (!path || !*path)
100 return -EINVAL;
101
102 file = filp_open(path, O_RDONLY, 0);
103 if (IS_ERR(file))
104 return PTR_ERR(file);
105
Kees Cook113eeb52020-10-02 10:38:18 -0700106 ret = kernel_read_file(file, buf, buf_size, id);
Kees Cook5287b072020-10-02 10:38:16 -0700107 fput(file);
108 return ret;
109}
110EXPORT_SYMBOL_GPL(kernel_read_file_from_path);
111
112int kernel_read_file_from_path_initns(const char *path, void **buf,
Kees Cook113eeb52020-10-02 10:38:18 -0700113 size_t buf_size,
Kees Cook5287b072020-10-02 10:38:16 -0700114 enum kernel_read_file_id id)
115{
116 struct file *file;
117 struct path root;
118 int ret;
119
120 if (!path || !*path)
121 return -EINVAL;
122
123 task_lock(&init_task);
124 get_fs_root(init_task.fs, &root);
125 task_unlock(&init_task);
126
127 file = file_open_root(root.dentry, root.mnt, path, O_RDONLY, 0);
128 path_put(&root);
129 if (IS_ERR(file))
130 return PTR_ERR(file);
131
Kees Cook113eeb52020-10-02 10:38:18 -0700132 ret = kernel_read_file(file, buf, buf_size, id);
Kees Cook5287b072020-10-02 10:38:16 -0700133 fput(file);
134 return ret;
135}
136EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns);
137
Kees Cook113eeb52020-10-02 10:38:18 -0700138int kernel_read_file_from_fd(int fd, void **buf, size_t buf_size,
Kees Cook5287b072020-10-02 10:38:16 -0700139 enum kernel_read_file_id id)
140{
141 struct fd f = fdget(fd);
142 int ret = -EBADF;
143
144 if (!f.file)
145 goto out;
146
Kees Cook113eeb52020-10-02 10:38:18 -0700147 ret = kernel_read_file(f.file, buf, buf_size, id);
Kees Cook5287b072020-10-02 10:38:16 -0700148out:
149 fdput(f);
150 return ret;
151}
152EXPORT_SYMBOL_GPL(kernel_read_file_from_fd);