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.log;
29 : 0 : unsigned int logsize = ARRAY_SIZE(log->buffer);
30 : 0 : ssize_t ret;
31 : 0 : char *p;
32 : 0 : bool need_free;
33 : 0 : 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 : 0 : ret = -EMSGSIZE;
53 : 0 : n = strlen(p);
54 [ # # ]: 0 : if (n > len)
55 : 0 : goto err_free;
56 : 0 : ret = -EFAULT;
57 [ # # # # ]: 0 : if (copy_to_user(_buf, p, n) != 0)
58 : 0 : goto err_free;
59 : 0 : ret = n;
60 : :
61 : 0 : err_free:
62 [ # # ]: 0 : if (need_free)
63 : 0 : kfree(p);
64 : : 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 : 0 : 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 : : static int fscontext_alloc_log(struct fs_context *fc)
99 : : {
100 : : fc->log.log = kzalloc(sizeof(*fc->log.log), GFP_KERNEL);
101 : : if (!fc->log.log)
102 : : return -ENOMEM;
103 : : refcount_set(&fc->log.log->usage, 1);
104 : : fc->log.log->owner = fc->fs_type->owner;
105 : : 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 : 0 : struct file_system_type *fs_type;
118 : 0 : struct fs_context *fc;
119 : 0 : const char *fs_name;
120 : 0 : 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 : 0 : 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 : 0 : struct fs_context *fc;
161 : 0 : struct path target;
162 : 0 : unsigned int lookup_flags;
163 : 0 : 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 : 0 : lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
175 [ # # ]: 0 : if (flags & FSPICK_SYMLINK_NOFOLLOW)
176 : 0 : 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 : 0 : ret = user_path_at(dfd, path, lookup_flags, &target);
182 [ # # ]: 0 : if (ret < 0)
183 : 0 : goto err;
184 : :
185 : 0 : ret = -EINVAL;
186 [ # # ]: 0 : if (target.mnt->mnt_root != target.dentry)
187 : 0 : goto err_path;
188 : :
189 : 0 : fc = fs_context_for_reconfigure(target.dentry, 0, 0);
190 [ # # ]: 0 : if (IS_ERR(fc)) {
191 : 0 : 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 : 0 : 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 : 0 : err_path:
207 : 0 : path_put(&target);
208 : 0 : 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 : 0 : struct super_block *sb;
220 : 0 : int ret;
221 : :
222 : 0 : ret = finish_clean_context(fc);
223 [ # # ]: 0 : if (ret)
224 : : return ret;
225 [ # # # ]: 0 : switch (cmd) {
226 : 0 : 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 : 0 : 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 : 0 : 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 : 0 : struct fs_context *fc;
322 : 0 : struct fd f;
323 : 0 : int ret;
324 : 0 : int lookup_flags = 0;
325 : :
326 : 0 : struct fs_parameter param = {
327 : : .type = fs_value_is_undefined,
328 : : };
329 : :
330 [ # # ]: 0 : if (fd < 0)
331 : : return -EINVAL;
332 : :
333 [ # # # # : 0 : switch (cmd) {
# # # ]
334 : 0 : case FSCONFIG_SET_FLAG:
335 [ # # # # ]: 0 : if (!_key || _value || aux)
336 : : return -EINVAL;
337 : : break;
338 : 0 : case FSCONFIG_SET_STRING:
339 [ # # # # ]: 0 : if (!_key || !_value || aux)
340 : : return -EINVAL;
341 : : break;
342 : 0 : case FSCONFIG_SET_BINARY:
343 [ # # # # ]: 0 : if (!_key || !_value || aux <= 0 || aux > 1024 * 1024)
344 : : return -EINVAL;
345 : : break;
346 : 0 : case FSCONFIG_SET_PATH:
347 : : case FSCONFIG_SET_PATH_EMPTY:
348 [ # # # # ]: 0 : if (!_key || !_value || (aux != AT_FDCWD && aux < 0))
349 : : return -EINVAL;
350 : : break;
351 : 0 : case FSCONFIG_SET_FD:
352 [ # # # # ]: 0 : if (!_key || _value || aux < 0)
353 : : return -EINVAL;
354 : : break;
355 : 0 : case FSCONFIG_CMD_CREATE:
356 : : case FSCONFIG_CMD_RECONFIGURE:
357 [ # # # # ]: 0 : if (_key || _value || aux)
358 : : return -EINVAL;
359 : : break;
360 : : default:
361 : : return -EOPNOTSUPP;
362 : : }
363 : :
364 : 0 : f = fdget(fd);
365 [ # # ]: 0 : if (!f.file)
366 : : return -EBADF;
367 : 0 : ret = -EINVAL;
368 [ # # ]: 0 : if (f.file->f_op != &fscontext_fops)
369 : 0 : goto out_f;
370 : :
371 : 0 : fc = f.file->private_data;
372 [ # # ]: 0 : if (fc->ops == &legacy_fs_context_ops) {
373 [ # # ]: 0 : switch (cmd) {
374 : 0 : case FSCONFIG_SET_BINARY:
375 : : case FSCONFIG_SET_PATH:
376 : : case FSCONFIG_SET_PATH_EMPTY:
377 : : case FSCONFIG_SET_FD:
378 : 0 : ret = -EOPNOTSUPP;
379 : 0 : goto out_f;
380 : : }
381 : : }
382 : :
383 [ # # ]: 0 : if (_key) {
384 : 0 : param.key = strndup_user(_key, 256);
385 [ # # ]: 0 : if (IS_ERR(param.key)) {
386 : 0 : ret = PTR_ERR(param.key);
387 : 0 : goto out_f;
388 : : }
389 : : }
390 : :
391 [ # # # # : 0 : switch (cmd) {
# # # ]
392 : 0 : case FSCONFIG_SET_FLAG:
393 : 0 : param.type = fs_value_is_flag;
394 : 0 : break;
395 : 0 : case FSCONFIG_SET_STRING:
396 : 0 : param.type = fs_value_is_string;
397 : 0 : param.string = strndup_user(_value, 256);
398 [ # # ]: 0 : if (IS_ERR(param.string)) {
399 : 0 : ret = PTR_ERR(param.string);
400 : 0 : goto out_key;
401 : : }
402 : 0 : param.size = strlen(param.string);
403 : 0 : break;
404 : 0 : case FSCONFIG_SET_BINARY:
405 : 0 : param.type = fs_value_is_blob;
406 : 0 : param.size = aux;
407 : 0 : param.blob = memdup_user_nul(_value, aux);
408 [ # # ]: 0 : if (IS_ERR(param.blob)) {
409 : 0 : ret = PTR_ERR(param.blob);
410 : 0 : goto out_key;
411 : : }
412 : : break;
413 : 0 : case FSCONFIG_SET_PATH_EMPTY:
414 : 0 : lookup_flags = LOOKUP_EMPTY;
415 : : /* fallthru */
416 : 0 : case FSCONFIG_SET_PATH:
417 : 0 : param.type = fs_value_is_filename;
418 : 0 : param.name = getname_flags(_value, lookup_flags, NULL);
419 [ # # ]: 0 : if (IS_ERR(param.name)) {
420 : 0 : ret = PTR_ERR(param.name);
421 : 0 : goto out_key;
422 : : }
423 : 0 : param.dirfd = aux;
424 : 0 : param.size = strlen(param.name->name);
425 : 0 : break;
426 : 0 : case FSCONFIG_SET_FD:
427 : 0 : param.type = fs_value_is_file;
428 : 0 : ret = -EBADF;
429 : 0 : param.file = fget(aux);
430 [ # # ]: 0 : if (!param.file)
431 : 0 : goto out_key;
432 : : break;
433 : : default:
434 : : break;
435 : : }
436 : :
437 : 0 : ret = mutex_lock_interruptible(&fc->uapi_mutex);
438 [ # # ]: 0 : if (ret == 0) {
439 : 0 : ret = vfs_fsconfig_locked(fc, cmd, ¶m);
440 : 0 : mutex_unlock(&fc->uapi_mutex);
441 : : }
442 : :
443 : : /* Clean up the our record of any value that we obtained from
444 : : * userspace. Note that the value may have been stolen by the LSM or
445 : : * filesystem, in which case the value pointer will have been cleared.
446 : : */
447 [ # # # # ]: 0 : switch (cmd) {
448 : 0 : case FSCONFIG_SET_STRING:
449 : : case FSCONFIG_SET_BINARY:
450 : 0 : kfree(param.string);
451 : 0 : break;
452 : 0 : case FSCONFIG_SET_PATH:
453 : : case FSCONFIG_SET_PATH_EMPTY:
454 [ # # ]: 0 : if (param.name)
455 : 0 : putname(param.name);
456 : : break;
457 : 0 : case FSCONFIG_SET_FD:
458 [ # # ]: 0 : if (param.file)
459 : 0 : fput(param.file);
460 : : break;
461 : : default:
462 : : break;
463 : : }
464 : 0 : out_key:
465 : 0 : kfree(param.key);
466 : 0 : out_f:
467 [ # # ]: 0 : fdput(f);
468 : 0 : return ret;
469 : : }
|