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