diff mbox series

[v11,bpf-next,1/7] fs/xattr: bpf: Introduce security.bpf. xattr name prefix

Message ID 20250129205957.2457655-2-song@kernel.org (mailing list archive)
State New
Headers show
Series Enable writing xattr from BPF programs | expand

Commit Message

Song Liu Jan. 29, 2025, 8:59 p.m. UTC
Introduct new xattr name prefix security.bpf., and enable reading these
xattrs from bpf kfuncs bpf_get_[file|dentry]_xattr().

As we are on it, correct the comments for return value of
bpf_get_[file|dentry]_xattr(), i.e. return length the xattr value on
success.

Signed-off-by: Song Liu <song@kernel.org>
Acked-by: Christian Brauner <brauner@kernel.org>
Reviewed-by: Jan Kara <jack@suse.cz>
---
 fs/bpf_fs_kfuncs.c         | 19 ++++++++++++++-----
 include/uapi/linux/xattr.h |  4 ++++
 2 files changed, 18 insertions(+), 5 deletions(-)

Comments

Matt Bobrowski Jan. 30, 2025, 10:57 a.m. UTC | #1
On Wed, Jan 29, 2025 at 12:59:51PM -0800, Song Liu wrote:
> Introduct new xattr name prefix security.bpf., and enable reading these
> xattrs from bpf kfuncs bpf_get_[file|dentry]_xattr().
> 
> As we are on it, correct the comments for return value of
> bpf_get_[file|dentry]_xattr(), i.e. return length the xattr value on
> success.

Reviewed-by: Matt Bobrowski <mattbobrowski@google.com>

> Signed-off-by: Song Liu <song@kernel.org>
> Acked-by: Christian Brauner <brauner@kernel.org>
> Reviewed-by: Jan Kara <jack@suse.cz>
> ---
>  fs/bpf_fs_kfuncs.c         | 19 ++++++++++++++-----
>  include/uapi/linux/xattr.h |  4 ++++
>  2 files changed, 18 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
> index 3fe9f59ef867..8a65184c8c2c 100644
> --- a/fs/bpf_fs_kfuncs.c
> +++ b/fs/bpf_fs_kfuncs.c
> @@ -93,6 +93,11 @@ __bpf_kfunc int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz)
>  	return len;
>  }
>  
> +static bool match_security_bpf_prefix(const char *name__str)
> +{
> +	return !strncmp(name__str, XATTR_NAME_BPF_LSM, XATTR_NAME_BPF_LSM_LEN);
> +}

I think this can also just be match_xattr_prefix(const char
*name__str, const char *prefix, size_t len) such that we can do the
same checks for aribitrary xattr prefixes i.e. XATTR_USER_PREFIX,
XATTR_NAME_BPF_LSM.

>  /**
>   * bpf_get_dentry_xattr - get xattr of a dentry
>   * @dentry: dentry to get xattr from
> @@ -101,9 +106,10 @@ __bpf_kfunc int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz)
>   *
>   * Get xattr *name__str* of *dentry* and store the output in *value_ptr*.
>   *
> - * For security reasons, only *name__str* with prefix "user." is allowed.
> + * For security reasons, only *name__str* with prefix "user." or
      	  	   	    	 	     	  ^ prefixes
						  
> + * "security.bpf." is allowed.
                      ^ are

Out of curiosity, what is the security reasoning here? This isn't
obvious to me, and I'd like to understand this better. Is it simply
frowned upon to read arbitrary xattr values from the context of a BPF
LSM program, or has it got something to do with the backing xattr
handler that ends up being called once we step into __vfs_getxattr()
and such?  Also, just so that it's clear, I don't have anything
against this allow listing approach either, I just genuinely don't
understand the security implications.

> - * Return: 0 on success, a negative value on error.
> + * Return: length of the xattr value on success, a negative value on error.
>   */
>  __bpf_kfunc int bpf_get_dentry_xattr(struct dentry *dentry, const char *name__str,
>  				     struct bpf_dynptr *value_p)
> @@ -117,7 +123,9 @@ __bpf_kfunc int bpf_get_dentry_xattr(struct dentry *dentry, const char *name__st
>  	if (WARN_ON(!inode))
>  		return -EINVAL;
>  
> -	if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
> +	/* Allow reading xattr with user. and security.bpf. prefix */
> +	if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
> +	    !match_security_bpf_prefix(name__str))

I think it would be cleaner to have single function
i.e. is_allowed_xattr_prefix(const char *name__str) which simply
checks all the allowed xattr prefixes that can be read by this BPF
kfunc.

>  		return -EPERM;
>  
>  	value_len = __bpf_dynptr_size(value_ptr);
> @@ -139,9 +147,10 @@ __bpf_kfunc int bpf_get_dentry_xattr(struct dentry *dentry, const char *name__st
>   *
>   * Get xattr *name__str* of *file* and store the output in *value_ptr*.
>   *
> - * For security reasons, only *name__str* with prefix "user." is allowed.
> + * For security reasons, only *name__str* with prefix "user." or
      	  	   	    	 	     	  ^ prefixes

> + * "security.bpf." is allowed.
      		      ^ are

> - * Return: 0 on success, a negative value on error.
> + * Return: length of the xattr value on success, a negative value on error.
>   */
>  __bpf_kfunc int bpf_get_file_xattr(struct file *file, const char *name__str,
>  				   struct bpf_dynptr *value_p)
> diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
> index 9854f9cff3c6..c7c85bb504ba 100644
> --- a/include/uapi/linux/xattr.h
> +++ b/include/uapi/linux/xattr.h
> @@ -83,6 +83,10 @@ struct xattr_args {
>  #define XATTR_CAPS_SUFFIX "capability"
>  #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
>  
> +#define XATTR_BPF_LSM_SUFFIX "bpf."
> +#define XATTR_NAME_BPF_LSM (XATTR_SECURITY_PREFIX XATTR_BPF_LSM_SUFFIX)
> +#define XATTR_NAME_BPF_LSM_LEN (sizeof(XATTR_NAME_BPF_LSM) - 1)
> +
>  #define XATTR_POSIX_ACL_ACCESS  "posix_acl_access"
>  #define XATTR_NAME_POSIX_ACL_ACCESS XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_ACCESS
>  #define XATTR_POSIX_ACL_DEFAULT  "posix_acl_default"
> -- 
> 2.43.5
>
Christian Brauner Jan. 30, 2025, 3:20 p.m. UTC | #2
On Thu, Jan 30, 2025 at 10:57:35AM +0000, Matt Bobrowski wrote:
> On Wed, Jan 29, 2025 at 12:59:51PM -0800, Song Liu wrote:
> > Introduct new xattr name prefix security.bpf., and enable reading these
> > xattrs from bpf kfuncs bpf_get_[file|dentry]_xattr().
> > 
> > As we are on it, correct the comments for return value of
> > bpf_get_[file|dentry]_xattr(), i.e. return length the xattr value on
> > success.
> 
> Reviewed-by: Matt Bobrowski <mattbobrowski@google.com>
> 
> > Signed-off-by: Song Liu <song@kernel.org>
> > Acked-by: Christian Brauner <brauner@kernel.org>
> > Reviewed-by: Jan Kara <jack@suse.cz>
> > ---
> >  fs/bpf_fs_kfuncs.c         | 19 ++++++++++++++-----
> >  include/uapi/linux/xattr.h |  4 ++++
> >  2 files changed, 18 insertions(+), 5 deletions(-)
> > 
> > diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
> > index 3fe9f59ef867..8a65184c8c2c 100644
> > --- a/fs/bpf_fs_kfuncs.c
> > +++ b/fs/bpf_fs_kfuncs.c
> > @@ -93,6 +93,11 @@ __bpf_kfunc int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz)
> >  	return len;
> >  }
> >  
> > +static bool match_security_bpf_prefix(const char *name__str)
> > +{
> > +	return !strncmp(name__str, XATTR_NAME_BPF_LSM, XATTR_NAME_BPF_LSM_LEN);
> > +}
> 
> I think this can also just be match_xattr_prefix(const char
> *name__str, const char *prefix, size_t len) such that we can do the
> same checks for aribitrary xattr prefixes i.e. XATTR_USER_PREFIX,
> XATTR_NAME_BPF_LSM.
> 
> >  /**
> >   * bpf_get_dentry_xattr - get xattr of a dentry
> >   * @dentry: dentry to get xattr from
> > @@ -101,9 +106,10 @@ __bpf_kfunc int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz)
> >   *
> >   * Get xattr *name__str* of *dentry* and store the output in *value_ptr*.
> >   *
> > - * For security reasons, only *name__str* with prefix "user." is allowed.
> > + * For security reasons, only *name__str* with prefix "user." or
>       	  	   	    	 	     	  ^ prefixes
> 						  
> > + * "security.bpf." is allowed.
>                       ^ are
> 
> Out of curiosity, what is the security reasoning here? This isn't
> obvious to me, and I'd like to understand this better. Is it simply
> frowned upon to read arbitrary xattr values from the context of a BPF
> LSM program, or has it got something to do with the backing xattr
> handler that ends up being called once we step into __vfs_getxattr()
> and such?  Also, just so that it's clear, I don't have anything
> against this allow listing approach either, I just genuinely don't
> understand the security implications.

I've explained this at lenghts in multiple threads. The gist is various
xattrs require you to have access to properties that are carried by
objects you don't have access to (e.g., the mount) or can't guarantee
that you're in the correct context and interpreting those xattrs without
this information is either meaningless or actively wrong.
Song Liu Jan. 30, 2025, 6:08 p.m. UTC | #3
Hi Matt,

> On Jan 30, 2025, at 2:57 AM, Matt Bobrowski <mattbobrowski@google.com> wrote:
> 
> On Wed, Jan 29, 2025 at 12:59:51PM -0800, Song Liu wrote:
>> Introduct new xattr name prefix security.bpf., and enable reading these
>> xattrs from bpf kfuncs bpf_get_[file|dentry]_xattr().
>> 
>> As we are on it, correct the comments for return value of
>> bpf_get_[file|dentry]_xattr(), i.e. return length the xattr value on
>> success.
> 
> Reviewed-by: Matt Bobrowski <mattbobrowski@google.com>

Thanks for the review!

[...]
> 
>> - * Return: 0 on success, a negative value on error.
>> + * Return: length of the xattr value on success, a negative value on error.
>>  */
>> __bpf_kfunc int bpf_get_dentry_xattr(struct dentry *dentry, const char *name__str,
>>     struct bpf_dynptr *value_p)
>> @@ -117,7 +123,9 @@ __bpf_kfunc int bpf_get_dentry_xattr(struct dentry *dentry, const char *name__st
>> if (WARN_ON(!inode))
>> return -EINVAL;
>> 
>> - if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
>> + /* Allow reading xattr with user. and security.bpf. prefix */
>> + if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
>> +    !match_security_bpf_prefix(name__str))
> 
> I think it would be cleaner to have single function
> i.e. is_allowed_xattr_prefix(const char *name__str) which simply
> checks all the allowed xattr prefixes that can be read by this BPF
> kfunc.

Sure, we can add bpf_xattr_read_permission() which pairs with 
bpf_xattr_write_permission(). 

Thanks,
Song

> 
>> return -EPERM;
>> 
>> value_len = __bpf_dynptr_size(value_ptr);
>> @@ -139,9 +147,10 @@ __bpf_kfunc int bpf_get_dentry_xattr(struct dentry *dentry, const char *name__st
>>  *
>>  * Get xattr *name__str* of *file* and store the output in *value_ptr*.
>>  *
>> - * For security reasons, only *name__str* with prefix "user." is allowed.
>> + * For security reasons, only *name__str* with prefix "user." or
>                      ^ prefixes
> 
>> + * "security.bpf." is allowed.
>            ^ are
> 
>> - * Return: 0 on success, a negative value on error.
>> + * Return: length of the xattr value on success, a negative value on error.
>>  */
>> __bpf_kfunc int bpf_get_file_xattr(struct file *file, const char *name__str,
>>   struct bpf_dynptr *value_p)
>> diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
>> index 9854f9cff3c6..c7c85bb504ba 100644
>> --- a/include/uapi/linux/xattr.h
>> +++ b/include/uapi/linux/xattr.h
>> @@ -83,6 +83,10 @@ struct xattr_args {
>> #define XATTR_CAPS_SUFFIX "capability"
>> #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
>> 
>> +#define XATTR_BPF_LSM_SUFFIX "bpf."
>> +#define XATTR_NAME_BPF_LSM (XATTR_SECURITY_PREFIX XATTR_BPF_LSM_SUFFIX)
>> +#define XATTR_NAME_BPF_LSM_LEN (sizeof(XATTR_NAME_BPF_LSM) - 1)
>> +
>> #define XATTR_POSIX_ACL_ACCESS  "posix_acl_access"
>> #define XATTR_NAME_POSIX_ACL_ACCESS XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_ACCESS
>> #define XATTR_POSIX_ACL_DEFAULT  "posix_acl_default"
>> -- 
>> 2.43.5
>>
Matt Bobrowski Jan. 31, 2025, 8:32 a.m. UTC | #4
On Thu, Jan 30, 2025 at 04:20:04PM +0100, Christian Brauner wrote:
> On Thu, Jan 30, 2025 at 10:57:35AM +0000, Matt Bobrowski wrote:
> > On Wed, Jan 29, 2025 at 12:59:51PM -0800, Song Liu wrote:
> > > Introduct new xattr name prefix security.bpf., and enable reading these
> > > xattrs from bpf kfuncs bpf_get_[file|dentry]_xattr().
> > > 
> > > As we are on it, correct the comments for return value of
> > > bpf_get_[file|dentry]_xattr(), i.e. return length the xattr value on
> > > success.
> > 
> > Reviewed-by: Matt Bobrowski <mattbobrowski@google.com>
> > 
> > > Signed-off-by: Song Liu <song@kernel.org>
> > > Acked-by: Christian Brauner <brauner@kernel.org>
> > > Reviewed-by: Jan Kara <jack@suse.cz>
> > > ---
> > >  fs/bpf_fs_kfuncs.c         | 19 ++++++++++++++-----
> > >  include/uapi/linux/xattr.h |  4 ++++
> > >  2 files changed, 18 insertions(+), 5 deletions(-)
> > > 
> > > diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
> > > index 3fe9f59ef867..8a65184c8c2c 100644
> > > --- a/fs/bpf_fs_kfuncs.c
> > > +++ b/fs/bpf_fs_kfuncs.c
> > > @@ -93,6 +93,11 @@ __bpf_kfunc int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz)
> > >  	return len;
> > >  }
> > >  
> > > +static bool match_security_bpf_prefix(const char *name__str)
> > > +{
> > > +	return !strncmp(name__str, XATTR_NAME_BPF_LSM, XATTR_NAME_BPF_LSM_LEN);
> > > +}
> > 
> > I think this can also just be match_xattr_prefix(const char
> > *name__str, const char *prefix, size_t len) such that we can do the
> > same checks for aribitrary xattr prefixes i.e. XATTR_USER_PREFIX,
> > XATTR_NAME_BPF_LSM.
> > 
> > >  /**
> > >   * bpf_get_dentry_xattr - get xattr of a dentry
> > >   * @dentry: dentry to get xattr from
> > > @@ -101,9 +106,10 @@ __bpf_kfunc int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz)
> > >   *
> > >   * Get xattr *name__str* of *dentry* and store the output in *value_ptr*.
> > >   *
> > > - * For security reasons, only *name__str* with prefix "user." is allowed.
> > > + * For security reasons, only *name__str* with prefix "user." or
> >       	  	   	    	 	     	  ^ prefixes
> > 						  
> > > + * "security.bpf." is allowed.
> >                       ^ are
> > 
> > Out of curiosity, what is the security reasoning here? This isn't
> > obvious to me, and I'd like to understand this better. Is it simply
> > frowned upon to read arbitrary xattr values from the context of a BPF
> > LSM program, or has it got something to do with the backing xattr
> > handler that ends up being called once we step into __vfs_getxattr()
> > and such?  Also, just so that it's clear, I don't have anything
> > against this allow listing approach either, I just genuinely don't
> > understand the security implications.
> 
> I've explained this at lenghts in multiple threads. The gist is various
> xattrs require you to have access to properties that are carried by
> objects you don't have access to (e.g., the mount) or can't guarantee
> that you're in the correct context and interpreting those xattrs without
> this information is either meaningless or actively wrong.

Oh, right, I see. Thank you Christian!
diff mbox series

Patch

diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
index 3fe9f59ef867..8a65184c8c2c 100644
--- a/fs/bpf_fs_kfuncs.c
+++ b/fs/bpf_fs_kfuncs.c
@@ -93,6 +93,11 @@  __bpf_kfunc int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz)
 	return len;
 }
 
+static bool match_security_bpf_prefix(const char *name__str)
+{
+	return !strncmp(name__str, XATTR_NAME_BPF_LSM, XATTR_NAME_BPF_LSM_LEN);
+}
+
 /**
  * bpf_get_dentry_xattr - get xattr of a dentry
  * @dentry: dentry to get xattr from
@@ -101,9 +106,10 @@  __bpf_kfunc int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz)
  *
  * Get xattr *name__str* of *dentry* and store the output in *value_ptr*.
  *
- * For security reasons, only *name__str* with prefix "user." is allowed.
+ * For security reasons, only *name__str* with prefix "user." or
+ * "security.bpf." is allowed.
  *
- * Return: 0 on success, a negative value on error.
+ * Return: length of the xattr value on success, a negative value on error.
  */
 __bpf_kfunc int bpf_get_dentry_xattr(struct dentry *dentry, const char *name__str,
 				     struct bpf_dynptr *value_p)
@@ -117,7 +123,9 @@  __bpf_kfunc int bpf_get_dentry_xattr(struct dentry *dentry, const char *name__st
 	if (WARN_ON(!inode))
 		return -EINVAL;
 
-	if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
+	/* Allow reading xattr with user. and security.bpf. prefix */
+	if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
+	    !match_security_bpf_prefix(name__str))
 		return -EPERM;
 
 	value_len = __bpf_dynptr_size(value_ptr);
@@ -139,9 +147,10 @@  __bpf_kfunc int bpf_get_dentry_xattr(struct dentry *dentry, const char *name__st
  *
  * Get xattr *name__str* of *file* and store the output in *value_ptr*.
  *
- * For security reasons, only *name__str* with prefix "user." is allowed.
+ * For security reasons, only *name__str* with prefix "user." or
+ * "security.bpf." is allowed.
  *
- * Return: 0 on success, a negative value on error.
+ * Return: length of the xattr value on success, a negative value on error.
  */
 __bpf_kfunc int bpf_get_file_xattr(struct file *file, const char *name__str,
 				   struct bpf_dynptr *value_p)
diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index 9854f9cff3c6..c7c85bb504ba 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -83,6 +83,10 @@  struct xattr_args {
 #define XATTR_CAPS_SUFFIX "capability"
 #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
 
+#define XATTR_BPF_LSM_SUFFIX "bpf."
+#define XATTR_NAME_BPF_LSM (XATTR_SECURITY_PREFIX XATTR_BPF_LSM_SUFFIX)
+#define XATTR_NAME_BPF_LSM_LEN (sizeof(XATTR_NAME_BPF_LSM) - 1)
+
 #define XATTR_POSIX_ACL_ACCESS  "posix_acl_access"
 #define XATTR_NAME_POSIX_ACL_ACCESS XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_ACCESS
 #define XATTR_POSIX_ACL_DEFAULT  "posix_acl_default"