Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : /* Filesystem access-by-fd.
3 : : *
4 : : * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
5 : : * Written by David Howells (dhowells@redhat.com)
6 : : */
7 : :
8 : : #include <linux/fs_context.h>
9 : : #include <linux/fs_parser.h>
10 : : #include <linux/slab.h>
11 : : #include <linux/uaccess.h>
12 : : #include <linux/syscalls.h>
13 : : #include <linux/security.h>
14 : : #include <linux/anon_inodes.h>
15 : : #include <linux/namei.h>
16 : : #include <linux/file.h>
17 : : #include <uapi/linux/mount.h>
18 : : #include "internal.h"
19 : : #include "mount.h"
20 : :
21 : : /*
22 : : * Allow the user to read back any error, warning or informational messages.
23 : : */
24 : 0 : static ssize_t fscontext_read(struct file *file,
25 : : char __user *_buf, size_t len, loff_t *pos)
26 : : {
27 : 0 : struct fs_context *fc = file->private_data;
28 : 0 : struct fc_log *log = fc->log;
29 : : unsigned int logsize = ARRAY_SIZE(log->buffer);
30 : : ssize_t ret;
31 : : char *p;
32 : : bool need_free;
33 : : int index, n;
34 : :
35 : 0 : ret = mutex_lock_interruptible(&fc->uapi_mutex);
36 [ # # ]: 0 : if (ret < 0)
37 : : return ret;
38 : :
39 [ # # ]: 0 : if (log->head == log->tail) {
40 : 0 : mutex_unlock(&fc->uapi_mutex);
41 : 0 : return -ENODATA;
42 : : }
43 : :
44 : 0 : index = log->tail & (logsize - 1);
45 : 0 : p = log->buffer[index];
46 : 0 : need_free = log->need_free & (1 << index);
47 : 0 : log->buffer[index] = NULL;
48 : 0 : log->need_free &= ~(1 << index);
49 : 0 : log->tail++;
50 : 0 : mutex_unlock(&fc->uapi_mutex);
51 : :
52 : : ret = -EMSGSIZE;
53 : 0 : n = strlen(p);
54 [ # # ]: 0 : if (n > len)
55 : : goto err_free;
56 : : ret = -EFAULT;
57 [ # # ]: 0 : if (copy_to_user(_buf, p, n) != 0)
58 : : goto err_free;
59 : : ret = n;
60 : :
61 : : err_free:
62 [ # # ]: 0 : if (need_free)
63 : 0 : kfree(p);
64 : 0 : return ret;
65 : : }
66 : :
67 : 0 : static int fscontext_release(struct inode *inode, struct file *file)
68 : : {
69 : 0 : struct fs_context *fc = file->private_data;
70 : :
71 [ # # ]: 0 : if (fc) {
72 : 0 : file->private_data = NULL;
73 : 0 : put_fs_context(fc);
74 : : }
75 : 0 : return 0;
76 : : }
77 : :
78 : : const struct file_operations fscontext_fops = {
79 : : .read = fscontext_read,
80 : : .release = fscontext_release,
81 : : .llseek = no_llseek,
82 : : };
83 : :
84 : : /*
85 : : * Attach a filesystem context to a file and an fd.
86 : : */
87 : 0 : static int fscontext_create_fd(struct fs_context *fc, unsigned int o_flags)
88 : : {
89 : : int fd;
90 : :
91 : 0 : fd = anon_inode_getfd("[fscontext]", &fscontext_fops, fc,
92 : 0 : O_RDWR | o_flags);
93 [ # # ]: 0 : if (fd < 0)
94 : 0 : put_fs_context(fc);
95 : 0 : return fd;
96 : : }
97 : :
98 : 0 : static int fscontext_alloc_log(struct fs_context *fc)
99 : : {
100 : 0 : fc->log = kzalloc(sizeof(*fc->log), GFP_KERNEL);
101 [ # # ]: 0 : if (!fc->log)
102 : : return -ENOMEM;
103 : : refcount_set(&fc->log->usage, 1);
104 : 0 : fc->log->owner = fc->fs_type->owner;
105 : 0 : return 0;
106 : : }
107 : :
108 : : /*
109 : : * Open a filesystem by name so that it can be configured for mounting.
110 : : *
111 : : * We are allowed to specify a container in which the filesystem will be
112 : : * opened, thereby indicating which namespaces will be used (notably, which
113 : : * network namespace will be used for network filesystems).
114 : : */
115 : 0 : SYSCALL_DEFINE2(fsopen, const char __user *, _fs_name, unsigned int, flags)
116 : : {
117 : : struct file_system_type *fs_type;
118 : : struct fs_context *fc;
119 : : const char *fs_name;
120 : : int ret;
121 : :
122 [ # # ]: 0 : if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
123 : : return -EPERM;
124 : :
125 [ # # ]: 0 : if (flags & ~FSOPEN_CLOEXEC)
126 : : return -EINVAL;
127 : :
128 : 0 : fs_name = strndup_user(_fs_name, PAGE_SIZE);
129 [ # # ]: 0 : if (IS_ERR(fs_name))
130 : 0 : return PTR_ERR(fs_name);
131 : :
132 : 0 : fs_type = get_fs_type(fs_name);
133 : 0 : kfree(fs_name);
134 [ # # ]: 0 : if (!fs_type)
135 : : return -ENODEV;
136 : :
137 : 0 : fc = fs_context_for_mount(fs_type, 0);
138 : 0 : put_filesystem(fs_type);
139 [ # # ]: 0 : if (IS_ERR(fc))
140 : 0 : return PTR_ERR(fc);
141 : :
142 : 0 : fc->phase = FS_CONTEXT_CREATE_PARAMS;
143 : :
144 : 0 : ret = fscontext_alloc_log(fc);
145 [ # # ]: 0 : if (ret < 0)
146 : : goto err_fc;
147 : :
148 [ # # ]: 0 : return fscontext_create_fd(fc, flags & FSOPEN_CLOEXEC ? O_CLOEXEC : 0);
149 : :
150 : : err_fc:
151 : 0 : put_fs_context(fc);
152 : 0 : return ret;
153 : : }
154 : :
155 : : /*
156 : : * Pick a superblock into a context for reconfiguration.
157 : : */
158 : 0 : SYSCALL_DEFINE3(fspick, int, dfd, const char __user *, path, unsigned int, flags)
159 : : {
160 : : struct fs_context *fc;
161 : : struct path target;
162 : : unsigned int lookup_flags;
163 : : int ret;
164 : :
165 [ # # ]: 0 : if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
166 : : return -EPERM;
167 : :
168 [ # # ]: 0 : if ((flags & ~(FSPICK_CLOEXEC |
169 : : FSPICK_SYMLINK_NOFOLLOW |
170 : : FSPICK_NO_AUTOMOUNT |
171 : : FSPICK_EMPTY_PATH)) != 0)
172 : : return -EINVAL;
173 : :
174 : : lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
175 [ # # ]: 0 : if (flags & FSPICK_SYMLINK_NOFOLLOW)
176 : : lookup_flags &= ~LOOKUP_FOLLOW;
177 [ # # ]: 0 : if (flags & FSPICK_NO_AUTOMOUNT)
178 : 0 : lookup_flags &= ~LOOKUP_AUTOMOUNT;
179 [ # # ]: 0 : if (flags & FSPICK_EMPTY_PATH)
180 : 0 : lookup_flags |= LOOKUP_EMPTY;
181 : : ret = user_path_at(dfd, path, lookup_flags, &target);
182 [ # # ]: 0 : if (ret < 0)
183 : : goto err;
184 : :
185 : : ret = -EINVAL;
186 [ # # ]: 0 : if (target.mnt->mnt_root != target.dentry)
187 : : goto err_path;
188 : :
189 : 0 : fc = fs_context_for_reconfigure(target.dentry, 0, 0);
190 [ # # ]: 0 : if (IS_ERR(fc)) {
191 : : ret = PTR_ERR(fc);
192 : 0 : goto err_path;
193 : : }
194 : :
195 : 0 : fc->phase = FS_CONTEXT_RECONF_PARAMS;
196 : :
197 : 0 : ret = fscontext_alloc_log(fc);
198 [ # # ]: 0 : if (ret < 0)
199 : : goto err_fc;
200 : :
201 : 0 : path_put(&target);
202 [ # # ]: 0 : return fscontext_create_fd(fc, flags & FSPICK_CLOEXEC ? O_CLOEXEC : 0);
203 : :
204 : : err_fc:
205 : 0 : put_fs_context(fc);
206 : : err_path:
207 : 0 : path_put(&target);
208 : : err:
209 : 0 : return ret;
210 : : }
211 : :
212 : : /*
213 : : * Check the state and apply the configuration. Note that this function is
214 : : * allowed to 'steal' the value by setting param->xxx to NULL before returning.
215 : : */
216 : 0 : static int vfs_fsconfig_locked(struct fs_context *fc, int cmd,
217 : : struct fs_parameter *param)
218 : : {
219 : : struct super_block *sb;
220 : : int ret;
221 : :
222 : 0 : ret = finish_clean_context(fc);
223 [ # # ]: 0 : if (ret)
224 : : return ret;
225 [ # # # ]: 0 : switch (cmd) {
226 : : case FSCONFIG_CMD_CREATE:
227 [ # # ]: 0 : if (fc->phase != FS_CONTEXT_CREATE_PARAMS)
228 : : return -EBUSY;
229 [ # # ]: 0 : if (!mount_capable(fc))
230 : : return -EPERM;
231 : 0 : fc->phase = FS_CONTEXT_CREATING;
232 : 0 : ret = vfs_get_tree(fc);
233 [ # # ]: 0 : if (ret)
234 : : break;
235 : 0 : sb = fc->root->d_sb;
236 : 0 : ret = security_sb_kern_mount(sb);
237 [ # # ]: 0 : if (unlikely(ret)) {
238 : 0 : fc_drop_locked(fc);
239 : 0 : break;
240 : : }
241 : 0 : up_write(&sb->s_umount);
242 : 0 : fc->phase = FS_CONTEXT_AWAITING_MOUNT;
243 : 0 : return 0;
244 : : case FSCONFIG_CMD_RECONFIGURE:
245 [ # # ]: 0 : if (fc->phase != FS_CONTEXT_RECONF_PARAMS)
246 : : return -EBUSY;
247 : 0 : fc->phase = FS_CONTEXT_RECONFIGURING;
248 : 0 : sb = fc->root->d_sb;
249 [ # # ]: 0 : if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) {
250 : : ret = -EPERM;
251 : : break;
252 : : }
253 : 0 : down_write(&sb->s_umount);
254 : 0 : ret = reconfigure_super(fc);
255 : 0 : up_write(&sb->s_umount);
256 [ # # ]: 0 : if (ret)
257 : : break;
258 : 0 : vfs_clean_context(fc);
259 : 0 : return 0;
260 : : default:
261 [ # # ]: 0 : if (fc->phase != FS_CONTEXT_CREATE_PARAMS &&
262 : : fc->phase != FS_CONTEXT_RECONF_PARAMS)
263 : : return -EBUSY;
264 : :
265 : 0 : return vfs_parse_fs_param(fc, param);
266 : : }
267 : 0 : fc->phase = FS_CONTEXT_FAILED;
268 : 0 : return ret;
269 : : }
270 : :
271 : : /**
272 : : * sys_fsconfig - Set parameters and trigger actions on a context
273 : : * @fd: The filesystem context to act upon
274 : : * @cmd: The action to take
275 : : * @_key: Where appropriate, the parameter key to set
276 : : * @_value: Where appropriate, the parameter value to set
277 : : * @aux: Additional information for the value
278 : : *
279 : : * This system call is used to set parameters on a context, including
280 : : * superblock settings, data source and security labelling.
281 : : *
282 : : * Actions include triggering the creation of a superblock and the
283 : : * reconfiguration of the superblock attached to the specified context.
284 : : *
285 : : * When setting a parameter, @cmd indicates the type of value being proposed
286 : : * and @_key indicates the parameter to be altered.
287 : : *
288 : : * @_value and @aux are used to specify the value, should a value be required:
289 : : *
290 : : * (*) fsconfig_set_flag: No value is specified. The parameter must be boolean
291 : : * in nature. The key may be prefixed with "no" to invert the
292 : : * setting. @_value must be NULL and @aux must be 0.
293 : : *
294 : : * (*) fsconfig_set_string: A string value is specified. The parameter can be
295 : : * expecting boolean, integer, string or take a path. A conversion to an
296 : : * appropriate type will be attempted (which may include looking up as a
297 : : * path). @_value points to a NUL-terminated string and @aux must be 0.
298 : : *
299 : : * (*) fsconfig_set_binary: A binary blob is specified. @_value points to the
300 : : * blob and @aux indicates its size. The parameter must be expecting a
301 : : * blob.
302 : : *
303 : : * (*) fsconfig_set_path: A non-empty path is specified. The parameter must be
304 : : * expecting a path object. @_value points to a NUL-terminated string that
305 : : * is the path and @aux is a file descriptor at which to start a relative
306 : : * lookup or AT_FDCWD.
307 : : *
308 : : * (*) fsconfig_set_path_empty: As fsconfig_set_path, but with AT_EMPTY_PATH
309 : : * implied.
310 : : *
311 : : * (*) fsconfig_set_fd: An open file descriptor is specified. @_value must be
312 : : * NULL and @aux indicates the file descriptor.
313 : : */
314 : 0 : SYSCALL_DEFINE5(fsconfig,
315 : : int, fd,
316 : : unsigned int, cmd,
317 : : const char __user *, _key,
318 : : const void __user *, _value,
319 : : int, aux)
320 : : {
321 : : struct fs_context *fc;
322 : : struct fd f;
323 : : int ret;
324 : :
325 : 0 : struct fs_parameter param = {
326 : : .type = fs_value_is_undefined,
327 : : };
328 : :
329 [ # # ]: 0 : if (fd < 0)
330 : : return -EINVAL;
331 : :
332 [ # # # # : 0 : switch (cmd) {
# # # ]
333 : : case FSCONFIG_SET_FLAG:
334 [ # # # # ]: 0 : if (!_key || _value || aux)
335 : : return -EINVAL;
336 : : break;
337 : : case FSCONFIG_SET_STRING:
338 [ # # # # ]: 0 : if (!_key || !_value || aux)
339 : : return -EINVAL;
340 : : break;
341 : : case FSCONFIG_SET_BINARY:
342 [ # # # # ]: 0 : if (!_key || !_value || aux <= 0 || aux > 1024 * 1024)
343 : : return -EINVAL;
344 : : break;
345 : : case FSCONFIG_SET_PATH:
346 : : case FSCONFIG_SET_PATH_EMPTY:
347 [ # # # # ]: 0 : if (!_key || !_value || (aux != AT_FDCWD && aux < 0))
348 : : return -EINVAL;
349 : : break;
350 : : case FSCONFIG_SET_FD:
351 [ # # # # ]: 0 : if (!_key || _value || aux < 0)
352 : : return -EINVAL;
353 : : break;
354 : : case FSCONFIG_CMD_CREATE:
355 : : case FSCONFIG_CMD_RECONFIGURE:
356 [ # # # # ]: 0 : if (_key || _value || aux)
357 : : return -EINVAL;
358 : : break;
359 : : default:
360 : : return -EOPNOTSUPP;
361 : : }
362 : :
363 : 0 : f = fdget(fd);
364 [ # # ]: 0 : if (!f.file)
365 : : return -EBADF;
366 : : ret = -EINVAL;
367 [ # # ]: 0 : if (f.file->f_op != &fscontext_fops)
368 : : goto out_f;
369 : :
370 : 0 : fc = f.file->private_data;
371 [ # # ]: 0 : if (fc->ops == &legacy_fs_context_ops) {
372 [ # # ]: 0 : switch (cmd) {
373 : : case FSCONFIG_SET_BINARY:
374 : : case FSCONFIG_SET_PATH:
375 : : case FSCONFIG_SET_PATH_EMPTY:
376 : : case FSCONFIG_SET_FD:
377 : : ret = -EOPNOTSUPP;
378 : : goto out_f;
379 : : }
380 : : }
381 : :
382 [ # # ]: 0 : if (_key) {
383 : 0 : param.key = strndup_user(_key, 256);
384 [ # # ]: 0 : if (IS_ERR(param.key)) {
385 : : ret = PTR_ERR(param.key);
386 : 0 : goto out_f;
387 : : }
388 : : }
389 : :
390 [ # # # # : 0 : switch (cmd) {
# # # ]
391 : : case FSCONFIG_SET_FLAG:
392 : 0 : param.type = fs_value_is_flag;
393 : 0 : break;
394 : : case FSCONFIG_SET_STRING:
395 : 0 : param.type = fs_value_is_string;
396 : 0 : param.string = strndup_user(_value, 256);
397 [ # # ]: 0 : if (IS_ERR(param.string)) {
398 : : ret = PTR_ERR(param.string);
399 : 0 : goto out_key;
400 : : }
401 : 0 : param.size = strlen(param.string);
402 : 0 : break;
403 : : case FSCONFIG_SET_BINARY:
404 : 0 : param.type = fs_value_is_blob;
405 : 0 : param.size = aux;
406 : 0 : param.blob = memdup_user_nul(_value, aux);
407 [ # # ]: 0 : if (IS_ERR(param.blob)) {
408 : : ret = PTR_ERR(param.blob);
409 : 0 : goto out_key;
410 : : }
411 : : break;
412 : : case FSCONFIG_SET_PATH:
413 : 0 : param.type = fs_value_is_filename;
414 : 0 : param.name = getname_flags(_value, 0, NULL);
415 [ # # ]: 0 : if (IS_ERR(param.name)) {
416 : : ret = PTR_ERR(param.name);
417 : 0 : goto out_key;
418 : : }
419 : 0 : param.dirfd = aux;
420 : 0 : param.size = strlen(param.name->name);
421 : 0 : break;
422 : : case FSCONFIG_SET_PATH_EMPTY:
423 : 0 : param.type = fs_value_is_filename_empty;
424 : 0 : param.name = getname_flags(_value, LOOKUP_EMPTY, NULL);
425 [ # # ]: 0 : if (IS_ERR(param.name)) {
426 : : ret = PTR_ERR(param.name);
427 : 0 : goto out_key;
428 : : }
429 : 0 : param.dirfd = aux;
430 : 0 : param.size = strlen(param.name->name);
431 : 0 : break;
432 : : case FSCONFIG_SET_FD:
433 : 0 : param.type = fs_value_is_file;
434 : : ret = -EBADF;
435 : 0 : param.file = fget(aux);
436 [ # # ]: 0 : if (!param.file)
437 : : goto out_key;
438 : : break;
439 : : default:
440 : : break;
441 : : }
442 : :
443 : 0 : ret = mutex_lock_interruptible(&fc->uapi_mutex);
444 [ # # ]: 0 : if (ret == 0) {
445 : 0 : ret = vfs_fsconfig_locked(fc, cmd, ¶m);
446 : 0 : mutex_unlock(&fc->uapi_mutex);
447 : : }
448 : :
449 : : /* Clean up the our record of any value that we obtained from
450 : : * userspace. Note that the value may have been stolen by the LSM or
451 : : * filesystem, in which case the value pointer will have been cleared.
452 : : */
453 [ # # # # ]: 0 : switch (cmd) {
454 : : case FSCONFIG_SET_STRING:
455 : : case FSCONFIG_SET_BINARY:
456 : 0 : kfree(param.string);
457 : 0 : break;
458 : : case FSCONFIG_SET_PATH:
459 : : case FSCONFIG_SET_PATH_EMPTY:
460 [ # # ]: 0 : if (param.name)
461 : 0 : putname(param.name);
462 : : break;
463 : : case FSCONFIG_SET_FD:
464 [ # # ]: 0 : if (param.file)
465 : 0 : fput(param.file);
466 : : break;
467 : : default:
468 : : break;
469 : : }
470 : : out_key:
471 : 0 : kfree(param.key);
472 : : out_f:
473 : : fdput(f);
474 : 0 : return ret;
475 : : }
|