Skip to content

Commit

Permalink
Support idmapped mount in user namespace
Browse files Browse the repository at this point in the history
Signed-off-by: Youzhong Yang <yyang@mathworks.com>
  • Loading branch information
Youzhong Yang committed Oct 30, 2022
1 parent b37d495 commit 866c144
Show file tree
Hide file tree
Showing 14 changed files with 315 additions and 66 deletions.
8 changes: 4 additions & 4 deletions include/os/linux/kernel/linux/xattr_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ fn(const struct xattr_handler *handler, struct user_namespace *user_ns, \
struct dentry *dentry, struct inode *inode, const char *name, \
const void *buffer, size_t size, int flags) \
{ \
return (__ ## fn(inode, name, buffer, size, flags)); \
return (__ ## fn(user_ns, inode, name, buffer, size, flags)); \
}
/*
* 4.7 API change,
Expand All @@ -160,7 +160,7 @@ fn(const struct xattr_handler *handler, struct dentry *dentry, \
struct inode *inode, const char *name, const void *buffer, \
size_t size, int flags) \
{ \
return (__ ## fn(inode, name, buffer, size, flags)); \
return (__ ## fn(NULL, inode, name, buffer, size, flags)); \
}
/*
* 4.4 API change,
Expand All @@ -174,7 +174,7 @@ static int \
fn(const struct xattr_handler *handler, struct dentry *dentry, \
const char *name, const void *buffer, size_t size, int flags) \
{ \
return (__ ## fn(dentry->d_inode, name, buffer, size, flags)); \
return (__ ## fn(NULL, dentry->d_inode, name, buffer, size, flags));\
}
/*
* 2.6.33 API change,
Expand All @@ -187,7 +187,7 @@ static int \
fn(struct dentry *dentry, const char *name, const void *buffer, \
size_t size, int flags, int unused_handler_flags) \
{ \
return (__ ## fn(dentry->d_inode, name, buffer, size, flags)); \
return (__ ## fn(NULL, dentry->d_inode, name, buffer, size, flags));\
}
#else
#error "Unsupported kernel"
Expand Down
74 changes: 57 additions & 17 deletions include/os/linux/spl/sys/cred.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,32 +45,73 @@ typedef struct cred cred_t;
#define SGID_TO_KGID(x) (KGIDT_INIT(x))
#define KGIDP_TO_SGIDP(x) (&(x)->val)

static inline uid_t zfs_uid_into_mnt(struct user_namespace *mnt_ns, uid_t uid)
extern boolean_t zfs_is_init_userns(struct user_namespace *ns);
extern struct user_namespace *zfs_get_init_userns(void);

static inline struct user_namespace *zfs_i_user_ns(struct inode *inode)
{
#ifdef HAVE_SUPER_USER_NS
return (inode->i_sb->s_user_ns);
#else
return (NULL);
#endif
}

static inline boolean_t zfs_no_idmapping(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns)
{
if (mnt_ns)
return (__kuid_val(make_kuid(mnt_ns, uid)));
return (uid);
return (!mnt_userns || !fs_userns ||
zfs_is_init_userns(mnt_userns) || mnt_userns == fs_userns);
}

static inline gid_t zfs_gid_into_mnt(struct user_namespace *mnt_ns, gid_t gid)
static inline uid_t zfs_uid_to_vfsuid(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns, uid_t uid)
{
if (mnt_ns)
return (__kgid_val(make_kgid(mnt_ns, gid)));
return (gid);
if (zfs_no_idmapping(mnt_userns, fs_userns))
return (uid);
if (!zfs_is_init_userns(fs_userns))
uid = from_kuid(fs_userns, KUIDT_INIT(uid));
if (uid == (uid_t)-1)
return (uid);
return (__kuid_val(make_kuid(mnt_userns, uid)));
}

static inline uid_t zfs_uid_from_mnt(struct user_namespace *mnt_ns, uid_t uid)
static inline gid_t zfs_gid_to_vfsgid(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns, gid_t gid)
{
if (mnt_ns)
return (from_kuid(mnt_ns, KUIDT_INIT(uid)));
return (uid);
if (zfs_no_idmapping(mnt_userns, fs_userns))
return (gid);
if (!zfs_is_init_userns(fs_userns))
gid = from_kgid(fs_userns, KGIDT_INIT(gid));
if (gid == (gid_t)-1)
return (gid);
return (__kgid_val(make_kgid(mnt_userns, gid)));
}

static inline gid_t zfs_gid_from_mnt(struct user_namespace *mnt_ns, gid_t gid)
static inline uid_t zfs_vfsuid_to_uid(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns, uid_t uid)
{
if (mnt_ns)
return (from_kgid(mnt_ns, KGIDT_INIT(gid)));
return (gid);
if (zfs_no_idmapping(mnt_userns, fs_userns))
return (uid);
uid = from_kuid(mnt_userns, KUIDT_INIT(uid));
if (uid == (uid_t)-1)
return (uid);
if (zfs_is_init_userns(fs_userns))
return (uid);
return (__kuid_val(make_kuid(fs_userns, uid)));
}

static inline gid_t zfs_vfsgid_to_gid(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns, gid_t gid)
{
if (zfs_no_idmapping(mnt_userns, fs_userns))
return (gid);
gid = from_kgid(mnt_userns, KGIDT_INIT(gid));
if (gid == (gid_t)-1)
return (gid);
if (zfs_is_init_userns(fs_userns))
return (gid);
return (__kgid_val(make_kgid(fs_userns, gid)));
}

extern void crhold(cred_t *cr);
Expand All @@ -81,5 +122,4 @@ extern gid_t crgetgid(const cred_t *cr);
extern int crgetngroups(const cred_t *cr);
extern gid_t *crgetgroups(const cred_t *cr);
extern int groupmember(gid_t gid, const cred_t *cr);

#endif /* _SPL_CRED_H */
5 changes: 3 additions & 2 deletions include/os/linux/zfs/sys/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ int secpolicy_vnode_create_gid(const cred_t *);
int secpolicy_vnode_remove(const cred_t *);
int secpolicy_vnode_setdac(const cred_t *, uid_t);
int secpolicy_vnode_setid_retain(struct znode *, const cred_t *, boolean_t);
int secpolicy_vnode_setids_setgids(const cred_t *, gid_t, zuserns_t *);
int secpolicy_vnode_setids_setgids(const cred_t *, gid_t, zuserns_t *,
zuserns_t *);
int secpolicy_zinject(const cred_t *);
int secpolicy_zfs(const cred_t *);
int secpolicy_zfs_proc(const cred_t *, proc_t *);
void secpolicy_setid_clear(vattr_t *, cred_t *);
int secpolicy_setid_setsticky_clear(struct inode *, vattr_t *,
const vattr_t *, cred_t *, zuserns_t *);
const vattr_t *, cred_t *, zuserns_t *, zuserns_t *);
int secpolicy_xvattr(xvattr_t *, uid_t, cred_t *, mode_t);
int secpolicy_vnode_setattr(cred_t *, struct inode *, struct vattr *,
const struct vattr *, int, int (void *, int, cred_t *), void *);
Expand Down
24 changes: 24 additions & 0 deletions module/os/linux/spl/spl-cred.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,30 @@ crgetgid(const cred_t *cr)
return (KGID_TO_SGID(cr->fsgid));
}

/* Check if the user ns is the initial one */
boolean_t
zfs_is_init_userns(struct user_namespace *user_ns)
{
#if defined(CONFIG_USER_NS)
return (user_ns == &init_user_ns);
#else
return (B_FALSE);
#endif
}

/* Return the initial user ns */
struct user_namespace *
zfs_get_init_userns(void)
{
#if defined(CONFIG_USER_NS)
return (&init_user_ns);
#else
return (NULL);
#endif
}

EXPORT_SYMBOL(zfs_is_init_userns);
EXPORT_SYMBOL(zfs_get_init_userns);
EXPORT_SYMBOL(crhold);
EXPORT_SYMBOL(crfree);
EXPORT_SYMBOL(crgetuid);
Expand Down
17 changes: 10 additions & 7 deletions module/os/linux/zfs/policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,10 @@ secpolicy_vnode_setid_retain(struct znode *zp __maybe_unused, const cred_t *cr,
* Determine that subject can set the file setgid flag.
*/
int
secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid, zuserns_t *mnt_ns)
secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid, zuserns_t *mnt_ns,
zuserns_t *fs_ns)
{
gid = zfs_gid_into_mnt(mnt_ns, gid);
gid = zfs_gid_to_vfsgid(mnt_ns, fs_ns, gid);
#if defined(CONFIG_USER_NS)
if (!kgid_has_mapping(cr->user_ns, SGID_TO_KGID(gid)))
return (EPERM);
Expand Down Expand Up @@ -285,9 +286,10 @@ secpolicy_setid_clear(vattr_t *vap, cred_t *cr)
* Determine that subject can set the file setid flags.
*/
static int
secpolicy_vnode_setid_modify(const cred_t *cr, uid_t owner, zuserns_t *mnt_ns)
secpolicy_vnode_setid_modify(const cred_t *cr, uid_t owner, zuserns_t *mnt_ns,
zuserns_t *fs_ns)
{
owner = zfs_uid_into_mnt(mnt_ns, owner);
owner = zfs_uid_to_vfsuid(mnt_ns, fs_ns, owner);

if (crgetuid(cr) == owner)
return (0);
Expand All @@ -313,13 +315,13 @@ secpolicy_vnode_stky_modify(const cred_t *cr)

int
secpolicy_setid_setsticky_clear(struct inode *ip, vattr_t *vap,
const vattr_t *ovap, cred_t *cr, zuserns_t *mnt_ns)
const vattr_t *ovap, cred_t *cr, zuserns_t *mnt_ns, zuserns_t *fs_ns)
{
int error;

if ((vap->va_mode & S_ISUID) != 0 &&
(error = secpolicy_vnode_setid_modify(cr,
ovap->va_uid, mnt_ns)) != 0) {
ovap->va_uid, mnt_ns, fs_ns)) != 0) {
return (error);
}

Expand All @@ -337,7 +339,8 @@ secpolicy_setid_setsticky_clear(struct inode *ip, vattr_t *vap,
* group-id bit.
*/
if ((vap->va_mode & S_ISGID) != 0 &&
secpolicy_vnode_setids_setgids(cr, ovap->va_gid, mnt_ns) != 0) {
secpolicy_vnode_setids_setgids(cr, ovap->va_gid,
mnt_ns, fs_ns) != 0) {
vap->va_mode &= ~S_ISGID;
}

Expand Down
10 changes: 6 additions & 4 deletions module/os/linux/zfs/zfs_acl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1889,7 +1889,8 @@ zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
acl_ids->z_mode |= S_ISGID;
} else {
if ((acl_ids->z_mode & S_ISGID) &&
secpolicy_vnode_setids_setgids(cr, gid, mnt_ns) != 0) {
secpolicy_vnode_setids_setgids(cr, gid, mnt_ns,
zfs_i_user_ns(ZTOI(dzp))) != 0) {
acl_ids->z_mode &= ~S_ISGID;
}
}
Expand Down Expand Up @@ -2301,9 +2302,9 @@ zfs_zaccess_aces_check(znode_t *zp, uint32_t *working_mode,
uid_t fowner;

if (mnt_ns) {
fowner = zfs_uid_into_mnt(mnt_ns,
fowner = zfs_uid_to_vfsuid(mnt_ns, zfs_i_user_ns(ZTOI(zp)),
KUID_TO_SUID(ZTOI(zp)->i_uid));
gowner = zfs_gid_into_mnt(mnt_ns,
gowner = zfs_gid_to_vfsgid(mnt_ns, zfs_i_user_ns(ZTOI(zp)),
KGID_TO_SGID(ZTOI(zp)->i_gid));
} else
zfs_fuid_map_ids(zp, cr, &fowner, &gowner);
Expand Down Expand Up @@ -2662,7 +2663,8 @@ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr,
}
}

owner = zfs_uid_into_mnt(mnt_ns, KUID_TO_SUID(ZTOI(zp)->i_uid));
owner = zfs_uid_to_vfsuid(mnt_ns, zfs_i_user_ns(ZTOI(zp)),
KUID_TO_SUID(ZTOI(zp)->i_uid));
owner = zfs_fuid_map_id(ZTOZSB(zp), owner, cr, ZFS_OWNER);

/*
Expand Down
10 changes: 5 additions & 5 deletions module/os/linux/zfs/zfs_vnops_os.c
Original file line number Diff line number Diff line change
Expand Up @@ -2024,10 +2024,10 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zuserns_t *mnt_ns)
* Take ownership or chgrp to group we are a member of
*/

uid = zfs_uid_into_mnt((struct user_namespace *)mnt_ns,
vap->va_uid);
gid = zfs_gid_into_mnt((struct user_namespace *)mnt_ns,
vap->va_gid);
uid = zfs_uid_to_vfsuid((struct user_namespace *)mnt_ns,
zfs_i_user_ns(ip), vap->va_uid);
gid = zfs_gid_to_vfsgid((struct user_namespace *)mnt_ns,
zfs_i_user_ns(ip), vap->va_gid);
take_owner = (mask & ATTR_UID) && (uid == crgetuid(cr));
take_group = (mask & ATTR_GID) &&
zfs_groupmember(zfsvfs, gid, cr);
Expand Down Expand Up @@ -2162,7 +2162,7 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zuserns_t *mnt_ns)
if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr,
mnt_ns) == 0) {
err = secpolicy_setid_setsticky_clear(ip, vap,
&oldva, cr, mnt_ns);
&oldva, cr, mnt_ns, zfs_i_user_ns(ip));
if (err)
goto out3;
trim_mask |= ATTR_MODE;
Expand Down
8 changes: 4 additions & 4 deletions module/os/linux/zfs/zpl_inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,16 @@ zpl_vap_init(vattr_t *vap, struct inode *dir, umode_t mode, cred_t *cr,
vap->va_mask = ATTR_MODE;
vap->va_mode = mode;

vap->va_uid = zfs_uid_from_mnt((struct user_namespace *)mnt_ns,
crgetuid(cr));
vap->va_uid = zfs_vfsuid_to_uid((struct user_namespace *)mnt_ns,
zfs_i_user_ns(dir), crgetuid(cr));

if (dir && dir->i_mode & S_ISGID) {
vap->va_gid = KGID_TO_SGID(dir->i_gid);
if (S_ISDIR(mode))
vap->va_mode |= S_ISGID;
} else {
vap->va_gid = zfs_gid_from_mnt((struct user_namespace *)mnt_ns,
crgetgid(cr));
vap->va_gid = zfs_vfsgid_to_gid((struct user_namespace *)mnt_ns,
zfs_i_user_ns(dir), crgetgid(cr));
}
}

Expand Down
Loading

0 comments on commit 866c144

Please sign in to comment.