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