syscalls.c 12.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Landlock LSM - System call implementations and user space interfaces
 *
 * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net>
 * Copyright © 2018-2020 ANSSI
 */

#include <asm/current.h>
#include <linux/anon_inodes.h>
#include <linux/build_bug.h>
#include <linux/capability.h>
#include <linux/compiler_types.h>
#include <linux/dcache.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/limits.h>
#include <linux/mount.h>
#include <linux/path.h>
#include <linux/sched.h>
#include <linux/security.h>
#include <linux/stddef.h>
#include <linux/syscalls.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <uapi/linux/landlock.h>

#include "cred.h"
#include "fs.h"
#include "limits.h"
#include "ruleset.h"
#include "setup.h"

/**
 * copy_min_struct_from_user - Safe future-proof argument copying
 *
 * Extend copy_struct_from_user() to check for consistent user buffer.
 *
 * @dst: Kernel space pointer or NULL.
 * @ksize: Actual size of the data pointed to by @dst.
 * @ksize_min: Minimal required size to be copied.
 * @src: User space pointer or NULL.
 * @usize: (Alleged) size of the data pointed to by @src.
 */
46 47 48 49
static __always_inline int
copy_min_struct_from_user(void *const dst, const size_t ksize,
			  const size_t ksize_min, const void __user *const src,
			  const size_t usize)
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
{
	/* Checks buffer inconsistencies. */
	BUILD_BUG_ON(!dst);
	if (!src)
		return -EFAULT;

	/* Checks size ranges. */
	BUILD_BUG_ON(ksize <= 0);
	BUILD_BUG_ON(ksize < ksize_min);
	if (usize < ksize_min)
		return -EINVAL;
	if (usize > PAGE_SIZE)
		return -E2BIG;

	/* Copies user buffer and fills with zeros. */
	return copy_struct_from_user(dst, ksize, src, usize);
}

/*
 * This function only contains arithmetic operations with constants, leading to
 * BUILD_BUG_ON().  The related code is evaluated and checked at build time,
 * but it is then ignored thanks to compiler optimizations.
 */
static void build_check_abi(void)
{
	struct landlock_ruleset_attr ruleset_attr;
	struct landlock_path_beneath_attr path_beneath_attr;
	size_t ruleset_size, path_beneath_size;

	/*
	 * For each user space ABI structures, first checks that there is no
	 * hole in them, then checks that all architectures have the same
	 * struct size.
	 */
	ruleset_size = sizeof(ruleset_attr.handled_access_fs);
	BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
	BUILD_BUG_ON(sizeof(ruleset_attr) != 8);

	path_beneath_size = sizeof(path_beneath_attr.allowed_access);
	path_beneath_size += sizeof(path_beneath_attr.parent_fd);
	BUILD_BUG_ON(sizeof(path_beneath_attr) != path_beneath_size);
	BUILD_BUG_ON(sizeof(path_beneath_attr) != 12);
}

/* Ruleset handling */

static int fop_ruleset_release(struct inode *const inode,
97
			       struct file *const filp)
98 99 100 101 102 103 104 105
{
	struct landlock_ruleset *ruleset = filp->private_data;

	landlock_put_ruleset(ruleset);
	return 0;
}

static ssize_t fop_dummy_read(struct file *const filp, char __user *const buf,
106
			      const size_t size, loff_t *const ppos)
107 108 109 110 111 112
{
	/* Dummy handler to enable FMODE_CAN_READ. */
	return -EINVAL;
}

static ssize_t fop_dummy_write(struct file *const filp,
113 114
			       const char __user *const buf, const size_t size,
			       loff_t *const ppos)
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
{
	/* Dummy handler to enable FMODE_CAN_WRITE. */
	return -EINVAL;
}

/*
 * A ruleset file descriptor enables to build a ruleset by adding (i.e.
 * writing) rule after rule, without relying on the task's context.  This
 * reentrant design is also used in a read way to enforce the ruleset on the
 * current task.
 */
static const struct file_operations ruleset_fops = {
	.release = fop_ruleset_release,
	.read = fop_dummy_read,
	.write = fop_dummy_write,
};

132
#define LANDLOCK_ABI_VERSION 2
133

134 135 136 137 138 139 140
/**
 * sys_landlock_create_ruleset - Create a new ruleset
 *
 * @attr: Pointer to a &struct landlock_ruleset_attr identifying the scope of
 *        the new ruleset.
 * @size: Size of the pointed &struct landlock_ruleset_attr (needed for
 *        backward and forward compatibility).
141
 * @flags: Supported value: %LANDLOCK_CREATE_RULESET_VERSION.
142 143 144 145
 *
 * This system call enables to create a new Landlock ruleset, and returns the
 * related file descriptor on success.
 *
146 147 148 149
 * If @flags is %LANDLOCK_CREATE_RULESET_VERSION and @attr is NULL and @size is
 * 0, then the returned value is the highest supported Landlock ABI version
 * (starting at 1).
 *
150 151 152
 * Possible returned errors are:
 *
 * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
153
 * - EINVAL: unknown @flags, or unknown access, or too small @size;
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
 * - E2BIG or EFAULT: @attr or @size inconsistencies;
 * - ENOMSG: empty &landlock_ruleset_attr.handled_access_fs.
 */
SYSCALL_DEFINE3(landlock_create_ruleset,
		const struct landlock_ruleset_attr __user *const, attr,
		const size_t, size, const __u32, flags)
{
	struct landlock_ruleset_attr ruleset_attr;
	struct landlock_ruleset *ruleset;
	int err, ruleset_fd;

	/* Build-time checks. */
	build_check_abi();

	if (!landlock_initialized)
		return -EOPNOTSUPP;

171
	if (flags) {
172 173
		if ((flags == LANDLOCK_CREATE_RULESET_VERSION) && !attr &&
		    !size)
174
			return LANDLOCK_ABI_VERSION;
175
		return -EINVAL;
176
	}
177 178 179

	/* Copies raw user space buffer. */
	err = copy_min_struct_from_user(&ruleset_attr, sizeof(ruleset_attr),
180 181 182
					offsetofend(typeof(ruleset_attr),
						    handled_access_fs),
					attr, size);
183 184 185 186 187
	if (err)
		return err;

	/* Checks content (and 32-bits cast). */
	if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
188
	    LANDLOCK_MASK_ACCESS_FS)
189 190 191 192 193 194 195 196
		return -EINVAL;

	/* Checks arguments and transforms to kernel struct. */
	ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
	if (IS_ERR(ruleset))
		return PTR_ERR(ruleset);

	/* Creates anonymous FD referring to the ruleset. */
197
	ruleset_fd = anon_inode_getfd("[landlock-ruleset]", &ruleset_fops,
198
				      ruleset, O_RDWR | O_CLOEXEC);
199 200 201 202 203 204 205 206 207 208
	if (ruleset_fd < 0)
		landlock_put_ruleset(ruleset);
	return ruleset_fd;
}

/*
 * Returns an owned ruleset from a FD. It is thus needed to call
 * landlock_put_ruleset() on the return value.
 */
static struct landlock_ruleset *get_ruleset_from_fd(const int fd,
209
						    const fmode_t mode)
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
{
	struct fd ruleset_f;
	struct landlock_ruleset *ruleset;

	ruleset_f = fdget(fd);
	if (!ruleset_f.file)
		return ERR_PTR(-EBADF);

	/* Checks FD type and access right. */
	if (ruleset_f.file->f_op != &ruleset_fops) {
		ruleset = ERR_PTR(-EBADFD);
		goto out_fdput;
	}
	if (!(ruleset_f.file->f_mode & mode)) {
		ruleset = ERR_PTR(-EPERM);
		goto out_fdput;
	}
	ruleset = ruleset_f.file->private_data;
	if (WARN_ON_ONCE(ruleset->num_layers != 1)) {
		ruleset = ERR_PTR(-EINVAL);
		goto out_fdput;
	}
	landlock_get_ruleset(ruleset);

out_fdput:
	fdput(ruleset_f);
	return ruleset;
}

/* Path handling */

/*
 * @path: Must call put_path(@path) after the call if it succeeded.
 */
static int get_path_from_fd(const s32 fd, struct path *const path)
{
	struct fd f;
	int err = 0;

249 250
	BUILD_BUG_ON(!__same_type(
		fd, ((struct landlock_path_beneath_attr *)NULL)->parent_fd));
251 252 253 254 255 256 257 258 259 260 261

	/* Handles O_PATH. */
	f = fdget_raw(fd);
	if (!f.file)
		return -EBADF;
	/*
	 * Forbids ruleset FDs, internal filesystems (e.g. nsfs), including
	 * pseudo filesystems that will never be mountable (e.g. sockfs,
	 * pipefs).
	 */
	if ((f.file->f_op == &ruleset_fops) ||
262 263 264 265
	    (f.file->f_path.mnt->mnt_flags & MNT_INTERNAL) ||
	    (f.file->f_path.dentry->d_sb->s_flags & SB_NOUSER) ||
	    d_is_negative(f.file->f_path.dentry) ||
	    IS_PRIVATE(d_backing_inode(f.file->f_path.dentry))) {
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
		err = -EBADFD;
		goto out_fdput;
	}
	*path = f.file->f_path;
	path_get(path);

out_fdput:
	fdput(f);
	return err;
}

/**
 * sys_landlock_add_rule - Add a new rule to a ruleset
 *
 * @ruleset_fd: File descriptor tied to the ruleset that should be extended
 *		with the new rule.
 * @rule_type: Identify the structure type pointed to by @rule_attr (only
 *             LANDLOCK_RULE_PATH_BENEATH for now).
 * @rule_attr: Pointer to a rule (only of type &struct
 *             landlock_path_beneath_attr for now).
 * @flags: Must be 0.
 *
 * This system call enables to define a new rule and add it to an existing
 * ruleset.
 *
 * Possible returned errors are:
 *
 * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
 * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
295 296
 *   &landlock_path_beneath_attr.allowed_access is not a subset of the
 *   ruleset handled accesses);
297 298 299 300
 * - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access);
 * - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
 *   member of @rule_attr is not a file descriptor as expected;
 * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
301
 *   @rule_attr is not the expected file descriptor type;
302 303 304
 * - EPERM: @ruleset_fd has no write access to the underlying ruleset;
 * - EFAULT: @rule_attr inconsistency.
 */
305 306
SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
		const enum landlock_rule_type, rule_type,
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
		const void __user *const, rule_attr, const __u32, flags)
{
	struct landlock_path_beneath_attr path_beneath_attr;
	struct path path;
	struct landlock_ruleset *ruleset;
	int res, err;

	if (!landlock_initialized)
		return -EOPNOTSUPP;

	/* No flag for now. */
	if (flags)
		return -EINVAL;

	/* Gets and checks the ruleset. */
	ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
	if (IS_ERR(ruleset))
		return PTR_ERR(ruleset);

326 327 328 329 330 331 332 333 334 335 336 337 338
	if (rule_type != LANDLOCK_RULE_PATH_BENEATH) {
		err = -EINVAL;
		goto out_put_ruleset;
	}

	/* Copies raw user space buffer, only one type for now. */
	res = copy_from_user(&path_beneath_attr, rule_attr,
			     sizeof(path_beneath_attr));
	if (res) {
		err = -EFAULT;
		goto out_put_ruleset;
	}

339 340 341 342 343 344 345 346 347 348 349 350 351
	/*
	 * Informs about useless rule: empty allowed_access (i.e. deny rules)
	 * are ignored in path walks.
	 */
	if (!path_beneath_attr.allowed_access) {
		err = -ENOMSG;
		goto out_put_ruleset;
	}
	/*
	 * Checks that allowed_access matches the @ruleset constraints
	 * (ruleset->fs_access_masks[0] is automatically upgraded to 64-bits).
	 */
	if ((path_beneath_attr.allowed_access | ruleset->fs_access_masks[0]) !=
352
	    ruleset->fs_access_masks[0]) {
353 354 355 356 357 358 359 360 361 362 363
		err = -EINVAL;
		goto out_put_ruleset;
	}

	/* Gets and checks the new rule. */
	err = get_path_from_fd(path_beneath_attr.parent_fd, &path);
	if (err)
		goto out_put_ruleset;

	/* Imports the new rule. */
	err = landlock_append_fs_rule(ruleset, &path,
364
				      path_beneath_attr.allowed_access);
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
	path_put(&path);

out_put_ruleset:
	landlock_put_ruleset(ruleset);
	return err;
}

/* Enforcement */

/**
 * sys_landlock_restrict_self - Enforce a ruleset on the calling thread
 *
 * @ruleset_fd: File descriptor tied to the ruleset to merge with the target.
 * @flags: Must be 0.
 *
 * This system call enables to enforce a Landlock ruleset on the current
 * thread.  Enforcing a ruleset requires that the task has CAP_SYS_ADMIN in its
 * namespace or is running with no_new_privs.  This avoids scenarios where
 * unprivileged tasks can affect the behavior of privileged children.
 *
 * Possible returned errors are:
 *
 * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
 * - EINVAL: @flags is not 0.
 * - EBADF: @ruleset_fd is not a file descriptor for the current thread;
 * - EBADFD: @ruleset_fd is not a ruleset file descriptor;
 * - EPERM: @ruleset_fd has no read access to the underlying ruleset, or the
 *   current thread is not running with no_new_privs, or it doesn't have
 *   CAP_SYS_ADMIN in its namespace.
 * - E2BIG: The maximum number of stacked rulesets is reached for the current
 *   thread.
 */
397 398
SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
		flags)
399 400 401 402 403 404 405 406 407 408 409 410 411 412
{
	struct landlock_ruleset *new_dom, *ruleset;
	struct cred *new_cred;
	struct landlock_cred_security *new_llcred;
	int err;

	if (!landlock_initialized)
		return -EOPNOTSUPP;

	/*
	 * Similar checks as for seccomp(2), except that an -EPERM may be
	 * returned.
	 */
	if (!task_no_new_privs(current) &&
413
	    !ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN))
414 415
		return -EPERM;

416 417 418 419
	/* No flag for now. */
	if (flags)
		return -EINVAL;

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
	/* Gets and checks the ruleset. */
	ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ);
	if (IS_ERR(ruleset))
		return PTR_ERR(ruleset);

	/* Prepares new credentials. */
	new_cred = prepare_creds();
	if (!new_cred) {
		err = -ENOMEM;
		goto out_put_ruleset;
	}
	new_llcred = landlock_cred(new_cred);

	/*
	 * There is no possible race condition while copying and manipulating
	 * the current credentials because they are dedicated per thread.
	 */
	new_dom = landlock_merge_ruleset(new_llcred->domain, ruleset);
	if (IS_ERR(new_dom)) {
		err = PTR_ERR(new_dom);
		goto out_put_creds;
	}

	/* Replaces the old (prepared) domain. */
	landlock_put_ruleset(new_llcred->domain);
	new_llcred->domain = new_dom;

	landlock_put_ruleset(ruleset);
	return commit_creds(new_cred);

out_put_creds:
	abort_creds(new_cred);

out_put_ruleset:
	landlock_put_ruleset(ruleset);
	return err;
}