LCOV - code coverage report
Current view: top level - fs - mnt_idmapping.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-djwx @ Mon Jul 31 20:08:22 PDT 2023 Lines: 61 66 92.4 %
Date: 2023-07-31 20:08:22 Functions: 9 9 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /* Copyright (c) 2022 Christian Brauner <brauner@kernel.org> */
       3             : 
       4             : #include <linux/cred.h>
       5             : #include <linux/fs.h>
       6             : #include <linux/mnt_idmapping.h>
       7             : #include <linux/slab.h>
       8             : #include <linux/user_namespace.h>
       9             : 
      10             : #include "internal.h"
      11             : 
      12             : struct mnt_idmap {
      13             :         struct user_namespace *owner;
      14             :         refcount_t count;
      15             : };
      16             : 
      17             : /*
      18             :  * Carries the initial idmapping of 0:0:4294967295 which is an identity
      19             :  * mapping. This means that {g,u}id 0 is mapped to {g,u}id 0, {g,u}id 1 is
      20             :  * mapped to {g,u}id 1, [...], {g,u}id 1000 to {g,u}id 1000, [...].
      21             :  */
      22             : struct mnt_idmap nop_mnt_idmap = {
      23             :         .owner  = &init_user_ns,
      24             :         .count  = REFCOUNT_INIT(1),
      25             : };
      26             : EXPORT_SYMBOL_GPL(nop_mnt_idmap);
      27             : 
      28             : /**
      29             :  * check_fsmapping - check whether an mount idmapping is allowed
      30             :  * @idmap: idmap of the relevent mount
      31             :  * @sb:    super block of the filesystem
      32             :  *
      33             :  * Return: true if @idmap is allowed, false if not.
      34             :  */
      35        1254 : bool check_fsmapping(const struct mnt_idmap *idmap,
      36             :                      const struct super_block *sb)
      37             : {
      38        1254 :         return idmap->owner != sb->s_user_ns;
      39             : }
      40             : 
      41             : /**
      42             :  * initial_idmapping - check whether this is the initial mapping
      43             :  * @ns: idmapping to check
      44             :  *
      45             :  * Check whether this is the initial mapping, mapping 0 to 0, 1 to 1,
      46             :  * [...], 1000 to 1000 [...].
      47             :  *
      48             :  * Return: true if this is the initial mapping, false if not.
      49             :  */
      50             : static inline bool initial_idmapping(const struct user_namespace *ns)
      51             : {
      52 >15942*10^7 :         return ns == &init_user_ns;
      53             : }
      54             : 
      55             : /**
      56             :  * no_idmapping - check whether we can skip remapping a kuid/gid
      57             :  * @mnt_userns: the mount's idmapping
      58             :  * @fs_userns: the filesystem's idmapping
      59             :  *
      60             :  * This function can be used to check whether a remapping between two
      61             :  * idmappings is required.
      62             :  * An idmapped mount is a mount that has an idmapping attached to it that
      63             :  * is different from the filsystem's idmapping and the initial idmapping.
      64             :  * If the initial mapping is used or the idmapping of the mount and the
      65             :  * filesystem are identical no remapping is required.
      66             :  *
      67             :  * Return: true if remapping can be skipped, false if not.
      68             :  */
      69             : static inline bool no_idmapping(const struct user_namespace *mnt_userns,
      70             :                                 const struct user_namespace *fs_userns)
      71             : {
      72    32190176 :         return initial_idmapping(mnt_userns) || mnt_userns == fs_userns;
      73             : }
      74             : 
      75             : /**
      76             :  * make_vfsuid - map a filesystem kuid according to an idmapping
      77             :  * @idmap: the mount's idmapping
      78             :  * @fs_userns: the filesystem's idmapping
      79             :  * @kuid : kuid to be mapped
      80             :  *
      81             :  * Take a @kuid and remap it from @fs_userns into @idmap. Use this
      82             :  * function when preparing a @kuid to be reported to userspace.
      83             :  *
      84             :  * If no_idmapping() determines that this is not an idmapped mount we can
      85             :  * simply return @kuid unchanged.
      86             :  * If initial_idmapping() tells us that the filesystem is not mounted with an
      87             :  * idmapping we know the value of @kuid won't change when calling
      88             :  * from_kuid() so we can simply retrieve the value via __kuid_val()
      89             :  * directly.
      90             :  *
      91             :  * Return: @kuid mapped according to @idmap.
      92             :  * If @kuid has no mapping in either @idmap or @fs_userns INVALID_UID is
      93             :  * returned.
      94             :  */
      95             : 
      96 99879358540 : vfsuid_t make_vfsuid(struct mnt_idmap *idmap,
      97             :                                    struct user_namespace *fs_userns,
      98             :                                    kuid_t kuid)
      99             : {
     100 99879358540 :         uid_t uid;
     101 99879358540 :         struct user_namespace *mnt_userns = idmap->owner;
     102             : 
     103 99898800801 :         if (no_idmapping(mnt_userns, fs_userns))
     104 99859916279 :                 return VFSUIDT_INIT(kuid);
     105    19442261 :         if (initial_idmapping(fs_userns))
     106    19442261 :                 uid = __kuid_val(kuid);
     107             :         else
     108           0 :                 uid = from_kuid(fs_userns, kuid);
     109    19442261 :         if (uid == (uid_t)-1)
     110          80 :                 return INVALID_VFSUID;
     111    19442181 :         return VFSUIDT_INIT(make_kuid(mnt_userns, uid));
     112             : }
     113             : EXPORT_SYMBOL_GPL(make_vfsuid);
     114             : 
     115             : /**
     116             :  * make_vfsgid - map a filesystem kgid according to an idmapping
     117             :  * @idmap: the mount's idmapping
     118             :  * @fs_userns: the filesystem's idmapping
     119             :  * @kgid : kgid to be mapped
     120             :  *
     121             :  * Take a @kgid and remap it from @fs_userns into @idmap. Use this
     122             :  * function when preparing a @kgid to be reported to userspace.
     123             :  *
     124             :  * If no_idmapping() determines that this is not an idmapped mount we can
     125             :  * simply return @kgid unchanged.
     126             :  * If initial_idmapping() tells us that the filesystem is not mounted with an
     127             :  * idmapping we know the value of @kgid won't change when calling
     128             :  * from_kgid() so we can simply retrieve the value via __kgid_val()
     129             :  * directly.
     130             :  *
     131             :  * Return: @kgid mapped according to @idmap.
     132             :  * If @kgid has no mapping in either @idmap or @fs_userns INVALID_GID is
     133             :  * returned.
     134             :  */
     135 58235935000 : vfsgid_t make_vfsgid(struct mnt_idmap *idmap,
     136             :                      struct user_namespace *fs_userns, kgid_t kgid)
     137             : {
     138 58235935000 :         gid_t gid;
     139 58235935000 :         struct user_namespace *mnt_userns = idmap->owner;
     140             : 
     141 58243270510 :         if (no_idmapping(mnt_userns, fs_userns))
     142 58228599490 :                 return VFSGIDT_INIT(kgid);
     143     7335510 :         if (initial_idmapping(fs_userns))
     144     7335510 :                 gid = __kgid_val(kgid);
     145             :         else
     146           0 :                 gid = from_kgid(fs_userns, kgid);
     147     7335510 :         if (gid == (gid_t)-1)
     148           0 :                 return INVALID_VFSGID;
     149     7335510 :         return VFSGIDT_INIT(make_kgid(mnt_userns, gid));
     150             : }
     151             : EXPORT_SYMBOL_GPL(make_vfsgid);
     152             : 
     153             : /**
     154             :  * from_vfsuid - map a vfsuid into the filesystem idmapping
     155             :  * @idmap: the mount's idmapping
     156             :  * @fs_userns: the filesystem's idmapping
     157             :  * @vfsuid : vfsuid to be mapped
     158             :  *
     159             :  * Map @vfsuid into the filesystem idmapping. This function has to be used in
     160             :  * order to e.g. write @vfsuid to inode->i_uid.
     161             :  *
     162             :  * Return: @vfsuid mapped into the filesystem idmapping
     163             :  */
     164   636550423 : kuid_t from_vfsuid(struct mnt_idmap *idmap,
     165             :                    struct user_namespace *fs_userns, vfsuid_t vfsuid)
     166             : {
     167   636550423 :         uid_t uid;
     168   636550423 :         struct user_namespace *mnt_userns = idmap->owner;
     169             : 
     170   639257173 :         if (no_idmapping(mnt_userns, fs_userns))
     171   633843673 :                 return AS_KUIDT(vfsuid);
     172     2706750 :         uid = from_kuid(mnt_userns, AS_KUIDT(vfsuid));
     173     2706750 :         if (uid == (uid_t)-1)
     174         689 :                 return INVALID_UID;
     175     2706061 :         if (initial_idmapping(fs_userns))
     176     2706061 :                 return KUIDT_INIT(uid);
     177           0 :         return make_kuid(fs_userns, uid);
     178             : }
     179             : EXPORT_SYMBOL_GPL(from_vfsuid);
     180             : 
     181             : /**
     182             :  * from_vfsgid - map a vfsgid into the filesystem idmapping
     183             :  * @idmap: the mount's idmapping
     184             :  * @fs_userns: the filesystem's idmapping
     185             :  * @vfsgid : vfsgid to be mapped
     186             :  *
     187             :  * Map @vfsgid into the filesystem idmapping. This function has to be used in
     188             :  * order to e.g. write @vfsgid to inode->i_gid.
     189             :  *
     190             :  * Return: @vfsgid mapped into the filesystem idmapping
     191             :  */
     192   636565981 : kgid_t from_vfsgid(struct mnt_idmap *idmap,
     193             :                    struct user_namespace *fs_userns, vfsgid_t vfsgid)
     194             : {
     195   636565981 :         gid_t gid;
     196   636565981 :         struct user_namespace *mnt_userns = idmap->owner;
     197             : 
     198   639271636 :         if (no_idmapping(mnt_userns, fs_userns))
     199   633860326 :                 return AS_KGIDT(vfsgid);
     200     2705655 :         gid = from_kgid(mnt_userns, AS_KGIDT(vfsgid));
     201     2705655 :         if (gid == (gid_t)-1)
     202         261 :                 return INVALID_GID;
     203     2705394 :         if (initial_idmapping(fs_userns))
     204     2705394 :                 return KGIDT_INIT(gid);
     205           0 :         return make_kgid(fs_userns, gid);
     206             : }
     207             : EXPORT_SYMBOL_GPL(from_vfsgid);
     208             : 
     209             : #ifdef CONFIG_MULTIUSER
     210             : /**
     211             :  * vfsgid_in_group_p() - check whether a vfsuid matches the caller's groups
     212             :  * @vfsgid: the mnt gid to match
     213             :  *
     214             :  * This function can be used to determine whether @vfsuid matches any of the
     215             :  * caller's groups.
     216             :  *
     217             :  * Return: 1 if vfsuid matches caller's groups, 0 if not.
     218             :  */
     219    39508887 : int vfsgid_in_group_p(vfsgid_t vfsgid)
     220             : {
     221    39508887 :         return in_group_p(AS_KGIDT(vfsgid));
     222             : }
     223             : #else
     224             : int vfsgid_in_group_p(vfsgid_t vfsgid)
     225             : {
     226             :         return 1;
     227             : }
     228             : #endif
     229             : EXPORT_SYMBOL_GPL(vfsgid_in_group_p);
     230             : 
     231        1254 : struct mnt_idmap *alloc_mnt_idmap(struct user_namespace *mnt_userns)
     232             : {
     233        1254 :         struct mnt_idmap *idmap;
     234             : 
     235        1254 :         idmap = kzalloc(sizeof(struct mnt_idmap), GFP_KERNEL_ACCOUNT);
     236        1254 :         if (!idmap)
     237             :                 return ERR_PTR(-ENOMEM);
     238             : 
     239        1254 :         idmap->owner = get_user_ns(mnt_userns);
     240        1254 :         refcount_set(&idmap->count, 1);
     241        1254 :         return idmap;
     242             : }
     243             : 
     244             : /**
     245             :  * mnt_idmap_get - get a reference to an idmapping
     246             :  * @idmap: the idmap to bump the reference on
     247             :  *
     248             :  * If @idmap is not the @nop_mnt_idmap bump the reference count.
     249             :  *
     250             :  * Return: @idmap with reference count bumped if @not_mnt_idmap isn't passed.
     251             :  */
     252     2757762 : struct mnt_idmap *mnt_idmap_get(struct mnt_idmap *idmap)
     253             : {
     254     2757762 :         if (idmap != &nop_mnt_idmap)
     255        6123 :                 refcount_inc(&idmap->count);
     256             : 
     257     2757762 :         return idmap;
     258             : }
     259             : 
     260             : /**
     261             :  * mnt_idmap_put - put a reference to an idmapping
     262             :  * @idmap: the idmap to put the reference on
     263             :  *
     264             :  * If this is a non-initial idmapping, put the reference count when a mount is
     265             :  * released and free it if we're the last user.
     266             :  */
     267     2855845 : void mnt_idmap_put(struct mnt_idmap *idmap)
     268             : {
     269     2855845 :         if (idmap != &nop_mnt_idmap && refcount_dec_and_test(&idmap->count)) {
     270        1253 :                 put_user_ns(idmap->owner);
     271        1253 :                 kfree(idmap);
     272             :         }
     273     2855845 : }

Generated by: LCOV version 1.14