LCOV - code coverage report
Current view: top level - security/landlock - syscalls.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 137 144 95.1 %
Date: 2021-04-22 12:43:58 Functions: 11 14 78.6 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-only
       2             : /*
       3             :  * Landlock LSM - System call implementations and user space interfaces
       4             :  *
       5             :  * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net>
       6             :  * Copyright © 2018-2020 ANSSI
       7             :  */
       8             : 
       9             : #include <asm/current.h>
      10             : #include <linux/anon_inodes.h>
      11             : #include <linux/build_bug.h>
      12             : #include <linux/capability.h>
      13             : #include <linux/compiler_types.h>
      14             : #include <linux/dcache.h>
      15             : #include <linux/err.h>
      16             : #include <linux/errno.h>
      17             : #include <linux/fs.h>
      18             : #include <linux/limits.h>
      19             : #include <linux/mount.h>
      20             : #include <linux/path.h>
      21             : #include <linux/sched.h>
      22             : #include <linux/security.h>
      23             : #include <linux/stddef.h>
      24             : #include <linux/syscalls.h>
      25             : #include <linux/types.h>
      26             : #include <linux/uaccess.h>
      27             : #include <uapi/linux/landlock.h>
      28             : 
      29             : #include "cred.h"
      30             : #include "fs.h"
      31             : #include "limits.h"
      32             : #include "ruleset.h"
      33             : #include "setup.h"
      34             : 
      35             : /**
      36             :  * copy_min_struct_from_user - Safe future-proof argument copying
      37             :  *
      38             :  * Extend copy_struct_from_user() to check for consistent user buffer.
      39             :  *
      40             :  * @dst: Kernel space pointer or NULL.
      41             :  * @ksize: Actual size of the data pointed to by @dst.
      42             :  * @ksize_min: Minimal required size to be copied.
      43             :  * @src: User space pointer or NULL.
      44             :  * @usize: (Alleged) size of the data pointed to by @src.
      45             :  */
      46          88 : static __always_inline int copy_min_struct_from_user(void *const dst,
      47             :                 const size_t ksize, const size_t ksize_min,
      48             :                 const void __user *const src, const size_t usize)
      49             : {
      50             :         /* Checks buffer inconsistencies. */
      51          88 :         BUILD_BUG_ON(!dst);
      52          88 :         if (!src)
      53             :                 return -EFAULT;
      54             : 
      55             :         /* Checks size ranges. */
      56          86 :         BUILD_BUG_ON(ksize <= 0);
      57          86 :         BUILD_BUG_ON(ksize < ksize_min);
      58          86 :         if (usize < ksize_min)
      59             :                 return -EINVAL;
      60          84 :         if (usize > PAGE_SIZE)
      61             :                 return -E2BIG;
      62             : 
      63             :         /* Copies user buffer and fills with zeros. */
      64          82 :         return copy_struct_from_user(dst, ksize, src, usize);
      65             : }
      66             : 
      67             : /*
      68             :  * This function only contains arithmetic operations with constants, leading to
      69             :  * BUILD_BUG_ON().  The related code is evaluated and checked at build time,
      70             :  * but it is then ignored thanks to compiler optimizations.
      71             :  */
      72          97 : static void build_check_abi(void)
      73             : {
      74          97 :         struct landlock_ruleset_attr ruleset_attr;
      75          97 :         struct landlock_path_beneath_attr path_beneath_attr;
      76          97 :         size_t ruleset_size, path_beneath_size;
      77             : 
      78             :         /*
      79             :          * For each user space ABI structures, first checks that there is no
      80             :          * hole in them, then checks that all architectures have the same
      81             :          * struct size.
      82             :          */
      83          97 :         ruleset_size = sizeof(ruleset_attr.handled_access_fs);
      84          97 :         BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
      85          97 :         BUILD_BUG_ON(sizeof(ruleset_attr) != 8);
      86             : 
      87          97 :         path_beneath_size = sizeof(path_beneath_attr.allowed_access);
      88          97 :         path_beneath_size += sizeof(path_beneath_attr.parent_fd);
      89          97 :         BUILD_BUG_ON(sizeof(path_beneath_attr) != path_beneath_size);
      90          97 :         BUILD_BUG_ON(sizeof(path_beneath_attr) != 12);
      91             : }
      92             : 
      93             : /* Ruleset handling */
      94             : 
      95          78 : static int fop_ruleset_release(struct inode *const inode,
      96             :                 struct file *const filp)
      97             : {
      98          78 :         struct landlock_ruleset *ruleset = filp->private_data;
      99             : 
     100          78 :         landlock_put_ruleset(ruleset);
     101          78 :         return 0;
     102             : }
     103             : 
     104           1 : static ssize_t fop_dummy_read(struct file *const filp, char __user *const buf,
     105             :                 const size_t size, loff_t *const ppos)
     106             : {
     107             :         /* Dummy handler to enable FMODE_CAN_READ. */
     108           1 :         return -EINVAL;
     109             : }
     110             : 
     111           1 : static ssize_t fop_dummy_write(struct file *const filp,
     112             :                 const char __user *const buf, const size_t size,
     113             :                 loff_t *const ppos)
     114             : {
     115             :         /* Dummy handler to enable FMODE_CAN_WRITE. */
     116           1 :         return -EINVAL;
     117             : }
     118             : 
     119             : /*
     120             :  * A ruleset file descriptor enables to build a ruleset by adding (i.e.
     121             :  * writing) rule after rule, without relying on the task's context.  This
     122             :  * reentrant design is also used in a read way to enforce the ruleset on the
     123             :  * current task.
     124             :  */
     125             : static const struct file_operations ruleset_fops = {
     126             :         .release = fop_ruleset_release,
     127             :         .read = fop_dummy_read,
     128             :         .write = fop_dummy_write,
     129             : };
     130             : 
     131             : #define LANDLOCK_ABI_VERSION    1
     132             : 
     133             : /**
     134             :  * sys_landlock_create_ruleset - Create a new ruleset
     135             :  *
     136             :  * @attr: Pointer to a &struct landlock_ruleset_attr identifying the scope of
     137             :  *        the new ruleset.
     138             :  * @size: Size of the pointed &struct landlock_ruleset_attr (needed for
     139             :  *        backward and forward compatibility).
     140             :  * @flags: Supported value: %LANDLOCK_CREATE_RULESET_VERSION.
     141             :  *
     142             :  * This system call enables to create a new Landlock ruleset, and returns the
     143             :  * related file descriptor on success.
     144             :  *
     145             :  * If @flags is %LANDLOCK_CREATE_RULESET_VERSION and @attr is NULL and @size is
     146             :  * 0, then the returned value is the highest supported Landlock ABI version
     147             :  * (starting at 1).
     148             :  *
     149             :  * Possible returned errors are:
     150             :  *
     151             :  * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
     152             :  * - EINVAL: unknown @flags, or unknown access, or too small @size;
     153             :  * - E2BIG or EFAULT: @attr or @size inconsistencies;
     154             :  * - ENOMSG: empty &landlock_ruleset_attr.handled_access_fs.
     155             :  */
     156         194 : SYSCALL_DEFINE3(landlock_create_ruleset,
     157             :                 const struct landlock_ruleset_attr __user *const, attr,
     158             :                 const size_t, size, const __u32, flags)
     159             : {
     160          97 :         struct landlock_ruleset_attr ruleset_attr;
     161          97 :         struct landlock_ruleset *ruleset;
     162          97 :         int err, ruleset_fd;
     163             : 
     164             :         /* Build-time checks. */
     165          97 :         build_check_abi();
     166             : 
     167          97 :         if (!landlock_initialized)
     168             :                 return -EOPNOTSUPP;
     169             : 
     170          97 :         if (flags) {
     171           9 :                 if ((flags == LANDLOCK_CREATE_RULESET_VERSION)
     172           9 :                                 && !attr && !size)
     173             :                         return LANDLOCK_ABI_VERSION;
     174           8 :                 return -EINVAL;
     175             :         }
     176             : 
     177             :         /* Copies raw user space buffer. */
     178          88 :         err = copy_min_struct_from_user(&ruleset_attr, sizeof(ruleset_attr),
     179             :                         offsetofend(typeof(ruleset_attr), handled_access_fs),
     180             :                         attr, size);
     181          81 :         if (err)
     182           7 :                 return err;
     183             : 
     184             :         /* Checks content (and 32-bits cast). */
     185          81 :         if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
     186             :                         LANDLOCK_MASK_ACCESS_FS)
     187             :                 return -EINVAL;
     188             : 
     189             :         /* Checks arguments and transforms to kernel struct. */
     190          81 :         ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
     191          81 :         if (IS_ERR(ruleset))
     192           3 :                 return PTR_ERR(ruleset);
     193             : 
     194             :         /* Creates anonymous FD referring to the ruleset. */
     195          78 :         ruleset_fd = anon_inode_getfd("landlock-ruleset", &ruleset_fops,
     196             :                         ruleset, O_RDWR | O_CLOEXEC);
     197          78 :         if (ruleset_fd < 0)
     198           0 :                 landlock_put_ruleset(ruleset);
     199          78 :         return ruleset_fd;
     200             : }
     201             : 
     202             : /*
     203             :  * Returns an owned ruleset from a FD. It is thus needed to call
     204             :  * landlock_put_ruleset() on the return value.
     205             :  */
     206         267 : static struct landlock_ruleset *get_ruleset_from_fd(const int fd,
     207             :                 const fmode_t mode)
     208             : {
     209         267 :         struct fd ruleset_f;
     210         267 :         struct landlock_ruleset *ruleset;
     211             : 
     212         267 :         ruleset_f = fdget(fd);
     213         267 :         if (!ruleset_f.file)
     214         267 :                 return ERR_PTR(-EBADF);
     215             : 
     216             :         /* Checks FD type and access right. */
     217         265 :         if (ruleset_f.file->f_op != &ruleset_fops) {
     218           1 :                 ruleset = ERR_PTR(-EBADFD);
     219           1 :                 goto out_fdput;
     220             :         }
     221         264 :         if (!(ruleset_f.file->f_mode & mode)) {
     222           0 :                 ruleset = ERR_PTR(-EPERM);
     223           0 :                 goto out_fdput;
     224             :         }
     225         264 :         ruleset = ruleset_f.file->private_data;
     226         264 :         if (WARN_ON_ONCE(ruleset->num_layers != 1)) {
     227           0 :                 ruleset = ERR_PTR(-EINVAL);
     228           0 :                 goto out_fdput;
     229             :         }
     230         264 :         landlock_get_ruleset(ruleset);
     231             : 
     232         265 : out_fdput:
     233         265 :         fdput(ruleset_f);
     234         265 :         return ruleset;
     235             : }
     236             : 
     237             : /* Path handling */
     238             : 
     239             : /*
     240             :  * @path: Must call put_path(@path) after the call if it succeeded.
     241             :  */
     242         116 : static int get_path_from_fd(const s32 fd, struct path *const path)
     243             : {
     244         116 :         struct fd f;
     245         116 :         int err = 0;
     246             : 
     247         116 :         BUILD_BUG_ON(!__same_type(fd,
     248             :                 ((struct landlock_path_beneath_attr *)NULL)->parent_fd));
     249             : 
     250             :         /* Handles O_PATH. */
     251         116 :         f = fdget_raw(fd);
     252         116 :         if (!f.file)
     253             :                 return -EBADF;
     254             :         /*
     255             :          * Forbids ruleset FDs, internal filesystems (e.g. nsfs), including
     256             :          * pseudo filesystems that will never be mountable (e.g. sockfs,
     257             :          * pipefs).
     258             :          */
     259         116 :         if ((f.file->f_op == &ruleset_fops) ||
     260         115 :                         (f.file->f_path.mnt->mnt_flags & MNT_INTERNAL) ||
     261         114 :                         (f.file->f_path.dentry->d_sb->s_flags & SB_NOUSER) ||
     262         114 :                         d_is_negative(f.file->f_path.dentry) ||
     263         114 :                         IS_PRIVATE(d_backing_inode(f.file->f_path.dentry))) {
     264           2 :                 err = -EBADFD;
     265           2 :                 goto out_fdput;
     266             :         }
     267         114 :         *path = f.file->f_path;
     268         114 :         path_get(path);
     269             : 
     270         116 : out_fdput:
     271         116 :         fdput(f);
     272         116 :         return err;
     273             : }
     274             : 
     275             : /**
     276             :  * sys_landlock_add_rule - Add a new rule to a ruleset
     277             :  *
     278             :  * @ruleset_fd: File descriptor tied to the ruleset that should be extended
     279             :  *              with the new rule.
     280             :  * @rule_type: Identify the structure type pointed to by @rule_attr (only
     281             :  *             LANDLOCK_RULE_PATH_BENEATH for now).
     282             :  * @rule_attr: Pointer to a rule (only of type &struct
     283             :  *             landlock_path_beneath_attr for now).
     284             :  * @flags: Must be 0.
     285             :  *
     286             :  * This system call enables to define a new rule and add it to an existing
     287             :  * ruleset.
     288             :  *
     289             :  * Possible returned errors are:
     290             :  *
     291             :  * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
     292             :  * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
     293             :  *   &landlock_path_beneath_attr.allowed_access is not a subset of the rule's
     294             :  *   accesses);
     295             :  * - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access);
     296             :  * - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
     297             :  *   member of @rule_attr is not a file descriptor as expected;
     298             :  * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
     299             :  *   @rule_attr is not the expected file descriptor type (e.g. file open
     300             :  *   without O_PATH);
     301             :  * - EPERM: @ruleset_fd has no write access to the underlying ruleset;
     302             :  * - EFAULT: @rule_attr inconsistency.
     303             :  */
     304         244 : SYSCALL_DEFINE4(landlock_add_rule,
     305             :                 const int, ruleset_fd, const enum landlock_rule_type, rule_type,
     306             :                 const void __user *const, rule_attr, const __u32, flags)
     307             : {
     308         122 :         struct landlock_path_beneath_attr path_beneath_attr;
     309         122 :         struct path path;
     310         122 :         struct landlock_ruleset *ruleset;
     311         122 :         int res, err;
     312             : 
     313         122 :         if (!landlock_initialized)
     314             :                 return -EOPNOTSUPP;
     315             : 
     316             :         /* No flag for now. */
     317         122 :         if (flags)
     318             :                 return -EINVAL;
     319             : 
     320         122 :         if (rule_type != LANDLOCK_RULE_PATH_BENEATH)
     321             :                 return -EINVAL;
     322             : 
     323             :         /* Copies raw user space buffer, only one type for now. */
     324         122 :         res = copy_from_user(&path_beneath_attr, rule_attr,
     325             :                         sizeof(path_beneath_attr));
     326         122 :         if (res)
     327             :                 return -EFAULT;
     328             : 
     329             :         /* Gets and checks the ruleset. */
     330         121 :         ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
     331         121 :         if (IS_ERR(ruleset))
     332           2 :                 return PTR_ERR(ruleset);
     333             : 
     334             :         /*
     335             :          * Informs about useless rule: empty allowed_access (i.e. deny rules)
     336             :          * are ignored in path walks.
     337             :          */
     338         119 :         if (!path_beneath_attr.allowed_access) {
     339           1 :                 err = -ENOMSG;
     340           1 :                 goto out_put_ruleset;
     341             :         }
     342             :         /*
     343             :          * Checks that allowed_access matches the @ruleset constraints
     344             :          * (ruleset->fs_access_masks[0] is automatically upgraded to 64-bits).
     345             :          */
     346         118 :         if ((path_beneath_attr.allowed_access | ruleset->fs_access_masks[0]) !=
     347             :                         ruleset->fs_access_masks[0]) {
     348           2 :                 err = -EINVAL;
     349           2 :                 goto out_put_ruleset;
     350             :         }
     351             : 
     352             :         /* Gets and checks the new rule. */
     353         116 :         err = get_path_from_fd(path_beneath_attr.parent_fd, &path);
     354         116 :         if (err)
     355           2 :                 goto out_put_ruleset;
     356             : 
     357             :         /* Imports the new rule. */
     358         228 :         err = landlock_append_fs_rule(ruleset, &path,
     359         114 :                         path_beneath_attr.allowed_access);
     360         114 :         path_put(&path);
     361             : 
     362         119 : out_put_ruleset:
     363         119 :         landlock_put_ruleset(ruleset);
     364         119 :         return err;
     365             : }
     366             : 
     367             : /* Enforcement */
     368             : 
     369             : /**
     370             :  * sys_landlock_restrict_self - Enforce a ruleset on the calling thread
     371             :  *
     372             :  * @ruleset_fd: File descriptor tied to the ruleset to merge with the target.
     373             :  * @flags: Must be 0.
     374             :  *
     375             :  * This system call enables to enforce a Landlock ruleset on the current
     376             :  * thread.  Enforcing a ruleset requires that the task has CAP_SYS_ADMIN in its
     377             :  * namespace or is running with no_new_privs.  This avoids scenarios where
     378             :  * unprivileged tasks can affect the behavior of privileged children.
     379             :  *
     380             :  * Possible returned errors are:
     381             :  *
     382             :  * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
     383             :  * - EINVAL: @flags is not 0.
     384             :  * - EBADF: @ruleset_fd is not a file descriptor for the current thread;
     385             :  * - EBADFD: @ruleset_fd is not a ruleset file descriptor;
     386             :  * - EPERM: @ruleset_fd has no read access to the underlying ruleset, or the
     387             :  *   current thread is not running with no_new_privs, or it doesn't have
     388             :  *   CAP_SYS_ADMIN in its namespace.
     389             :  * - E2BIG: The maximum number of stacked rulesets is reached for the current
     390             :  *   thread.
     391             :  */
     392         296 : SYSCALL_DEFINE2(landlock_restrict_self,
     393             :                 const int, ruleset_fd, const __u32, flags)
     394             : {
     395         148 :         struct landlock_ruleset *new_dom, *ruleset;
     396         148 :         struct cred *new_cred;
     397         148 :         struct landlock_cred_security *new_llcred;
     398         148 :         int err;
     399             : 
     400         148 :         if (!landlock_initialized)
     401             :                 return -EOPNOTSUPP;
     402             : 
     403             :         /* No flag for now. */
     404         148 :         if (flags)
     405             :                 return -EINVAL;
     406             : 
     407             :         /*
     408             :          * Similar checks as for seccomp(2), except that an -EPERM may be
     409             :          * returned.
     410             :          */
     411         150 :         if (!task_no_new_privs(current) &&
     412           2 :                         !ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN))
     413             :                 return -EPERM;
     414             : 
     415             :         /* Gets and checks the ruleset. */
     416         146 :         ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ);
     417         146 :         if (IS_ERR(ruleset))
     418           1 :                 return PTR_ERR(ruleset);
     419             : 
     420             :         /* Prepares new credentials. */
     421         145 :         new_cred = prepare_creds();
     422         145 :         if (!new_cred) {
     423           0 :                 err = -ENOMEM;
     424           0 :                 goto out_put_ruleset;
     425             :         }
     426         145 :         new_llcred = landlock_cred(new_cred);
     427             : 
     428             :         /*
     429             :          * There is no possible race condition while copying and manipulating
     430             :          * the current credentials because they are dedicated per thread.
     431             :          */
     432         145 :         new_dom = landlock_merge_ruleset(new_llcred->domain, ruleset);
     433         145 :         if (IS_ERR(new_dom)) {
     434           2 :                 err = PTR_ERR(new_dom);
     435           2 :                 goto out_put_creds;
     436             :         }
     437             : 
     438             :         /* Replaces the old (prepared) domain. */
     439         143 :         landlock_put_ruleset(new_llcred->domain);
     440         143 :         new_llcred->domain = new_dom;
     441             : 
     442         143 :         landlock_put_ruleset(ruleset);
     443         143 :         return commit_creds(new_cred);
     444             : 
     445           2 : out_put_creds:
     446           2 :         abort_creds(new_cred);
     447             : 
     448           2 : out_put_ruleset:
     449           2 :         landlock_put_ruleset(ruleset);
     450           2 :         return err;
     451             : }

Generated by: LCOV version 1.14