LCOV - code coverage report
Current view: top level - security/landlock - syscalls.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 134 141 95.0 %
Date: 2021-04-07 12:34:12 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          88 : static void build_check_abi(void)
      73             : {
      74          88 :         struct landlock_ruleset_attr ruleset_attr;
      75          88 :         struct landlock_path_beneath_attr path_beneath_attr;
      76          88 :         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          88 :         ruleset_size = sizeof(ruleset_attr.handled_access_fs);
      84          88 :         BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
      85          88 :         BUILD_BUG_ON(sizeof(ruleset_attr) != 8);
      86             : 
      87          88 :         path_beneath_size = sizeof(path_beneath_attr.allowed_access);
      88          88 :         path_beneath_size += sizeof(path_beneath_attr.parent_fd);
      89          88 :         BUILD_BUG_ON(sizeof(path_beneath_attr) != path_beneath_size);
      90          88 :         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             : /**
     132             :  * sys_landlock_create_ruleset - Create a new ruleset
     133             :  *
     134             :  * @attr: Pointer to a &struct landlock_ruleset_attr identifying the scope of
     135             :  *        the new ruleset.
     136             :  * @size: Size of the pointed &struct landlock_ruleset_attr (needed for
     137             :  *        backward and forward compatibility).
     138             :  * @flags: Must be 0.
     139             :  *
     140             :  * This system call enables to create a new Landlock ruleset, and returns the
     141             :  * related file descriptor on success.
     142             :  *
     143             :  * Possible returned errors are:
     144             :  *
     145             :  * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
     146             :  * - EINVAL: @flags is not 0, or unknown access, or too small @size;
     147             :  * - E2BIG or EFAULT: @attr or @size inconsistencies;
     148             :  * - ENOMSG: empty &landlock_ruleset_attr.handled_access_fs.
     149             :  */
     150         176 : SYSCALL_DEFINE3(landlock_create_ruleset,
     151             :                 const struct landlock_ruleset_attr __user *const, attr,
     152             :                 const size_t, size, const __u32, flags)
     153             : {
     154          88 :         struct landlock_ruleset_attr ruleset_attr;
     155          88 :         struct landlock_ruleset *ruleset;
     156          88 :         int err, ruleset_fd;
     157             : 
     158             :         /* Build-time checks. */
     159          88 :         build_check_abi();
     160             : 
     161          88 :         if (!landlock_initialized)
     162             :                 return -EOPNOTSUPP;
     163             : 
     164             :         /* No flag for now. */
     165          88 :         if (flags)
     166             :                 return -EINVAL;
     167             : 
     168             :         /* Copies raw user space buffer. */
     169          88 :         err = copy_min_struct_from_user(&ruleset_attr, sizeof(ruleset_attr),
     170             :                         offsetofend(typeof(ruleset_attr), handled_access_fs),
     171             :                         attr, size);
     172          81 :         if (err)
     173           7 :                 return err;
     174             : 
     175             :         /* Checks content (and 32-bits cast). */
     176          81 :         if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
     177             :                         LANDLOCK_MASK_ACCESS_FS)
     178             :                 return -EINVAL;
     179             : 
     180             :         /* Checks arguments and transforms to kernel struct. */
     181          81 :         ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
     182          81 :         if (IS_ERR(ruleset))
     183           3 :                 return PTR_ERR(ruleset);
     184             : 
     185             :         /* Creates anonymous FD referring to the ruleset. */
     186          78 :         ruleset_fd = anon_inode_getfd("landlock-ruleset", &ruleset_fops,
     187             :                         ruleset, O_RDWR | O_CLOEXEC);
     188          78 :         if (ruleset_fd < 0)
     189           0 :                 landlock_put_ruleset(ruleset);
     190          78 :         return ruleset_fd;
     191             : }
     192             : 
     193             : /*
     194             :  * Returns an owned ruleset from a FD. It is thus needed to call
     195             :  * landlock_put_ruleset() on the return value.
     196             :  */
     197         267 : static struct landlock_ruleset *get_ruleset_from_fd(const int fd,
     198             :                 const fmode_t mode)
     199             : {
     200         267 :         struct fd ruleset_f;
     201         267 :         struct landlock_ruleset *ruleset;
     202             : 
     203         267 :         ruleset_f = fdget(fd);
     204         267 :         if (!ruleset_f.file)
     205         267 :                 return ERR_PTR(-EBADF);
     206             : 
     207             :         /* Checks FD type and access right. */
     208         265 :         if (ruleset_f.file->f_op != &ruleset_fops) {
     209           1 :                 ruleset = ERR_PTR(-EBADFD);
     210           1 :                 goto out_fdput;
     211             :         }
     212         264 :         if (!(ruleset_f.file->f_mode & mode)) {
     213           0 :                 ruleset = ERR_PTR(-EPERM);
     214           0 :                 goto out_fdput;
     215             :         }
     216         264 :         ruleset = ruleset_f.file->private_data;
     217         264 :         if (WARN_ON_ONCE(ruleset->num_layers != 1)) {
     218           0 :                 ruleset = ERR_PTR(-EINVAL);
     219           0 :                 goto out_fdput;
     220             :         }
     221         264 :         landlock_get_ruleset(ruleset);
     222             : 
     223         265 : out_fdput:
     224         265 :         fdput(ruleset_f);
     225         265 :         return ruleset;
     226             : }
     227             : 
     228             : /* Path handling */
     229             : 
     230             : /*
     231             :  * @path: Must call put_path(@path) after the call if it succeeded.
     232             :  */
     233         116 : static int get_path_from_fd(const s32 fd, struct path *const path)
     234             : {
     235         116 :         struct fd f;
     236         116 :         int err = 0;
     237             : 
     238         116 :         BUILD_BUG_ON(!__same_type(fd,
     239             :                 ((struct landlock_path_beneath_attr *)NULL)->parent_fd));
     240             : 
     241             :         /* Handles O_PATH. */
     242         116 :         f = fdget_raw(fd);
     243         116 :         if (!f.file)
     244             :                 return -EBADF;
     245             :         /*
     246             :          * Forbids ruleset FDs, internal filesystems (e.g. nsfs), including
     247             :          * pseudo filesystems that will never be mountable (e.g. sockfs,
     248             :          * pipefs).
     249             :          */
     250         116 :         if ((f.file->f_op == &ruleset_fops) ||
     251         115 :                         (f.file->f_path.mnt->mnt_flags & MNT_INTERNAL) ||
     252         114 :                         (f.file->f_path.dentry->d_sb->s_flags & SB_NOUSER) ||
     253         114 :                         d_is_negative(f.file->f_path.dentry) ||
     254         114 :                         IS_PRIVATE(d_backing_inode(f.file->f_path.dentry))) {
     255           2 :                 err = -EBADFD;
     256           2 :                 goto out_fdput;
     257             :         }
     258         114 :         *path = f.file->f_path;
     259         114 :         path_get(path);
     260             : 
     261         116 : out_fdput:
     262         116 :         fdput(f);
     263         116 :         return err;
     264             : }
     265             : 
     266             : /**
     267             :  * sys_landlock_add_rule - Add a new rule to a ruleset
     268             :  *
     269             :  * @ruleset_fd: File descriptor tied to the ruleset that should be extended
     270             :  *              with the new rule.
     271             :  * @rule_type: Identify the structure type pointed to by @rule_attr (only
     272             :  *             LANDLOCK_RULE_PATH_BENEATH for now).
     273             :  * @rule_attr: Pointer to a rule (only of type &struct
     274             :  *             landlock_path_beneath_attr for now).
     275             :  * @flags: Must be 0.
     276             :  *
     277             :  * This system call enables to define a new rule and add it to an existing
     278             :  * ruleset.
     279             :  *
     280             :  * Possible returned errors are:
     281             :  *
     282             :  * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
     283             :  * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
     284             :  *   &landlock_path_beneath_attr.allowed_access is not a subset of the rule's
     285             :  *   accesses);
     286             :  * - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access);
     287             :  * - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
     288             :  *   member of @rule_attr is not a file descriptor as expected;
     289             :  * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
     290             :  *   @rule_attr is not the expected file descriptor type (e.g. file open
     291             :  *   without O_PATH);
     292             :  * - EPERM: @ruleset_fd has no write access to the underlying ruleset;
     293             :  * - EFAULT: @rule_attr inconsistency.
     294             :  */
     295         244 : SYSCALL_DEFINE4(landlock_add_rule,
     296             :                 const int, ruleset_fd, const enum landlock_rule_type, rule_type,
     297             :                 const void __user *const, rule_attr, const __u32, flags)
     298             : {
     299         122 :         struct landlock_path_beneath_attr path_beneath_attr;
     300         122 :         struct path path;
     301         122 :         struct landlock_ruleset *ruleset;
     302         122 :         int res, err;
     303             : 
     304         122 :         if (!landlock_initialized)
     305             :                 return -EOPNOTSUPP;
     306             : 
     307             :         /* No flag for now. */
     308         122 :         if (flags)
     309             :                 return -EINVAL;
     310             : 
     311         122 :         if (rule_type != LANDLOCK_RULE_PATH_BENEATH)
     312             :                 return -EINVAL;
     313             : 
     314             :         /* Copies raw user space buffer, only one type for now. */
     315         122 :         res = copy_from_user(&path_beneath_attr, rule_attr,
     316             :                         sizeof(path_beneath_attr));
     317         122 :         if (res)
     318             :                 return -EFAULT;
     319             : 
     320             :         /* Gets and checks the ruleset. */
     321         121 :         ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
     322         121 :         if (IS_ERR(ruleset))
     323           2 :                 return PTR_ERR(ruleset);
     324             : 
     325             :         /*
     326             :          * Informs about useless rule: empty allowed_access (i.e. deny rules)
     327             :          * are ignored in path walks.
     328             :          */
     329         119 :         if (!path_beneath_attr.allowed_access) {
     330           1 :                 err = -ENOMSG;
     331           1 :                 goto out_put_ruleset;
     332             :         }
     333             :         /*
     334             :          * Checks that allowed_access matches the @ruleset constraints
     335             :          * (ruleset->fs_access_masks[0] is automatically upgraded to 64-bits).
     336             :          */
     337         118 :         if ((path_beneath_attr.allowed_access | ruleset->fs_access_masks[0]) !=
     338             :                         ruleset->fs_access_masks[0]) {
     339           2 :                 err = -EINVAL;
     340           2 :                 goto out_put_ruleset;
     341             :         }
     342             : 
     343             :         /* Gets and checks the new rule. */
     344         116 :         err = get_path_from_fd(path_beneath_attr.parent_fd, &path);
     345         116 :         if (err)
     346           2 :                 goto out_put_ruleset;
     347             : 
     348             :         /* Imports the new rule. */
     349         228 :         err = landlock_append_fs_rule(ruleset, &path,
     350         114 :                         path_beneath_attr.allowed_access);
     351         114 :         path_put(&path);
     352             : 
     353         119 : out_put_ruleset:
     354         119 :         landlock_put_ruleset(ruleset);
     355         119 :         return err;
     356             : }
     357             : 
     358             : /* Enforcement */
     359             : 
     360             : /**
     361             :  * sys_landlock_restrict_self - Enforce a ruleset on the calling thread
     362             :  *
     363             :  * @ruleset_fd: File descriptor tied to the ruleset to merge with the target.
     364             :  * @flags: Must be 0.
     365             :  *
     366             :  * This system call enables to enforce a Landlock ruleset on the current
     367             :  * thread.  Enforcing a ruleset requires that the task has CAP_SYS_ADMIN in its
     368             :  * namespace or is running with no_new_privs.  This avoids scenarios where
     369             :  * unprivileged tasks can affect the behavior of privileged children.
     370             :  *
     371             :  * Possible returned errors are:
     372             :  *
     373             :  * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
     374             :  * - EINVAL: @flags is not 0.
     375             :  * - EBADF: @ruleset_fd is not a file descriptor for the current thread;
     376             :  * - EBADFD: @ruleset_fd is not a ruleset file descriptor;
     377             :  * - EPERM: @ruleset_fd has no read access to the underlying ruleset, or the
     378             :  *   current thread is not running with no_new_privs, or it doesn't have
     379             :  *   CAP_SYS_ADMIN in its namespace.
     380             :  * - E2BIG: The maximum number of stacked rulesets is reached for the current
     381             :  *   thread.
     382             :  */
     383         296 : SYSCALL_DEFINE2(landlock_restrict_self,
     384             :                 const int, ruleset_fd, const __u32, flags)
     385             : {
     386         148 :         struct landlock_ruleset *new_dom, *ruleset;
     387         148 :         struct cred *new_cred;
     388         148 :         struct landlock_cred_security *new_llcred;
     389         148 :         int err;
     390             : 
     391         148 :         if (!landlock_initialized)
     392             :                 return -EOPNOTSUPP;
     393             : 
     394             :         /* No flag for now. */
     395         148 :         if (flags)
     396             :                 return -EINVAL;
     397             : 
     398             :         /*
     399             :          * Similar checks as for seccomp(2), except that an -EPERM may be
     400             :          * returned.
     401             :          */
     402         150 :         if (!task_no_new_privs(current) &&
     403           2 :                         !ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN))
     404             :                 return -EPERM;
     405             : 
     406             :         /* Gets and checks the ruleset. */
     407         146 :         ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ);
     408         146 :         if (IS_ERR(ruleset))
     409           1 :                 return PTR_ERR(ruleset);
     410             : 
     411             :         /* Prepares new credentials. */
     412         145 :         new_cred = prepare_creds();
     413         145 :         if (!new_cred) {
     414           0 :                 err = -ENOMEM;
     415           0 :                 goto out_put_ruleset;
     416             :         }
     417         145 :         new_llcred = landlock_cred(new_cred);
     418             : 
     419             :         /*
     420             :          * There is no possible race condition while copying and manipulating
     421             :          * the current credentials because they are dedicated per thread.
     422             :          */
     423         145 :         new_dom = landlock_merge_ruleset(new_llcred->domain, ruleset);
     424         145 :         if (IS_ERR(new_dom)) {
     425           2 :                 err = PTR_ERR(new_dom);
     426           2 :                 goto out_put_creds;
     427             :         }
     428             : 
     429             :         /* Replaces the old (prepared) domain. */
     430         143 :         landlock_put_ruleset(new_llcred->domain);
     431         143 :         new_llcred->domain = new_dom;
     432             : 
     433         143 :         landlock_put_ruleset(ruleset);
     434         143 :         return commit_creds(new_cred);
     435             : 
     436           2 : out_put_creds:
     437           2 :         abort_creds(new_cred);
     438             : 
     439           2 : out_put_ruleset:
     440           2 :         landlock_put_ruleset(ruleset);
     441           2 :         return err;
     442             : }

Generated by: LCOV version 1.14