Commit 11ab4cd5 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'lsm-pr-20240715' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/lsm

Pull lsm updates from Paul Moore:
 "Two LSM patches focused on cleaning up the inode xattr capability
  handling"

* tag 'lsm-pr-20240715' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/lsm:
  selinux,smack: remove the capability checks in the removexattr hooks
  lsm: fixup the inode xattr capability handling
parents dad8d1a3 dd44477e
...@@ -144,6 +144,7 @@ LSM_HOOK(int, 0, inode_setattr, struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -144,6 +144,7 @@ LSM_HOOK(int, 0, inode_setattr, struct mnt_idmap *idmap, struct dentry *dentry,
LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, struct mnt_idmap *idmap, LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, struct mnt_idmap *idmap,
struct dentry *dentry, int ia_valid) struct dentry *dentry, int ia_valid)
LSM_HOOK(int, 0, inode_getattr, const struct path *path) LSM_HOOK(int, 0, inode_getattr, const struct path *path)
LSM_HOOK(int, 0, inode_xattr_skipcap, const char *name)
LSM_HOOK(int, 0, inode_setxattr, struct mnt_idmap *idmap, LSM_HOOK(int, 0, inode_setxattr, struct mnt_idmap *idmap,
struct dentry *dentry, const char *name, const void *value, struct dentry *dentry, const char *name, const void *value,
size_t size, int flags) size_t size, int flags)
......
...@@ -2278,7 +2278,20 @@ int security_inode_getattr(const struct path *path) ...@@ -2278,7 +2278,20 @@ int security_inode_getattr(const struct path *path)
* @size: size of xattr value * @size: size of xattr value
* @flags: flags * @flags: flags
* *
* Check permission before setting the extended attributes. * This hook performs the desired permission checks before setting the extended
* attributes (xattrs) on @dentry. It is important to note that we have some
* additional logic before the main LSM implementation calls to detect if we
* need to perform an additional capability check at the LSM layer.
*
* Normally we enforce a capability check prior to executing the various LSM
* hook implementations, but if a LSM wants to avoid this capability check,
* it can register a 'inode_xattr_skipcap' hook and return a value of 1 for
* xattrs that it wants to avoid the capability check, leaving the LSM fully
* responsible for enforcing the access control for the specific xattr. If all
* of the enabled LSMs refrain from registering a 'inode_xattr_skipcap' hook,
* or return a 0 (the default return value), the capability check is still
* performed. If no 'inode_xattr_skipcap' hooks are registered the capability
* check is performed.
* *
* Return: Returns 0 if permission is granted. * Return: Returns 0 if permission is granted.
*/ */
...@@ -2286,20 +2299,20 @@ int security_inode_setxattr(struct mnt_idmap *idmap, ...@@ -2286,20 +2299,20 @@ int security_inode_setxattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name, struct dentry *dentry, const char *name,
const void *value, size_t size, int flags) const void *value, size_t size, int flags)
{ {
int ret; int rc;
if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0; return 0;
/*
* SELinux and Smack integrate the cap call,
* so assume that all LSMs supplying this call do so.
*/
ret = call_int_hook(inode_setxattr, idmap, dentry, name, value, size,
flags);
if (ret == 1) /* enforce the capability checks at the lsm layer, if needed */
ret = cap_inode_setxattr(dentry, name, value, size, flags); if (!call_int_hook(inode_xattr_skipcap, name)) {
return ret; rc = cap_inode_setxattr(dentry, name, value, size, flags);
if (rc)
return rc;
}
return call_int_hook(inode_setxattr, idmap, dentry, name, value, size,
flags);
} }
/** /**
...@@ -2452,26 +2465,39 @@ int security_inode_listxattr(struct dentry *dentry) ...@@ -2452,26 +2465,39 @@ int security_inode_listxattr(struct dentry *dentry)
* @dentry: file * @dentry: file
* @name: xattr name * @name: xattr name
* *
* Check permission before removing the extended attribute identified by @name * This hook performs the desired permission checks before setting the extended
* for @dentry. * attributes (xattrs) on @dentry. It is important to note that we have some
* additional logic before the main LSM implementation calls to detect if we
* need to perform an additional capability check at the LSM layer.
*
* Normally we enforce a capability check prior to executing the various LSM
* hook implementations, but if a LSM wants to avoid this capability check,
* it can register a 'inode_xattr_skipcap' hook and return a value of 1 for
* xattrs that it wants to avoid the capability check, leaving the LSM fully
* responsible for enforcing the access control for the specific xattr. If all
* of the enabled LSMs refrain from registering a 'inode_xattr_skipcap' hook,
* or return a 0 (the default return value), the capability check is still
* performed. If no 'inode_xattr_skipcap' hooks are registered the capability
* check is performed.
* *
* Return: Returns 0 if permission is granted. * Return: Returns 0 if permission is granted.
*/ */
int security_inode_removexattr(struct mnt_idmap *idmap, int security_inode_removexattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name) struct dentry *dentry, const char *name)
{ {
int ret; int rc;
if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0; return 0;
/*
* SELinux and Smack integrate the cap call, /* enforce the capability checks at the lsm layer, if needed */
* so assume that all LSMs supplying this call do so. if (!call_int_hook(inode_xattr_skipcap, name)) {
*/ rc = cap_inode_removexattr(idmap, dentry, name);
ret = call_int_hook(inode_removexattr, idmap, dentry, name); if (rc)
if (ret == 1) return rc;
ret = cap_inode_removexattr(idmap, dentry, name); }
return ret;
return call_int_hook(inode_removexattr, idmap, dentry, name);
} }
/** /**
......
...@@ -3177,6 +3177,23 @@ static bool has_cap_mac_admin(bool audit) ...@@ -3177,6 +3177,23 @@ static bool has_cap_mac_admin(bool audit)
return true; return true;
} }
/**
* selinux_inode_xattr_skipcap - Skip the xattr capability checks?
* @name: name of the xattr
*
* Returns 1 to indicate that SELinux "owns" the access control rights to xattrs
* named @name; the LSM layer should avoid enforcing any traditional
* capability based access controls on this xattr. Returns 0 to indicate that
* SELinux does not "own" the access control rights to xattrs named @name and is
* deferring to the LSM layer for further access controls, including capability
* based controls.
*/
static int selinux_inode_xattr_skipcap(const char *name)
{
/* require capability check if not a selinux xattr */
return !strcmp(name, XATTR_NAME_SELINUX);
}
static int selinux_inode_setxattr(struct mnt_idmap *idmap, static int selinux_inode_setxattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name, struct dentry *dentry, const char *name,
const void *value, size_t size, int flags) const void *value, size_t size, int flags)
...@@ -3188,15 +3205,9 @@ static int selinux_inode_setxattr(struct mnt_idmap *idmap, ...@@ -3188,15 +3205,9 @@ static int selinux_inode_setxattr(struct mnt_idmap *idmap,
u32 newsid, sid = current_sid(); u32 newsid, sid = current_sid();
int rc = 0; int rc = 0;
if (strcmp(name, XATTR_NAME_SELINUX)) { /* if not a selinux xattr, only check the ordinary setattr perm */
rc = cap_inode_setxattr(dentry, name, value, size, flags); if (strcmp(name, XATTR_NAME_SELINUX))
if (rc)
return rc;
/* Not an attribute we recognize, so just check the
ordinary setattr permission. */
return dentry_has_perm(current_cred(), dentry, FILE__SETATTR); return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
}
if (!selinux_initialized()) if (!selinux_initialized())
return (inode_owner_or_capable(idmap, inode) ? 0 : -EPERM); return (inode_owner_or_capable(idmap, inode) ? 0 : -EPERM);
...@@ -3345,15 +3356,9 @@ static int selinux_inode_listxattr(struct dentry *dentry) ...@@ -3345,15 +3356,9 @@ static int selinux_inode_listxattr(struct dentry *dentry)
static int selinux_inode_removexattr(struct mnt_idmap *idmap, static int selinux_inode_removexattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name) struct dentry *dentry, const char *name)
{ {
if (strcmp(name, XATTR_NAME_SELINUX)) { /* if not a selinux xattr, only check the ordinary setattr perm */
int rc = cap_inode_removexattr(idmap, dentry, name); if (strcmp(name, XATTR_NAME_SELINUX))
if (rc)
return rc;
/* Not an attribute we recognize, so just check the
ordinary setattr permission. */
return dentry_has_perm(current_cred(), dentry, FILE__SETATTR); return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
}
if (!selinux_initialized()) if (!selinux_initialized())
return 0; return 0;
...@@ -7175,6 +7180,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { ...@@ -7175,6 +7180,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_permission, selinux_inode_permission), LSM_HOOK_INIT(inode_permission, selinux_inode_permission),
LSM_HOOK_INIT(inode_setattr, selinux_inode_setattr), LSM_HOOK_INIT(inode_setattr, selinux_inode_setattr),
LSM_HOOK_INIT(inode_getattr, selinux_inode_getattr), LSM_HOOK_INIT(inode_getattr, selinux_inode_getattr),
LSM_HOOK_INIT(inode_xattr_skipcap, selinux_inode_xattr_skipcap),
LSM_HOOK_INIT(inode_setxattr, selinux_inode_setxattr), LSM_HOOK_INIT(inode_setxattr, selinux_inode_setxattr),
LSM_HOOK_INIT(inode_post_setxattr, selinux_inode_post_setxattr), LSM_HOOK_INIT(inode_post_setxattr, selinux_inode_post_setxattr),
LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr), LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr),
......
...@@ -1282,6 +1282,33 @@ static int smack_inode_getattr(const struct path *path) ...@@ -1282,6 +1282,33 @@ static int smack_inode_getattr(const struct path *path)
return rc; return rc;
} }
/**
* smack_inode_xattr_skipcap - Skip the xattr capability checks?
* @name: name of the xattr
*
* Returns 1 to indicate that Smack "owns" the access control rights to xattrs
* named @name; the LSM layer should avoid enforcing any traditional
* capability based access controls on this xattr. Returns 0 to indicate that
* Smack does not "own" the access control rights to xattrs named @name and is
* deferring to the LSM layer for further access controls, including capability
* based controls.
*/
static int smack_inode_xattr_skipcap(const char *name)
{
if (strncmp(name, XATTR_SMACK_SUFFIX, strlen(XATTR_SMACK_SUFFIX)))
return 0;
if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
strcmp(name, XATTR_NAME_SMACKMMAP) == 0 ||
strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0)
return 1;
return 0;
}
/** /**
* smack_inode_setxattr - Smack check for setting xattrs * smack_inode_setxattr - Smack check for setting xattrs
* @idmap: idmap of the mount * @idmap: idmap of the mount
...@@ -1325,8 +1352,7 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap, ...@@ -1325,8 +1352,7 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
size != TRANS_TRUE_SIZE || size != TRANS_TRUE_SIZE ||
strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0) strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
rc = -EINVAL; rc = -EINVAL;
} else }
rc = cap_inode_setxattr(dentry, name, value, size, flags);
if (check_priv && !smack_privileged(CAP_MAC_ADMIN)) if (check_priv && !smack_privileged(CAP_MAC_ADMIN))
rc = -EPERM; rc = -EPERM;
...@@ -1435,8 +1461,7 @@ static int smack_inode_removexattr(struct mnt_idmap *idmap, ...@@ -1435,8 +1461,7 @@ static int smack_inode_removexattr(struct mnt_idmap *idmap,
strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
if (!smack_privileged(CAP_MAC_ADMIN)) if (!smack_privileged(CAP_MAC_ADMIN))
rc = -EPERM; rc = -EPERM;
} else }
rc = cap_inode_removexattr(idmap, dentry, name);
if (rc != 0) if (rc != 0)
return rc; return rc;
...@@ -5053,6 +5078,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = { ...@@ -5053,6 +5078,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_permission, smack_inode_permission), LSM_HOOK_INIT(inode_permission, smack_inode_permission),
LSM_HOOK_INIT(inode_setattr, smack_inode_setattr), LSM_HOOK_INIT(inode_setattr, smack_inode_setattr),
LSM_HOOK_INIT(inode_getattr, smack_inode_getattr), LSM_HOOK_INIT(inode_getattr, smack_inode_getattr),
LSM_HOOK_INIT(inode_xattr_skipcap, smack_inode_xattr_skipcap),
LSM_HOOK_INIT(inode_setxattr, smack_inode_setxattr), LSM_HOOK_INIT(inode_setxattr, smack_inode_setxattr),
LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr), LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr),
LSM_HOOK_INIT(inode_getxattr, smack_inode_getxattr), LSM_HOOK_INIT(inode_getxattr, smack_inode_getxattr),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment