summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMimi Zohar <zohar@linux.ibm.com>2021-06-01 22:09:45 +0300
committerMimi Zohar <zohar@linux.ibm.com>2021-06-01 22:09:45 +0300
commit5a25d8ceb8611c06797b74e22d04af2b9fefd130 (patch)
tree4dfb18c8666e1cb579f8380fa8846e2a25343308
parent49219d9b8785ba712575c40e48ce0f7461254626 (diff)
parented1b472fc15aeaa20ddeeb93fd25190014e50d17 (diff)
downloadlinux-5a25d8ceb8611c06797b74e22d04af2b9fefd130.tar.xz
Merge branch 'misc-evm-v7' into next-integrity
From cover letter: EVM portable signatures are particularly suitable for the protection of metadata of immutable files where metadata is signed by a software vendor. They can be used for example in conjunction with an IMA policy that appraises only executed and memory mapped files. However, until now portable signatures can be properly installed only if the EVM_ALLOW_METADATA_WRITES initialization flag is also set, which disables metadata verification until an HMAC key is loaded. This will cause metadata writes to be allowed even in the situations where they shouldn't (metadata protected by a portable signature is immutable). The main reason why setting the flag is necessary is that the operations necessary to install portable signatures and protected metadata would be otherwise denied, despite being legitimate, due to the fact that the decision logic has to avoid an unsafe recalculation of the HMAC that would make the unsuccessfully verified metadata valid. However, the decision logic is too coarse, and does not fully take into account all the possible situations where metadata operations could be allowed. For example, if the HMAC key is not loaded and it cannot be loaded in the future due the EVM_SETUP_COMPLETE flag being set, it wouldn't be a problem to allow metadata operations, as they wouldn't result in an HMAC being recalculated. This patch set extends the decision logic and adds the necessary exceptions to use portable signatures without turning off metadata verification and deprecates the EVM_ALLOW_METADATA_WRITES flag. Link: https://lore.kernel.org/linux-integrity/20210514152753.982958-1-roberto.sassu@huawei.com/
-rw-r--r--Documentation/ABI/testing/evm36
-rw-r--r--Documentation/security/IMA-templates.rst4
-rw-r--r--include/linux/evm.h18
-rw-r--r--include/linux/integrity.h1
-rw-r--r--security/integrity/evm/evm_main.c247
-rw-r--r--security/integrity/evm/evm_secfs.c8
-rw-r--r--security/integrity/iint.c4
-rw-r--r--security/integrity/ima/ima_appraise.c43
-rw-r--r--security/integrity/ima/ima_init.c4
-rw-r--r--security/integrity/ima/ima_template.c2
-rw-r--r--security/integrity/ima/ima_template_lib.c33
-rw-r--r--security/integrity/ima/ima_template_lib.h2
-rw-r--r--security/security.c4
13 files changed, 355 insertions, 51 deletions
diff --git a/Documentation/ABI/testing/evm b/Documentation/ABI/testing/evm
index 3c477ba48a31..553fd8a33e56 100644
--- a/Documentation/ABI/testing/evm
+++ b/Documentation/ABI/testing/evm
@@ -24,7 +24,7 @@ Description:
1 Enable digital signature validation
2 Permit modification of EVM-protected metadata at
runtime. Not supported if HMAC validation and
- creation is enabled.
+ creation is enabled (deprecated).
31 Disable further runtime modification of EVM policy
=== ==================================================
@@ -47,10 +47,38 @@ Description:
will enable digital signature validation, permit
modification of EVM-protected metadata and
- disable all further modification of policy
+ disable all further modification of policy. This option is now
+ deprecated in favor of::
- Note that once a key has been loaded, it will no longer be
- possible to enable metadata modification.
+ echo 0x80000002 ><securityfs>/evm
+
+ as the outstanding issues that prevent the usage of EVM portable
+ signatures have been solved.
+
+ Echoing a value is additive, the new value is added to the
+ existing initialization flags.
+
+ For example, after::
+
+ echo 2 ><securityfs>/evm
+
+ another echo can be performed::
+
+ echo 1 ><securityfs>/evm
+
+ and the resulting value will be 3.
+
+ Note that once an HMAC key has been loaded, it will no longer
+ be possible to enable metadata modification. Signaling that an
+ HMAC key has been loaded will clear the corresponding flag.
+ For example, if the current value is 6 (2 and 4 set)::
+
+ echo 1 ><securityfs>/evm
+
+ will set the new value to 3 (4 cleared).
+
+ Loading an HMAC key is the only way to disable metadata
+ modification.
Until key loading has been signaled EVM can not create
or validate the 'security.evm' xattr, but returns
diff --git a/Documentation/security/IMA-templates.rst b/Documentation/security/IMA-templates.rst
index c5a8432972ef..9f3e86ab028a 100644
--- a/Documentation/security/IMA-templates.rst
+++ b/Documentation/security/IMA-templates.rst
@@ -70,9 +70,11 @@ descriptors by adding their identifier to the format string
prefix is shown only if the hash algorithm is not SHA1 or MD5);
- 'd-modsig': the digest of the event without the appended modsig;
- 'n-ng': the name of the event, without size limitations;
- - 'sig': the file signature;
+ - 'sig': the file signature, or the EVM portable signature if the file
+ signature is not found;
- 'modsig' the appended file signature;
- 'buf': the buffer data that was used to generate the hash without size limitations;
+ - 'evmsig': the EVM portable signature;
Below, there is the list of defined template descriptors:
diff --git a/include/linux/evm.h b/include/linux/evm.h
index 8302bc29bb35..31ef1dbbb3ac 100644
--- a/include/linux/evm.h
+++ b/include/linux/evm.h
@@ -23,18 +23,21 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
struct integrity_iint_cache *iint);
extern int evm_inode_setattr(struct dentry *dentry, struct iattr *attr);
extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid);
-extern int evm_inode_setxattr(struct dentry *dentry, const char *name,
+extern int evm_inode_setxattr(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *name,
const void *value, size_t size);
extern void evm_inode_post_setxattr(struct dentry *dentry,
const char *xattr_name,
const void *xattr_value,
size_t xattr_value_len);
-extern int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name);
+extern int evm_inode_removexattr(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *xattr_name);
extern void evm_inode_post_removexattr(struct dentry *dentry,
const char *xattr_name);
extern int evm_inode_init_security(struct inode *inode,
const struct xattr *xattr_array,
struct xattr *evm);
+extern bool evm_revalidate_status(const char *xattr_name);
#ifdef CONFIG_FS_POSIX_ACL
extern int posix_xattr_acl(const char *xattrname);
#else
@@ -71,7 +74,8 @@ static inline void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
return;
}
-static inline int evm_inode_setxattr(struct dentry *dentry, const char *name,
+static inline int evm_inode_setxattr(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *name,
const void *value, size_t size)
{
return 0;
@@ -85,7 +89,8 @@ static inline void evm_inode_post_setxattr(struct dentry *dentry,
return;
}
-static inline int evm_inode_removexattr(struct dentry *dentry,
+static inline int evm_inode_removexattr(struct user_namespace *mnt_userns,
+ struct dentry *dentry,
const char *xattr_name)
{
return 0;
@@ -104,5 +109,10 @@ static inline int evm_inode_init_security(struct inode *inode,
return 0;
}
+static inline bool evm_revalidate_status(const char *xattr_name)
+{
+ return false;
+}
+
#endif /* CONFIG_EVM */
#endif /* LINUX_EVM_H */
diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index 2271939c5c31..2ea0f2f65ab6 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -13,6 +13,7 @@ enum integrity_status {
INTEGRITY_PASS = 0,
INTEGRITY_PASS_IMMUTABLE,
INTEGRITY_FAIL,
+ INTEGRITY_FAIL_IMMUTABLE,
INTEGRITY_NOLABEL,
INTEGRITY_NOXATTRS,
INTEGRITY_UNKNOWN,
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 0de367aaa2d3..0196168aeb7d 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -18,6 +18,7 @@
#include <linux/integrity.h>
#include <linux/evm.h>
#include <linux/magic.h>
+#include <linux/posix_acl_xattr.h>
#include <crypto/hash.h>
#include <crypto/hash_info.h>
@@ -27,7 +28,8 @@
int evm_initialized;
static const char * const integrity_status_msg[] = {
- "pass", "pass_immutable", "fail", "no_label", "no_xattrs", "unknown"
+ "pass", "pass_immutable", "fail", "fail_immutable", "no_label",
+ "no_xattrs", "unknown"
};
int evm_hmac_attrs;
@@ -90,6 +92,24 @@ static bool evm_key_loaded(void)
return (bool)(evm_initialized & EVM_KEY_MASK);
}
+/*
+ * This function determines whether or not it is safe to ignore verification
+ * errors, based on the ability of EVM to calculate HMACs. If the HMAC key
+ * is not loaded, and it cannot be loaded in the future due to the
+ * EVM_SETUP_COMPLETE initialization flag, allowing an operation despite the
+ * attrs/xattrs being found invalid will not make them valid.
+ */
+static bool evm_hmac_disabled(void)
+{
+ if (evm_initialized & EVM_INIT_HMAC)
+ return false;
+
+ if (!(evm_initialized & EVM_SETUP_COMPLETE))
+ return false;
+
+ return true;
+}
+
static int evm_find_protected_xattrs(struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
@@ -137,7 +157,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
enum integrity_status evm_status = INTEGRITY_PASS;
struct evm_digest digest;
struct inode *inode;
- int rc, xattr_len;
+ int rc, xattr_len, evm_immutable = 0;
if (iint && (iint->evm_status == INTEGRITY_PASS ||
iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
@@ -182,8 +202,10 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
if (rc)
rc = -EINVAL;
break;
- case EVM_IMA_XATTR_DIGSIG:
case EVM_XATTR_PORTABLE_DIGSIG:
+ evm_immutable = 1;
+ fallthrough;
+ case EVM_IMA_XATTR_DIGSIG:
/* accept xattr with non-empty signature field */
if (xattr_len <= sizeof(struct signature_v2_hdr)) {
evm_status = INTEGRITY_FAIL;
@@ -220,9 +242,14 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
break;
}
- if (rc)
- evm_status = (rc == -ENODATA) ?
- INTEGRITY_NOXATTRS : INTEGRITY_FAIL;
+ if (rc) {
+ if (rc == -ENODATA)
+ evm_status = INTEGRITY_NOXATTRS;
+ else if (evm_immutable)
+ evm_status = INTEGRITY_FAIL_IMMUTABLE;
+ else
+ evm_status = INTEGRITY_FAIL;
+ }
out:
if (iint)
iint->evm_status = evm_status;
@@ -305,6 +332,92 @@ 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;
+
+ /*
+ * user_ns is not relevant here, ACL_USER/ACL_GROUP don't have impact
+ * on the inode mode (see posix_acl_equiv_mode()).
+ */
+ acl = posix_acl_from_xattr(&init_user_ns, 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
+ * @xattr_name: requested xattr
+ * @xattr_value: requested xattr value
+ * @xattr_value_len: requested xattr value length
+ *
+ * Check if passed xattr value differs from current value.
+ *
+ * Returns 1 if passed xattr value differs from current value, 0 otherwise.
+ */
+static int evm_xattr_change(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *xattr_name,
+ const void *xattr_value, size_t xattr_value_len)
+{
+ 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)
+ return 1;
+
+ if (rc == xattr_value_len)
+ rc = !!memcmp(xattr_value, xattr_data, rc);
+ else
+ rc = 1;
+
+ kfree(xattr_data);
+ return rc;
+}
+
+/*
* evm_protect_xattr - protect the EVM extended attribute
*
* Prevent security.evm from being modified or removed without the
@@ -316,7 +429,8 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
* For posix xattr acls only, permit security.evm, even if it currently
* doesn't exist, to be updated unless the EVM signature is immutable.
*/
-static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
+static int evm_protect_xattr(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
enum integrity_status evm_status;
@@ -338,6 +452,10 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
if (evm_status == INTEGRITY_NOXATTRS) {
struct integrity_iint_cache *iint;
+ /* Exception if the HMAC is not going to be calculated. */
+ if (evm_hmac_disabled())
+ return 0;
+
iint = integrity_iint_find(d_backing_inode(dentry));
if (iint && (iint->flags & IMA_NEW_FILE))
return 0;
@@ -354,7 +472,25 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
-EPERM, 0);
}
out:
- if (evm_status != INTEGRITY_PASS)
+ /* 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_xattr_change(mnt_userns, dentry, xattr_name, xattr_value,
+ xattr_value_len))
+ 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],
@@ -364,6 +500,7 @@ out:
/**
* evm_inode_setxattr - protect the EVM extended attribute
+ * @mnt_userns: user namespace of the idmapped mount
* @dentry: pointer to the affected dentry
* @xattr_name: pointer to the affected extended attribute name
* @xattr_value: pointer to the new extended attribute value
@@ -375,8 +512,9 @@ out:
* userspace from writing HMAC value. Writing 'security.evm' requires
* requires CAP_SYS_ADMIN privileges.
*/
-int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
- const void *xattr_value, size_t xattr_value_len)
+int evm_inode_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+ const char *xattr_name, const void *xattr_value,
+ size_t xattr_value_len)
{
const struct evm_ima_xattr_data *xattr_data = xattr_value;
@@ -393,19 +531,21 @@ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG)
return -EPERM;
}
- return evm_protect_xattr(dentry, xattr_name, xattr_value,
+ return evm_protect_xattr(mnt_userns, dentry, xattr_name, xattr_value,
xattr_value_len);
}
/**
* evm_inode_removexattr - protect the EVM extended attribute
+ * @mnt_userns: user namespace of the idmapped mount
* @dentry: pointer to the affected dentry
* @xattr_name: pointer to the affected extended attribute name
*
* Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that
* the current value is valid.
*/
-int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name)
+int evm_inode_removexattr(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *xattr_name)
{
/* Policy permits modification of the protected xattrs even though
* there's no HMAC key loaded
@@ -413,7 +553,7 @@ int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name)
if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
return 0;
- return evm_protect_xattr(dentry, xattr_name, NULL, 0);
+ return evm_protect_xattr(mnt_userns, dentry, xattr_name, NULL, 0);
}
static void evm_reset_status(struct inode *inode)
@@ -426,6 +566,31 @@ static void evm_reset_status(struct inode *inode)
}
/**
+ * evm_revalidate_status - report whether EVM status re-validation is necessary
+ * @xattr_name: pointer to the affected extended attribute name
+ *
+ * Report whether callers of evm_verifyxattr() should re-validate the
+ * EVM status.
+ *
+ * Return true if re-validation is necessary, false otherwise.
+ */
+bool evm_revalidate_status(const char *xattr_name)
+{
+ if (!evm_key_loaded())
+ return false;
+
+ /* evm_inode_post_setattr() passes NULL */
+ if (!xattr_name)
+ return true;
+
+ if (!evm_protected_xattr(xattr_name) && !posix_xattr_acl(xattr_name) &&
+ strcmp(xattr_name, XATTR_NAME_EVM))
+ return false;
+
+ return true;
+}
+
+/**
* evm_inode_post_setxattr - update 'security.evm' to reflect the changes
* @dentry: pointer to the affected dentry
* @xattr_name: pointer to the affected extended attribute name
@@ -441,12 +606,17 @@ static void evm_reset_status(struct inode *inode)
void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
- if (!evm_key_loaded() || (!evm_protected_xattr(xattr_name)
- && !posix_xattr_acl(xattr_name)))
+ if (!evm_revalidate_status(xattr_name))
return;
evm_reset_status(dentry->d_inode);
+ if (!strcmp(xattr_name, XATTR_NAME_EVM))
+ return;
+
+ if (!(evm_initialized & EVM_INIT_HMAC))
+ return;
+
evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len);
}
@@ -462,14 +632,33 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
*/
void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
{
- if (!evm_key_loaded() || !evm_protected_xattr(xattr_name))
+ if (!evm_revalidate_status(xattr_name))
return;
evm_reset_status(dentry->d_inode);
+ if (!strcmp(xattr_name, XATTR_NAME_EVM))
+ return;
+
+ if (!(evm_initialized & EVM_INIT_HMAC))
+ return;
+
evm_update_evmxattr(dentry, xattr_name, NULL, 0);
}
+static int evm_attr_change(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = d_backing_inode(dentry);
+ unsigned int ia_valid = attr->ia_valid;
+
+ if ((!(ia_valid & ATTR_UID) || uid_eq(attr->ia_uid, inode->i_uid)) &&
+ (!(ia_valid & ATTR_GID) || gid_eq(attr->ia_gid, inode->i_gid)) &&
+ (!(ia_valid & ATTR_MODE) || attr->ia_mode == inode->i_mode))
+ return 0;
+
+ return 1;
+}
+
/**
* evm_inode_setattr - prevent updating an invalid EVM extended attribute
* @dentry: pointer to the affected dentry
@@ -491,9 +680,21 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
return 0;
evm_status = evm_verify_current_integrity(dentry);
+ /*
+ * Writing attrs is safe for portable signatures, as portable signatures
+ * are immutable and can never be updated.
+ */
if ((evm_status == INTEGRITY_PASS) ||
- (evm_status == INTEGRITY_NOXATTRS))
+ (evm_status == INTEGRITY_NOXATTRS) ||
+ (evm_status == INTEGRITY_FAIL_IMMUTABLE) ||
+ (evm_hmac_disabled() && (evm_status == INTEGRITY_NOLABEL ||
+ evm_status == INTEGRITY_UNKNOWN)))
+ return 0;
+
+ if (evm_status == INTEGRITY_PASS_IMMUTABLE &&
+ !evm_attr_change(dentry, attr))
return 0;
+
integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry),
dentry->d_name.name, "appraise_metadata",
integrity_status_msg[evm_status], -EPERM, 0);
@@ -513,7 +714,12 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
*/
void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
{
- if (!evm_key_loaded())
+ if (!evm_revalidate_status(NULL))
+ return;
+
+ evm_reset_status(dentry->d_inode);
+
+ if (!(evm_initialized & EVM_INIT_HMAC))
return;
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
@@ -521,7 +727,7 @@ void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
}
/*
- * evm_inode_init_security - initializes security.evm
+ * evm_inode_init_security - initializes security.evm HMAC value
*/
int evm_inode_init_security(struct inode *inode,
const struct xattr *lsm_xattr,
@@ -530,7 +736,8 @@ int evm_inode_init_security(struct inode *inode,
struct evm_xattr *xattr_data;
int rc;
- if (!evm_key_loaded() || !evm_protected_xattr(lsm_xattr->name))
+ if (!(evm_initialized & EVM_INIT_HMAC) ||
+ !evm_protected_xattr(lsm_xattr->name))
return 0;
xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS);
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
index 0007d3362754..5f0da41bccd0 100644
--- a/security/integrity/evm/evm_secfs.c
+++ b/security/integrity/evm/evm_secfs.c
@@ -81,12 +81,12 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf,
if (!i || (i & ~EVM_INIT_MASK) != 0)
return -EINVAL;
- /* Don't allow a request to freshly enable metadata writes if
- * keys are loaded.
+ /*
+ * Don't allow a request to enable metadata writes if
+ * an HMAC key is loaded.
*/
if ((i & EVM_ALLOW_METADATA_WRITES) &&
- ((evm_initialized & EVM_KEY_MASK) != 0) &&
- !(evm_initialized & EVM_ALLOW_METADATA_WRITES))
+ (evm_initialized & EVM_INIT_HMAC) != 0)
return -EPERM;
if (i & EVM_INIT_HMAC) {
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index fca8a9409e4a..8638976f7990 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -208,7 +208,9 @@ int integrity_kernel_read(struct file *file, loff_t offset,
void __init integrity_load_keys(void)
{
ima_load_x509();
- evm_load_x509();
+
+ if (!IS_ENABLED(CONFIG_IMA_LOAD_X509))
+ evm_load_x509();
}
static int __init integrity_fs_init(void)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 4e5eb0236278..940695e7b535 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -242,12 +242,16 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
hash_start = 1;
fallthrough;
case IMA_XATTR_DIGEST:
- if (iint->flags & IMA_DIGSIG_REQUIRED) {
- *cause = "IMA-signature-required";
- *status = INTEGRITY_FAIL;
- break;
+ if (*status != INTEGRITY_PASS_IMMUTABLE) {
+ if (iint->flags & IMA_DIGSIG_REQUIRED) {
+ *cause = "IMA-signature-required";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+ clear_bit(IMA_DIGSIG, &iint->atomic_flags);
+ } else {
+ set_bit(IMA_DIGSIG, &iint->atomic_flags);
}
- clear_bit(IMA_DIGSIG, &iint->atomic_flags);
if (xattr_len - sizeof(xattr_value->type) - hash_start >=
iint->ima_hash->length)
/*
@@ -416,6 +420,9 @@ int ima_appraise_measurement(enum ima_hooks func,
case INTEGRITY_NOLABEL: /* No security.evm xattr. */
cause = "missing-HMAC";
goto out;
+ case INTEGRITY_FAIL_IMMUTABLE:
+ set_bit(IMA_DIGSIG, &iint->atomic_flags);
+ fallthrough;
case INTEGRITY_FAIL: /* Invalid HMAC/signature. */
cause = "invalid-HMAC";
goto out;
@@ -459,9 +466,12 @@ out:
status = INTEGRITY_PASS;
}
- /* Permit new files with file signatures, but without data. */
+ /*
+ * Permit new files with file/EVM portable signatures, but
+ * without data.
+ */
if (inode->i_size == 0 && iint->flags & IMA_NEW_FILE &&
- xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG) {
+ test_bit(IMA_DIGSIG, &iint->atomic_flags)) {
status = INTEGRITY_PASS;
}
@@ -522,8 +532,6 @@ void ima_inode_post_setattr(struct user_namespace *mnt_userns,
return;
action = ima_must_appraise(mnt_userns, inode, MAY_ACCESS, POST_SETATTR);
- if (!action)
- __vfs_removexattr(&init_user_ns, dentry, XATTR_NAME_IMA);
iint = integrity_iint_find(inode);
if (iint) {
set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
@@ -570,6 +578,7 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
const struct evm_ima_xattr_data *xvalue = xattr_value;
+ int digsig = 0;
int result;
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
@@ -577,9 +586,14 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
if (result == 1) {
if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
return -EINVAL;
- ima_reset_appraise_flags(d_backing_inode(dentry),
- xvalue->type == EVM_IMA_XATTR_DIGSIG);
- result = 0;
+ digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG);
+ } else if (!strcmp(xattr_name, XATTR_NAME_EVM) && xattr_value_len > 0) {
+ digsig = (xvalue->type == EVM_XATTR_PORTABLE_DIGSIG);
+ }
+ if (result == 1 || evm_revalidate_status(xattr_name)) {
+ ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
+ if (result == 1)
+ result = 0;
}
return result;
}
@@ -589,9 +603,10 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
int result;
result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
- if (result == 1) {
+ if (result == 1 || evm_revalidate_status(xattr_name)) {
ima_reset_appraise_flags(d_backing_inode(dentry), 0);
- result = 0;
+ if (result == 1)
+ result = 0;
}
return result;
}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 6e8742916d1d..5076a7d9d23e 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -108,6 +108,10 @@ void __init ima_load_x509(void)
ima_policy_flag &= ~unset_flags;
integrity_load_x509(INTEGRITY_KEYRING_IMA, CONFIG_IMA_X509_PATH);
+
+ /* load also EVM key to avoid appraisal */
+ evm_load_x509();
+
ima_policy_flag |= unset_flags;
}
#endif
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 4e081e650047..7a60848c04a5 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -45,6 +45,8 @@ static const struct ima_template_field supported_fields[] = {
.field_show = ima_show_template_digest_ng},
{.field_id = "modsig", .field_init = ima_eventmodsig_init,
.field_show = ima_show_template_sig},
+ {.field_id = "evmsig", .field_init = ima_eventevmsig_init,
+ .field_show = ima_show_template_sig},
};
/*
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index c022ee9e2a4e..4314d9a3514c 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -10,6 +10,7 @@
*/
#include "ima_template_lib.h"
+#include <linux/xattr.h>
static bool ima_template_hash_algo_allowed(u8 algo)
{
@@ -438,7 +439,7 @@ int ima_eventsig_init(struct ima_event_data *event_data,
struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;
if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG))
- return 0;
+ return ima_eventevmsig_init(event_data, field_data);
return ima_write_template_field_data(xattr_value, event_data->xattr_len,
DATA_FMT_HEX, field_data);
@@ -484,3 +485,33 @@ int ima_eventmodsig_init(struct ima_event_data *event_data,
return ima_write_template_field_data(data, data_len, DATA_FMT_HEX,
field_data);
}
+
+/*
+ * ima_eventevmsig_init - include the EVM portable signature as part of the
+ * template data
+ */
+int ima_eventevmsig_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ struct evm_ima_xattr_data *xattr_data = NULL;
+ int rc = 0;
+
+ if (!event_data->file)
+ return 0;
+
+ rc = vfs_getxattr_alloc(&init_user_ns, file_dentry(event_data->file),
+ XATTR_NAME_EVM, (char **)&xattr_data, 0,
+ GFP_NOFS);
+ if (rc <= 0)
+ return 0;
+
+ if (xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) {
+ kfree(xattr_data);
+ return 0;
+ }
+
+ rc = ima_write_template_field_data((char *)xattr_data, rc, DATA_FMT_HEX,
+ field_data);
+ kfree(xattr_data);
+ return rc;
+}
diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h
index 6b3b880637a0..f4b2a2056d1d 100644
--- a/security/integrity/ima/ima_template_lib.h
+++ b/security/integrity/ima/ima_template_lib.h
@@ -46,4 +46,6 @@ int ima_eventbuf_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventmodsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
+int ima_eventevmsig_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */
diff --git a/security/security.c b/security/security.c
index b38155b2de83..e9f8010a2341 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1354,7 +1354,7 @@ int security_inode_setxattr(struct user_namespace *mnt_userns,
ret = ima_inode_setxattr(dentry, name, value, size);
if (ret)
return ret;
- return evm_inode_setxattr(dentry, name, value, size);
+ return evm_inode_setxattr(mnt_userns, dentry, name, value, size);
}
void security_inode_post_setxattr(struct dentry *dentry, const char *name,
@@ -1399,7 +1399,7 @@ int security_inode_removexattr(struct user_namespace *mnt_userns,
ret = ima_inode_removexattr(dentry, name);
if (ret)
return ret;
- return evm_inode_removexattr(dentry, name);
+ return evm_inode_removexattr(mnt_userns, dentry, name);
}
int security_inode_need_killpriv(struct dentry *dentry)