The usual "bail on fail" behavior of LSM hooks doesn't work for security_inode_setxattr(). Modules are allowed to return -ENOSYS if the attribute specifed isn't one they manage. Fix the code to accomodate this unusal case. This requires changes to the hooks in SELinux and Smack. Signed-off-by: Casey Schaufler <casey@xxxxxxxxxxxxxxxx> --- security/security.c | 28 ++++++++++++++-------------- security/selinux/hooks.c | 7 ++----- security/smack/smack_lsm.c | 10 +++++----- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/security/security.c b/security/security.c index bb0eea873a7e..105b5ee22bcf 100644 --- a/security/security.c +++ b/security/security.c @@ -1343,24 +1343,24 @@ int security_inode_getattr(const struct path *path) int security_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { - int ret; + struct security_hook_list *hp; + int rc = -ENOSYS; if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) 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, 1, dentry, name, value, size, - flags); - if (ret == 1) - ret = cap_inode_setxattr(dentry, name, value, size, flags); - if (ret) - return ret; - ret = ima_inode_setxattr(dentry, name, value, size); - if (ret) - return ret; + hlist_for_each_entry(hp, &security_hook_heads.inode_setxattr, list) { + rc = hp->hook.inode_setxattr(dentry, name, value, size, flags); + if (rc != -ENOSYS) + break; + } + if (rc == -ENOSYS) + rc = cap_inode_setxattr(dentry, name, value, size, flags); + if (rc) + return rc; + rc = ima_inode_setxattr(dentry, name, value, size); + if (rc) + return rc; return evm_inode_setxattr(dentry, name, value, size); } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f58c96e2b6c1..501aed18f100 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2998,13 +2998,10 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, int rc = 0; if (strcmp(name, XATTR_NAME_SELINUX)) { - rc = cap_inode_setxattr(dentry, name, value, size, flags); - 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); + rc = dentry_has_perm(current_cred(), dentry, FILE__SETATTR); + return rc ? rc : -ENOSYS; } sbsec = selinux_superblock(inode->i_sb); diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index c45e2dc3f959..c84c71a31b80 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1194,7 +1194,7 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0) rc = -EINVAL; } else - rc = cap_inode_setxattr(dentry, name, value, size, flags); + rc = -ENOSYS; if (check_priv && !smack_privileged(CAP_MAC_ADMIN)) rc = -EPERM; @@ -1208,11 +1208,11 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, rc = -EINVAL; } - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); - smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - if (rc == 0) { - rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); + smk_ad_setfield_u_fs_path_dentry(&ad, dentry); + rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), + MAY_WRITE, &ad); rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc); } -- 2.17.0