diff mbox series

[07/34] capability: handle idmapped mounts

Message ID 20201029003252.2128653-8-christian.brauner@ubuntu.com
State New
Headers show
Series fs: idmapped mounts | expand

Commit Message

Christian Brauner Oct. 29, 2020, 12:32 a.m. UTC
In order to determine whether a caller holds privilege over a given
inode the capability framework exposes the two helpers
privileged_wrt_inode_uidgid() and capable_wrt_inode_uidgid(). The former
verifies that the inode has a mapping in the caller's user namespace and
the latter additionally verifies that the caller has the requested
capability in their current user namespace. If the inode is accessed
through an idmapped mount we first need to map it according to the
mount's user namespace. Afterwards the checks are identical to
non-idmapped inodes. If the initial user namespace is passed all
operations are a nop so non-idmapped mounts will not see a change in
behavior and will also not see any performance impact.
Since the privileged_wrt_inode_uidgid() helper only has one caller it
makes more sense to simply add an additional user namespace argument and
adapt the single callsite it is used in. The capable_wrt_inode_uidgid()
helper is used in more places so we introduce a new
capable_wrt_mapped_inode_uidgid() helper which can be used by the vfs.

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
---
 fs/exec.c                  |  2 +-
 include/linux/capability.h |  6 +++++-
 kernel/capability.c        | 22 ++++++++++++++++------
 3 files changed, 22 insertions(+), 8 deletions(-)

Comments

Christoph Hellwig Nov. 1, 2020, 2:48 p.m. UTC | #1
>  /**
>   * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
>   * @inode: The inode in question
> @@ -501,9 +513,7 @@ bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *
>   */
>  bool capable_wrt_inode_uidgid(const struct inode *inode, int cap)
>  {
> +	return capable_wrt_mapped_inode_uidgid(&init_user_ns, inode, cap);
>  }
>  EXPORT_SYMBOL(capable_wrt_inode_uidgid);

Please avoid these silly wrappers and just switch all callers to pass
the namespaces instead of creating boilerplate code.  Same for the other
functions where you do this even even worse the method calls.
Christian Brauner Nov. 2, 2020, 1:23 p.m. UTC | #2
On Sun, Nov 01, 2020 at 02:48:09PM +0000, Christoph Hellwig wrote:
> >  /**
> >   * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
> >   * @inode: The inode in question
> > @@ -501,9 +513,7 @@ bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *
> >   */
> >  bool capable_wrt_inode_uidgid(const struct inode *inode, int cap)
> >  {
> > +	return capable_wrt_mapped_inode_uidgid(&init_user_ns, inode, cap);
> >  }
> >  EXPORT_SYMBOL(capable_wrt_inode_uidgid);
> 
> Please avoid these silly wrappers and just switch all callers to pass
> the namespaces instead of creating boilerplate code.  Same for the other
> functions where you do this even even worse the method calls.

Christoph,

Thanks for the review!  

Ok, so I'll switch:
- all helpers to take an additional argument
  (capable_wrt_inode_uidgid()/inode_permission()/vfs_*() etc.)
- all inode method calls to take an additional argument (I assume that's
  what you're referring to: ->create()/->mknod()/->mkdir() etc.)
  I've always assumed that this is what we'd be doing in the end anyway
  (I've mentioned it in the commit message for the inode_operations
  method's. This will be a bit of work but we can get that done!)
diff mbox series

Patch

diff --git a/fs/exec.c b/fs/exec.c
index 547a2390baf5..8e75d7a33514 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1398,7 +1398,7 @@  void would_dump(struct linux_binprm *bprm, struct file *file)
 		/* Ensure mm->user_ns contains the executable */
 		user_ns = old = bprm->mm->user_ns;
 		while ((user_ns != &init_user_ns) &&
-		       !privileged_wrt_inode_uidgid(user_ns, inode))
+		       !privileged_wrt_inode_uidgid(user_ns, &init_user_ns, inode))
 			user_ns = user_ns->parent;
 
 		if (old != user_ns) {
diff --git a/include/linux/capability.h b/include/linux/capability.h
index 1e7fe311cabe..308d88096745 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -247,8 +247,12 @@  static inline bool ns_capable_setid(struct user_namespace *ns, int cap)
 	return true;
 }
 #endif /* CONFIG_MULTIUSER */
-extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode);
+extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns,
+					struct user_namespace *mnt_user_ns,
+					const struct inode *inode);
 extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
+extern bool capable_wrt_mapped_inode_uidgid(struct user_namespace *mnt_user_ns,
+					const struct inode *inode, int cap);
 extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
 extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns);
 static inline bool perfmon_capable(void)
diff --git a/kernel/capability.c b/kernel/capability.c
index de7eac903a2a..427776414487 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -484,12 +484,24 @@  EXPORT_SYMBOL(file_ns_capable);
  *
  * Return true if the inode uid and gid are within the namespace.
  */
-bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode)
+bool privileged_wrt_inode_uidgid(struct user_namespace *ns,
+				 struct user_namespace *mnt_user_ns,
+				 const struct inode *inode)
 {
-	return kuid_has_mapping(ns, inode->i_uid) &&
-		kgid_has_mapping(ns, inode->i_gid);
+	return kuid_has_mapping(ns, i_uid_into_mnt(mnt_user_ns, inode)) &&
+	       kgid_has_mapping(ns, i_gid_into_mnt(mnt_user_ns, inode));
 }
 
+bool capable_wrt_mapped_inode_uidgid(struct user_namespace *mnt_user_ns,
+				 const struct inode *inode, int cap)
+{
+	struct user_namespace *ns = current_user_ns();
+
+	return ns_capable(ns, cap) &&
+	       privileged_wrt_inode_uidgid(ns, mnt_user_ns, inode);
+}
+EXPORT_SYMBOL(capable_wrt_mapped_inode_uidgid);
+
 /**
  * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
  * @inode: The inode in question
@@ -501,9 +513,7 @@  bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *
  */
 bool capable_wrt_inode_uidgid(const struct inode *inode, int cap)
 {
-	struct user_namespace *ns = current_user_ns();
-
-	return ns_capable(ns, cap) && privileged_wrt_inode_uidgid(ns, inode);
+	return capable_wrt_mapped_inode_uidgid(&init_user_ns, inode, cap);
 }
 EXPORT_SYMBOL(capable_wrt_inode_uidgid);