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 0 : static int fscontext_alloc_log(struct fs_context *fc)
99 : {
100 0 : fc->log.log = kzalloc(sizeof(*fc->log.log), GFP_KERNEL);
101 0 : if (!fc->log.log)
102 : return -ENOMEM;
103 0 : refcount_set(&fc->log.log->usage, 1);
104 0 : fc->log.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 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 0 : 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 0 : 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 0 : fallthrough;
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 : }
|