Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit 61df7b8

Browse files
committed
lsm: fixup the inode xattr capability handling
The current security_inode_setxattr() and security_inode_removexattr() hooks rely on individual LSMs to either call into the associated capability hooks (cap_inode_setxattr() or cap_inode_removexattr()), or return a magic value of 1 to indicate that the LSM layer itself should perform the capability checks. Unfortunately, with the default return value for these LSM hooks being 0, an individual LSM hook returning a 1 will cause the LSM hook processing to exit early, potentially skipping a LSM. Thankfully, with the exception of the BPF LSM, none of the LSMs which currently register inode xattr hooks should end up returning a value of 1, and in the BPF LSM case, with the BPF LSM hooks executing last there should be no real harm in stopping processing of the LSM hooks. However, the reliance on the individual LSMs to either call the capability hooks themselves, or signal the LSM with a return value of 1, is fragile and relies on a specific set of LSMs being enabled. This patch is an effort to resolve, or minimize, these issues. Before we discuss the solution, there are a few observations and considerations that we need to take into account: * BPF LSM registers an implementation for every LSM hook, and that implementation simply returns the hook's default return value, a 0 in this case. We want to ensure that the default BPF LSM behavior results in the capability checks being called. * SELinux and Smack do not expect the traditional capability checks to be applied to the xattrs that they "own". * SELinux and Smack are currently written in such a way that the xattr capability checks happen before any additional LSM specific access control checks. SELinux does apply SELinux specific access controls to all xattrs, even those not "owned" by SELinux. * IMA and EVM also register xattr hooks but assume that the LSM layer and specific LSMs have already authorized the basic xattr operation. In order to ensure we perform the capability based access controls before the individual LSM access controls, perform only one capability access control check for each operation, and clarify the logic around applying the capability controls, we need a mechanism to determine if any of the enabled LSMs "own" a particular xattr and want to take responsibility for controlling access to that xattr. The solution in this patch is to create a new LSM hook, 'inode_xattr_skipcap', that is not exported to the rest of the kernel via a security_XXX() function, but is used by the LSM layer to determine if a LSM wants to control access to a given xattr and avoid the traditional capability controls. Registering an inode_xattr_skipcap hook is optional, if a LSM declines to register an implementation, or uses an implementation that simply returns the default value (0), there is no effect as the LSM continues to enforce the capability based controls (unless another LSM takes ownership of the xattr). If none of the LSMs signal that the capability checks should be skipped, the capability check is performed and if access is granted the individual LSM xattr access control hooks are executed, keeping with the DAC-before-LSM convention. Cc: stable@vger.kernel.org Acked-by: Casey Schaufler <casey@schaufler-ca.com> Signed-off-by: Paul Moore <paul@paul-moore.com>
1 parent 1613e60 commit 61df7b8

File tree

4 files changed

+98
-32
lines changed

4 files changed

+98
-32
lines changed

include/linux/lsm_hook_defs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ LSM_HOOK(int, 0, inode_setattr, struct mnt_idmap *idmap, struct dentry *dentry,
144144
LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, struct mnt_idmap *idmap,
145145
struct dentry *dentry, int ia_valid)
146146
LSM_HOOK(int, 0, inode_getattr, const struct path *path)
147+
LSM_HOOK(int, 0, inode_xattr_skipcap, const char *name)
147148
LSM_HOOK(int, 0, inode_setxattr, struct mnt_idmap *idmap,
148149
struct dentry *dentry, const char *name, const void *value,
149150
size_t size, int flags)

security/security.c

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,28 +2278,41 @@ int security_inode_getattr(const struct path *path)
22782278
* @size: size of xattr value
22792279
* @flags: flags
22802280
*
2281-
* Check permission before setting the extended attributes.
2281+
* This hook performs the desired permission checks before setting the extended
2282+
* attributes (xattrs) on @dentry. It is important to note that we have some
2283+
* additional logic before the main LSM implementation calls to detect if we
2284+
* need to perform an additional capability check at the LSM layer.
2285+
*
2286+
* Normally we enforce a capability check prior to executing the various LSM
2287+
* hook implementations, but if a LSM wants to avoid this capability check,
2288+
* it can register a 'inode_xattr_skipcap' hook and return a value of 1 for
2289+
* xattrs that it wants to avoid the capability check, leaving the LSM fully
2290+
* responsible for enforcing the access control for the specific xattr. If all
2291+
* of the enabled LSMs refrain from registering a 'inode_xattr_skipcap' hook,
2292+
* or return a 0 (the default return value), the capability check is still
2293+
* performed. If no 'inode_xattr_skipcap' hooks are registered the capability
2294+
* check is performed.
22822295
*
22832296
* Return: Returns 0 if permission is granted.
22842297
*/
22852298
int security_inode_setxattr(struct mnt_idmap *idmap,
22862299
struct dentry *dentry, const char *name,
22872300
const void *value, size_t size, int flags)
22882301
{
2289-
int ret;
2302+
int rc;
22902303

22912304
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
22922305
return 0;
2293-
/*
2294-
* SELinux and Smack integrate the cap call,
2295-
* so assume that all LSMs supplying this call do so.
2296-
*/
2297-
ret = call_int_hook(inode_setxattr, idmap, dentry, name, value, size,
2298-
flags);
22992306

2300-
if (ret == 1)
2301-
ret = cap_inode_setxattr(dentry, name, value, size, flags);
2302-
return ret;
2307+
/* enforce the capability checks at the lsm layer, if needed */
2308+
if (!call_int_hook(inode_xattr_skipcap, name)) {
2309+
rc = cap_inode_setxattr(dentry, name, value, size, flags);
2310+
if (rc)
2311+
return rc;
2312+
}
2313+
2314+
return call_int_hook(inode_setxattr, idmap, dentry, name, value, size,
2315+
flags);
23032316
}
23042317

23052318
/**
@@ -2452,26 +2465,39 @@ int security_inode_listxattr(struct dentry *dentry)
24522465
* @dentry: file
24532466
* @name: xattr name
24542467
*
2455-
* Check permission before removing the extended attribute identified by @name
2456-
* for @dentry.
2468+
* This hook performs the desired permission checks before setting the extended
2469+
* attributes (xattrs) on @dentry. It is important to note that we have some
2470+
* additional logic before the main LSM implementation calls to detect if we
2471+
* need to perform an additional capability check at the LSM layer.
2472+
*
2473+
* Normally we enforce a capability check prior to executing the various LSM
2474+
* hook implementations, but if a LSM wants to avoid this capability check,
2475+
* it can register a 'inode_xattr_skipcap' hook and return a value of 1 for
2476+
* xattrs that it wants to avoid the capability check, leaving the LSM fully
2477+
* responsible for enforcing the access control for the specific xattr. If all
2478+
* of the enabled LSMs refrain from registering a 'inode_xattr_skipcap' hook,
2479+
* or return a 0 (the default return value), the capability check is still
2480+
* performed. If no 'inode_xattr_skipcap' hooks are registered the capability
2481+
* check is performed.
24572482
*
24582483
* Return: Returns 0 if permission is granted.
24592484
*/
24602485
int security_inode_removexattr(struct mnt_idmap *idmap,
24612486
struct dentry *dentry, const char *name)
24622487
{
2463-
int ret;
2488+
int rc;
24642489

24652490
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
24662491
return 0;
2467-
/*
2468-
* SELinux and Smack integrate the cap call,
2469-
* so assume that all LSMs supplying this call do so.
2470-
*/
2471-
ret = call_int_hook(inode_removexattr, idmap, dentry, name);
2472-
if (ret == 1)
2473-
ret = cap_inode_removexattr(idmap, dentry, name);
2474-
return ret;
2492+
2493+
/* enforce the capability checks at the lsm layer, if needed */
2494+
if (!call_int_hook(inode_xattr_skipcap, name)) {
2495+
rc = cap_inode_removexattr(idmap, dentry, name);
2496+
if (rc)
2497+
return rc;
2498+
}
2499+
2500+
return call_int_hook(inode_removexattr, idmap, dentry, name);
24752501
}
24762502

24772503
/**

security/selinux/hooks.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3177,6 +3177,23 @@ static bool has_cap_mac_admin(bool audit)
31773177
return true;
31783178
}
31793179

3180+
/**
3181+
* selinux_inode_xattr_skipcap - Skip the xattr capability checks?
3182+
* @name: name of the xattr
3183+
*
3184+
* Returns 1 to indicate that SELinux "owns" the access control rights to xattrs
3185+
* named @name; the LSM layer should avoid enforcing any traditional
3186+
* capability based access controls on this xattr. Returns 0 to indicate that
3187+
* SELinux does not "own" the access control rights to xattrs named @name and is
3188+
* deferring to the LSM layer for further access controls, including capability
3189+
* based controls.
3190+
*/
3191+
static int selinux_inode_xattr_skipcap(const char *name)
3192+
{
3193+
/* require capability check if not a selinux xattr */
3194+
return !strcmp(name, XATTR_NAME_SELINUX);
3195+
}
3196+
31803197
static int selinux_inode_setxattr(struct mnt_idmap *idmap,
31813198
struct dentry *dentry, const char *name,
31823199
const void *value, size_t size, int flags)
@@ -3188,15 +3205,9 @@ static int selinux_inode_setxattr(struct mnt_idmap *idmap,
31883205
u32 newsid, sid = current_sid();
31893206
int rc = 0;
31903207

3191-
if (strcmp(name, XATTR_NAME_SELINUX)) {
3192-
rc = cap_inode_setxattr(dentry, name, value, size, flags);
3193-
if (rc)
3194-
return rc;
3195-
3196-
/* Not an attribute we recognize, so just check the
3197-
ordinary setattr permission. */
3208+
/* if not a selinux xattr, only check the ordinary setattr perm */
3209+
if (strcmp(name, XATTR_NAME_SELINUX))
31983210
return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
3199-
}
32003211

32013212
if (!selinux_initialized())
32023213
return (inode_owner_or_capable(idmap, inode) ? 0 : -EPERM);
@@ -7175,6 +7186,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
71757186
LSM_HOOK_INIT(inode_permission, selinux_inode_permission),
71767187
LSM_HOOK_INIT(inode_setattr, selinux_inode_setattr),
71777188
LSM_HOOK_INIT(inode_getattr, selinux_inode_getattr),
7189+
LSM_HOOK_INIT(inode_xattr_skipcap, selinux_inode_xattr_skipcap),
71787190
LSM_HOOK_INIT(inode_setxattr, selinux_inode_setxattr),
71797191
LSM_HOOK_INIT(inode_post_setxattr, selinux_inode_post_setxattr),
71807192
LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr),

security/smack/smack_lsm.c

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,33 @@ static int smack_inode_getattr(const struct path *path)
12821282
return rc;
12831283
}
12841284

1285+
/**
1286+
* smack_inode_xattr_skipcap - Skip the xattr capability checks?
1287+
* @name: name of the xattr
1288+
*
1289+
* Returns 1 to indicate that Smack "owns" the access control rights to xattrs
1290+
* named @name; the LSM layer should avoid enforcing any traditional
1291+
* capability based access controls on this xattr. Returns 0 to indicate that
1292+
* Smack does not "own" the access control rights to xattrs named @name and is
1293+
* deferring to the LSM layer for further access controls, including capability
1294+
* based controls.
1295+
*/
1296+
static int smack_inode_xattr_skipcap(const char *name)
1297+
{
1298+
if (strncmp(name, XATTR_SMACK_SUFFIX, strlen(XATTR_SMACK_SUFFIX)))
1299+
return 0;
1300+
1301+
if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
1302+
strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
1303+
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
1304+
strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
1305+
strcmp(name, XATTR_NAME_SMACKMMAP) == 0 ||
1306+
strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0)
1307+
return 1;
1308+
1309+
return 0;
1310+
}
1311+
12851312
/**
12861313
* smack_inode_setxattr - Smack check for setting xattrs
12871314
* @idmap: idmap of the mount
@@ -1325,8 +1352,7 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
13251352
size != TRANS_TRUE_SIZE ||
13261353
strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
13271354
rc = -EINVAL;
1328-
} else
1329-
rc = cap_inode_setxattr(dentry, name, value, size, flags);
1355+
}
13301356

13311357
if (check_priv && !smack_privileged(CAP_MAC_ADMIN))
13321358
rc = -EPERM;
@@ -5051,6 +5077,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
50515077
LSM_HOOK_INIT(inode_permission, smack_inode_permission),
50525078
LSM_HOOK_INIT(inode_setattr, smack_inode_setattr),
50535079
LSM_HOOK_INIT(inode_getattr, smack_inode_getattr),
5080+
LSM_HOOK_INIT(inode_xattr_skipcap, smack_inode_xattr_skipcap),
50545081
LSM_HOOK_INIT(inode_setxattr, smack_inode_setxattr),
50555082
LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr),
50565083
LSM_HOOK_INIT(inode_getxattr, smack_inode_getxattr),

0 commit comments

Comments
 (0)