summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2022-10-24 16:43:21 +0200
committerChristian Brauner (Microsoft) <brauner@kernel.org>2022-10-24 16:43:21 +0200
commit03fd1402bd7d93bd4598fc961632ef2737a500fd (patch)
tree5c327eae239d9466d0e80c9974903ed4d4babb94 /security
parentb4dd412d4a6231de100ae8ce2a9e1ed649f9e748 (diff)
parent0a26bde2c9db9817e2b4c0f890236f78d4d8ed7c (diff)
Merge branch 'fs.acl.rework' into for-next
Diffstat (limited to 'security')
-rw-r--r--security/integrity/evm/evm_main.c147
-rw-r--r--security/integrity/ima/ima_appraise.c9
-rw-r--r--security/security.c42
-rw-r--r--security/selinux/hooks.c22
-rw-r--r--security/smack/smack_lsm.c71
5 files changed, 226 insertions, 65 deletions
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 23d484e05e6f..e074c2b4d499 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -8,7 +8,7 @@
*
* File: evm_main.c
* implements evm_inode_setxattr, evm_inode_post_setxattr,
- * evm_inode_removexattr, and evm_verifyxattr
+ * evm_inode_removexattr, evm_verifyxattr, and evm_inode_set_acl.
*/
#define pr_fmt(fmt) "EVM: "fmt
@@ -435,66 +435,6 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
}
/*
- * evm_xattr_acl_change - check if passed ACL changes the inode mode
- * @mnt_userns: user namespace of the idmapped mount
- * @dentry: pointer to the affected dentry
- * @xattr_name: requested xattr
- * @xattr_value: requested xattr value
- * @xattr_value_len: requested xattr value length
- *
- * Check if passed ACL changes the inode mode, which is protected by EVM.
- *
- * Returns 1 if passed ACL causes inode mode change, 0 otherwise.
- */
-static int evm_xattr_acl_change(struct user_namespace *mnt_userns,
- struct dentry *dentry, const char *xattr_name,
- const void *xattr_value, size_t xattr_value_len)
-{
-#ifdef CONFIG_FS_POSIX_ACL
- umode_t mode;
- struct posix_acl *acl = NULL, *acl_res;
- struct inode *inode = d_backing_inode(dentry);
- int rc;
-
- /*
- * An earlier comment here mentioned that the idmappings for
- * ACL_{GROUP,USER} don't matter since EVM is only interested in the
- * mode stored as part of POSIX ACLs. Nonetheless, if it must translate
- * from the uapi POSIX ACL representation to the VFS internal POSIX ACL
- * representation it should do so correctly. There's no guarantee that
- * we won't change POSIX ACLs in a way that ACL_{GROUP,USER} matters
- * for the mode at some point and it's difficult to keep track of all
- * the LSM and integrity modules and what they do to POSIX ACLs.
- *
- * Frankly, EVM shouldn't try to interpret the uapi struct for POSIX
- * ACLs it received. It requires knowledge that only the VFS is
- * guaranteed to have.
- */
- acl = vfs_set_acl_prepare(mnt_userns, i_user_ns(inode),
- xattr_value, xattr_value_len);
- if (IS_ERR_OR_NULL(acl))
- return 1;
-
- acl_res = acl;
- /*
- * Passing mnt_userns is necessary to correctly determine the GID in
- * an idmapped mount, as the GID is used to clear the setgid bit in
- * the inode mode.
- */
- rc = posix_acl_update_mode(mnt_userns, inode, &mode, &acl_res);
-
- posix_acl_release(acl);
-
- if (rc)
- return 1;
-
- if (inode->i_mode != mode)
- return 1;
-#endif
- return 0;
-}
-
-/*
* evm_xattr_change - check if passed xattr value differs from current value
* @mnt_userns: user namespace of the idmapped mount
* @dentry: pointer to the affected dentry
@@ -513,10 +453,6 @@ static int evm_xattr_change(struct user_namespace *mnt_userns,
char *xattr_data = NULL;
int rc = 0;
- if (posix_xattr_acl(xattr_name))
- return evm_xattr_acl_change(mnt_userns, dentry, xattr_name,
- xattr_value, xattr_value_len);
-
rc = vfs_getxattr_alloc(&init_user_ns, dentry, xattr_name, &xattr_data,
0, GFP_NOFS);
if (rc < 0)
@@ -670,6 +606,87 @@ int evm_inode_removexattr(struct user_namespace *mnt_userns,
return evm_protect_xattr(mnt_userns, dentry, xattr_name, NULL, 0);
}
+#ifdef CONFIG_FS_POSIX_ACL
+static int evm_inode_set_acl_change(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *name,
+ struct posix_acl *kacl)
+{
+ int rc;
+
+ umode_t mode;
+ struct inode *inode = d_backing_inode(dentry);
+
+ if (!kacl)
+ return 1;
+
+ rc = posix_acl_update_mode(mnt_userns, inode, &mode, &kacl);
+ if (rc || (inode->i_mode != mode))
+ return 1;
+
+ return 0;
+}
+#else
+static inline int evm_inode_set_acl_change(struct user_namespace *mnt_userns,
+ struct dentry *dentry,
+ const char *name,
+ struct posix_acl *kacl)
+{
+ return 0;
+}
+#endif
+
+/**
+ * evm_inode_set_acl - protect the EVM extended attribute from posix acls
+ * @mnt_userns: user namespace of the idmapped mount
+ * @dentry: pointer to the affected dentry
+ * @acl_name: name of the posix acl
+ * @kacl: pointer to the posix acls
+ *
+ * Prevent modifying posix acls causing the EVM HMAC to be re-calculated
+ * and 'security.evm' xattr updated, unless the existing 'security.evm' is
+ * valid.
+ */
+int evm_inode_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+ const char *acl_name, struct posix_acl *kacl)
+{
+ enum integrity_status evm_status;
+
+ /* Policy permits modification of the protected xattrs even though
+ * there's no HMAC key loaded
+ */
+ if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
+ return 0;
+
+ evm_status = evm_verify_current_integrity(dentry);
+ if ((evm_status == INTEGRITY_PASS) ||
+ (evm_status == INTEGRITY_NOXATTRS))
+ return 0;
+
+ /* Exception if the HMAC is not going to be calculated. */
+ if (evm_hmac_disabled() && (evm_status == INTEGRITY_NOLABEL ||
+ evm_status == INTEGRITY_UNKNOWN))
+ return 0;
+
+ /*
+ * Writing other xattrs is safe for portable signatures, as portable
+ * signatures are immutable and can never be updated.
+ */
+ if (evm_status == INTEGRITY_FAIL_IMMUTABLE)
+ return 0;
+
+ if (evm_status == INTEGRITY_PASS_IMMUTABLE &&
+ !evm_inode_set_acl_change(mnt_userns, dentry, acl_name, kacl))
+ return 0;
+
+ if (evm_status != INTEGRITY_PASS &&
+ evm_status != INTEGRITY_PASS_IMMUTABLE)
+ integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry),
+ dentry->d_name.name, "appraise_metadata",
+ integrity_status_msg[evm_status],
+ -EPERM, 0);
+ return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
+}
+
static void evm_reset_status(struct inode *inode)
{
struct integrity_iint_cache *iint;
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 3e0fbbd99534..3c9af3dc0713 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -774,6 +774,15 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
return result;
}
+int ima_inode_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+ const char *acl_name, struct posix_acl *kacl)
+{
+ if (evm_revalidate_status(acl_name))
+ ima_reset_appraise_flags(d_backing_inode(dentry), 0);
+
+ return 0;
+}
+
int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
{
int result;
diff --git a/security/security.c b/security/security.c
index 79d82cb6e469..bdc295ad5fba 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1372,6 +1372,48 @@ int security_inode_setxattr(struct user_namespace *mnt_userns,
return evm_inode_setxattr(mnt_userns, dentry, name, value, size);
}
+int security_inode_set_acl(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *acl_name,
+ struct posix_acl *kacl)
+{
+ int ret;
+
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+ ret = call_int_hook(inode_set_acl, 0, mnt_userns, dentry, acl_name,
+ kacl);
+ if (ret)
+ return ret;
+ ret = ima_inode_set_acl(mnt_userns, dentry, acl_name, kacl);
+ if (ret)
+ return ret;
+ return evm_inode_set_acl(mnt_userns, dentry, acl_name, kacl);
+}
+
+int security_inode_get_acl(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *acl_name)
+{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+ return call_int_hook(inode_get_acl, 0, mnt_userns, dentry, acl_name);
+}
+
+int security_inode_remove_acl(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *acl_name)
+{
+ int ret;
+
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+ ret = call_int_hook(inode_remove_acl, 0, mnt_userns, dentry, acl_name);
+ if (ret)
+ return ret;
+ ret = ima_inode_remove_acl(mnt_userns, dentry, acl_name);
+ if (ret)
+ return ret;
+ return evm_inode_remove_acl(mnt_userns, dentry, acl_name);
+}
+
void security_inode_post_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f553c370397e..7c5c8d17695c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3240,6 +3240,25 @@ static int selinux_inode_setxattr(struct user_namespace *mnt_userns,
&ad);
}
+static int selinux_inode_set_acl(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *acl_name,
+ struct posix_acl *kacl)
+{
+ return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
+}
+
+static int selinux_inode_get_acl(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *acl_name)
+{
+ return dentry_has_perm(current_cred(), dentry, FILE__GETATTR);
+}
+
+static int selinux_inode_remove_acl(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *acl_name)
+{
+ return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
+}
+
static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size,
int flags)
@@ -7088,6 +7107,9 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr),
LSM_HOOK_INIT(inode_listxattr, selinux_inode_listxattr),
LSM_HOOK_INIT(inode_removexattr, selinux_inode_removexattr),
+ LSM_HOOK_INIT(inode_set_acl, selinux_inode_set_acl),
+ LSM_HOOK_INIT(inode_get_acl, selinux_inode_get_acl),
+ LSM_HOOK_INIT(inode_remove_acl, selinux_inode_remove_acl),
LSM_HOOK_INIT(inode_getsecurity, selinux_inode_getsecurity),
LSM_HOOK_INIT(inode_setsecurity, selinux_inode_setsecurity),
LSM_HOOK_INIT(inode_listsecurity, selinux_inode_listsecurity),
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index b6306d71c908..cadef2f6a75e 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1393,6 +1393,74 @@ static int smack_inode_removexattr(struct user_namespace *mnt_userns,
}
/**
+ * smack_inode_set_acl - Smack check for setting posix acls
+ * @mnt_userns: the userns attached to the mnt this request came from
+ * @dentry: the object
+ * @acl_name: name of the posix acl
+ * @kacl: the posix acls
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_set_acl(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *acl_name,
+ struct posix_acl *kacl)
+{
+ struct smk_audit_info ad;
+ int rc;
+
+ 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);
+ return rc;
+}
+
+/**
+ * smack_inode_get_acl - Smack check for getting posix acls
+ * @mnt_userns: the userns attached to the mnt this request came from
+ * @dentry: the object
+ * @acl_name: name of the posix acl
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_get_acl(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *acl_name)
+{
+ struct smk_audit_info ad;
+ int rc;
+
+ 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_READ, &ad);
+ rc = smk_bu_inode(d_backing_inode(dentry), MAY_READ, rc);
+ return rc;
+}
+
+/**
+ * smack_inode_remove_acl - Smack check for getting posix acls
+ * @mnt_userns: the userns attached to the mnt this request came from
+ * @dentry: the object
+ * @acl_name: name of the posix acl
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_remove_acl(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *acl_name)
+{
+ struct smk_audit_info ad;
+ int rc;
+
+ 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);
+ return rc;
+}
+
+/**
* smack_inode_getsecurity - get smack xattrs
* @mnt_userns: active user namespace
* @inode: the object
@@ -4816,6 +4884,9 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr),
LSM_HOOK_INIT(inode_getxattr, smack_inode_getxattr),
LSM_HOOK_INIT(inode_removexattr, smack_inode_removexattr),
+ LSM_HOOK_INIT(inode_set_acl, smack_inode_set_acl),
+ LSM_HOOK_INIT(inode_get_acl, smack_inode_get_acl),
+ LSM_HOOK_INIT(inode_remove_acl, smack_inode_remove_acl),
LSM_HOOK_INIT(inode_getsecurity, smack_inode_getsecurity),
LSM_HOOK_INIT(inode_setsecurity, smack_inode_setsecurity),
LSM_HOOK_INIT(inode_listsecurity, smack_inode_listsecurity),