diff options
Diffstat (limited to 'fs/posix_acl.c')
| -rw-r--r-- | fs/posix_acl.c | 103 | 
1 files changed, 74 insertions, 29 deletions
diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 95882b3f5f62..f3309a7edb49 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -345,10 +345,13 @@ EXPORT_SYMBOL(posix_acl_from_mode);   * by the acl. Returns -E... otherwise.   */  int -posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want) +posix_acl_permission(struct user_namespace *mnt_userns, struct inode *inode, +		     const struct posix_acl *acl, int want)  {  	const struct posix_acl_entry *pa, *pe, *mask_obj;  	int found = 0; +	kuid_t uid; +	kgid_t gid;  	want &= MAY_READ | MAY_WRITE | MAY_EXEC; @@ -356,22 +359,26 @@ posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)                  switch(pa->e_tag) {                          case ACL_USER_OBJ:  				/* (May have been checked already) */ -				if (uid_eq(inode->i_uid, current_fsuid())) +				uid = i_uid_into_mnt(mnt_userns, inode); +				if (uid_eq(uid, current_fsuid()))                                          goto check_perm;                                  break;                          case ACL_USER: -				if (uid_eq(pa->e_uid, current_fsuid())) +				uid = kuid_into_mnt(mnt_userns, pa->e_uid); +				if (uid_eq(uid, current_fsuid()))                                          goto mask;  				break;                          case ACL_GROUP_OBJ: -                                if (in_group_p(inode->i_gid)) { +				gid = i_gid_into_mnt(mnt_userns, inode); +				if (in_group_p(gid)) {  					found = 1;  					if ((pa->e_perm & want) == want)  						goto mask;                                  }  				break;                          case ACL_GROUP: -				if (in_group_p(pa->e_gid)) { +				gid = kgid_into_mnt(mnt_userns, pa->e_gid); +				if (in_group_p(gid)) {  					found = 1;  					if ((pa->e_perm & want) == want)  						goto mask; @@ -551,8 +558,22 @@ __posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)  }  EXPORT_SYMBOL(__posix_acl_chmod); +/** + * posix_acl_chmod - chmod a posix acl + * + * @mnt_userns:	user namespace of the mount @inode was found from + * @inode:	inode to check permissions on + * @mode:	the new mode of @inode + * + * If the inode has been found through an idmapped mount the user namespace of + * the vfsmount must be passed through @mnt_userns. This function will then + * take care to map the inode according to @mnt_userns before checking + * permissions. On non-idmapped mounts or if permission checking is to be + * performed on the raw inode simply passs init_user_ns. + */  int -posix_acl_chmod(struct inode *inode, umode_t mode) + posix_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode, +		    umode_t mode)  {  	struct posix_acl *acl;  	int ret = 0; @@ -572,7 +593,7 @@ posix_acl_chmod(struct inode *inode, umode_t mode)  	ret = __posix_acl_chmod(&acl, GFP_KERNEL, mode);  	if (ret)  		return ret; -	ret = inode->i_op->set_acl(inode, acl, ACL_TYPE_ACCESS); +	ret = inode->i_op->set_acl(mnt_userns, inode, acl, ACL_TYPE_ACCESS);  	posix_acl_release(acl);  	return ret;  } @@ -631,9 +652,10 @@ EXPORT_SYMBOL_GPL(posix_acl_create);  /**   * posix_acl_update_mode  -  update mode in set_acl - * @inode: target inode - * @mode_p: mode (pointer) for update - * @acl: acl pointer + * @mnt_userns:	user namespace of the mount @inode was found from + * @inode:	target inode + * @mode_p:	mode (pointer) for update + * @acl:	acl pointer   *   * Update the file mode when setting an ACL: compute the new file permission   * bits based on the ACL.  In addition, if the ACL is equivalent to the new @@ -642,9 +664,16 @@ EXPORT_SYMBOL_GPL(posix_acl_create);   * As with chmod, clear the setgid bit if the caller is not in the owning group   * or capable of CAP_FSETID (see inode_change_ok).   * + * If the inode has been found through an idmapped mount the user namespace of + * the vfsmount must be passed through @mnt_userns. This function will then + * take care to map the inode according to @mnt_userns before checking + * permissions. On non-idmapped mounts or if permission checking is to be + * performed on the raw inode simply passs init_user_ns. + *   * Called from set_acl inode operations.   */ -int posix_acl_update_mode(struct inode *inode, umode_t *mode_p, +int posix_acl_update_mode(struct user_namespace *mnt_userns, +			  struct inode *inode, umode_t *mode_p,  			  struct posix_acl **acl)  {  	umode_t mode = inode->i_mode; @@ -655,8 +684,8 @@ int posix_acl_update_mode(struct inode *inode, umode_t *mode_p,  		return error;  	if (error == 0)  		*acl = NULL; -	if (!in_group_p(inode->i_gid) && -	    !capable_wrt_inode_uidgid(inode, CAP_FSETID)) +	if (!in_group_p(i_gid_into_mnt(mnt_userns, inode)) && +	    !capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))  		mode &= ~S_ISGID;  	*mode_p = mode;  	return 0; @@ -668,7 +697,8 @@ EXPORT_SYMBOL(posix_acl_update_mode);   */  static void posix_acl_fix_xattr_userns(  	struct user_namespace *to, struct user_namespace *from, -	void *value, size_t size) +	struct user_namespace *mnt_userns, +	void *value, size_t size, bool from_user)  {  	struct posix_acl_xattr_header *header = value;  	struct posix_acl_xattr_entry *entry = (void *)(header + 1), *end; @@ -693,10 +723,18 @@ static void posix_acl_fix_xattr_userns(  		switch(le16_to_cpu(entry->e_tag)) {  		case ACL_USER:  			uid = make_kuid(from, le32_to_cpu(entry->e_id)); +			if (from_user) +				uid = kuid_from_mnt(mnt_userns, uid); +			else +				uid = kuid_into_mnt(mnt_userns, uid);  			entry->e_id = cpu_to_le32(from_kuid(to, uid));  			break;  		case ACL_GROUP:  			gid = make_kgid(from, le32_to_cpu(entry->e_id)); +			if (from_user) +				gid = kgid_from_mnt(mnt_userns, gid); +			else +				gid = kgid_into_mnt(mnt_userns, gid);  			entry->e_id = cpu_to_le32(from_kgid(to, gid));  			break;  		default: @@ -705,20 +743,24 @@ static void posix_acl_fix_xattr_userns(  	}  } -void posix_acl_fix_xattr_from_user(void *value, size_t size) +void posix_acl_fix_xattr_from_user(struct user_namespace *mnt_userns, +				   void *value, size_t size)  {  	struct user_namespace *user_ns = current_user_ns(); -	if (user_ns == &init_user_ns) +	if ((user_ns == &init_user_ns) && (mnt_userns == &init_user_ns))  		return; -	posix_acl_fix_xattr_userns(&init_user_ns, user_ns, value, size); +	posix_acl_fix_xattr_userns(&init_user_ns, user_ns, mnt_userns, value, +				   size, true);  } -void posix_acl_fix_xattr_to_user(void *value, size_t size) +void posix_acl_fix_xattr_to_user(struct user_namespace *mnt_userns, +				 void *value, size_t size)  {  	struct user_namespace *user_ns = current_user_ns(); -	if (user_ns == &init_user_ns) +	if ((user_ns == &init_user_ns) && (mnt_userns == &init_user_ns))  		return; -	posix_acl_fix_xattr_userns(user_ns, &init_user_ns, value, size); +	posix_acl_fix_xattr_userns(user_ns, &init_user_ns, mnt_userns, value, +				   size, false);  }  /* @@ -858,7 +900,8 @@ posix_acl_xattr_get(const struct xattr_handler *handler,  }  int -set_posix_acl(struct inode *inode, int type, struct posix_acl *acl) +set_posix_acl(struct user_namespace *mnt_userns, struct inode *inode, +	      int type, struct posix_acl *acl)  {  	if (!IS_POSIXACL(inode))  		return -EOPNOTSUPP; @@ -867,7 +910,7 @@ set_posix_acl(struct inode *inode, int type, struct posix_acl *acl)  	if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))  		return acl ? -EACCES : 0; -	if (!inode_owner_or_capable(inode)) +	if (!inode_owner_or_capable(mnt_userns, inode))  		return -EPERM;  	if (acl) { @@ -875,15 +918,16 @@ set_posix_acl(struct inode *inode, int type, struct posix_acl *acl)  		if (ret)  			return ret;  	} -	return inode->i_op->set_acl(inode, acl, type); +	return inode->i_op->set_acl(mnt_userns, inode, acl, type);  }  EXPORT_SYMBOL(set_posix_acl);  static int  posix_acl_xattr_set(const struct xattr_handler *handler, -		    struct dentry *unused, struct inode *inode, -		    const char *name, const void *value, -		    size_t size, int flags) +			   struct user_namespace *mnt_userns, +			   struct dentry *unused, struct inode *inode, +			   const char *name, const void *value, size_t size, +			   int flags)  {  	struct posix_acl *acl = NULL;  	int ret; @@ -893,7 +937,7 @@ posix_acl_xattr_set(const struct xattr_handler *handler,  		if (IS_ERR(acl))  			return PTR_ERR(acl);  	} -	ret = set_posix_acl(inode, handler->flags, acl); +	ret = set_posix_acl(mnt_userns, inode, handler->flags, acl);  	posix_acl_release(acl);  	return ret;  } @@ -922,12 +966,13 @@ const struct xattr_handler posix_acl_default_xattr_handler = {  };  EXPORT_SYMBOL_GPL(posix_acl_default_xattr_handler); -int simple_set_acl(struct inode *inode, struct posix_acl *acl, int type) +int simple_set_acl(struct user_namespace *mnt_userns, struct inode *inode, +		   struct posix_acl *acl, int type)  {  	int error;  	if (type == ACL_TYPE_ACCESS) { -		error = posix_acl_update_mode(inode, +		error = posix_acl_update_mode(mnt_userns, inode,  				&inode->i_mode, &acl);  		if (error)  			return error;  | 
