summaryrefslogtreecommitdiffstats
path: root/security/integrity/ima
diff options
context:
space:
mode:
Diffstat (limited to 'security/integrity/ima')
-rw-r--r--security/integrity/ima/ima_appraise.c114
-rw-r--r--security/integrity/ima/ima_policy.c46
-rw-r--r--security/integrity/ima/ima_template_lib.c4
3 files changed, 151 insertions, 13 deletions
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 17232bbfb9f9..cdb84dccd24e 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -13,7 +13,9 @@
#include <linux/magic.h>
#include <linux/ima.h>
#include <linux/evm.h>
+#include <linux/fsverity.h>
#include <keys/system_keyring.h>
+#include <uapi/linux/fsverity.h>
#include "ima.h"
@@ -183,13 +185,18 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
return ima_hash_algo;
switch (xattr_value->type) {
+ case IMA_VERITY_DIGSIG:
+ sig = (typeof(sig))xattr_value;
+ if (sig->version != 3 || xattr_len <= sizeof(*sig) ||
+ sig->hash_algo >= HASH_ALGO__LAST)
+ return ima_hash_algo;
+ return sig->hash_algo;
case EVM_IMA_XATTR_DIGSIG:
sig = (typeof(sig))xattr_value;
if (sig->version != 2 || xattr_len <= sizeof(*sig)
|| sig->hash_algo >= HASH_ALGO__LAST)
return ima_hash_algo;
return sig->hash_algo;
- break;
case IMA_XATTR_DIGEST_NG:
/* first byte contains algorithm id */
ret = xattr_value->data[0];
@@ -226,6 +233,40 @@ int ima_read_xattr(struct dentry *dentry,
}
/*
+ * calc_file_id_hash - calculate the hash of the ima_file_id struct data
+ * @type: xattr type [enum evm_ima_xattr_type]
+ * @algo: hash algorithm [enum hash_algo]
+ * @digest: pointer to the digest to be hashed
+ * @hash: (out) pointer to the hash
+ *
+ * IMA signature version 3 disambiguates the data that is signed by
+ * indirectly signing the hash of the ima_file_id structure data.
+ *
+ * Signing the ima_file_id struct is currently only supported for
+ * IMA_VERITY_DIGSIG type xattrs.
+ *
+ * Return 0 on success, error code otherwise.
+ */
+static int calc_file_id_hash(enum evm_ima_xattr_type type,
+ enum hash_algo algo, const u8 *digest,
+ struct ima_digest_data *hash)
+{
+ struct ima_file_id file_id = {
+ .hash_type = IMA_VERITY_DIGSIG, .hash_algorithm = algo};
+ unsigned int unused = HASH_MAX_DIGESTSIZE - hash_digest_size[algo];
+
+ if (type != IMA_VERITY_DIGSIG)
+ return -EINVAL;
+
+ memcpy(file_id.hash, digest, hash_digest_size[algo]);
+
+ hash->algo = algo;
+ hash->length = hash_digest_size[algo];
+
+ return ima_calc_buffer_hash(&file_id, sizeof(file_id) - unused, hash);
+}
+
+/*
* xattr_verify - verify xattr digest or signature
*
* Verify whether the hash or signature matches the file contents.
@@ -236,7 +277,10 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
struct evm_ima_xattr_data *xattr_value, int xattr_len,
enum integrity_status *status, const char **cause)
{
+ struct ima_max_digest_data hash;
+ struct signature_v2_hdr *sig;
int rc = -EINVAL, hash_start = 0;
+ int mask;
switch (xattr_value->type) {
case IMA_XATTR_DIGEST_NG:
@@ -246,7 +290,10 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
case IMA_XATTR_DIGEST:
if (*status != INTEGRITY_PASS_IMMUTABLE) {
if (iint->flags & IMA_DIGSIG_REQUIRED) {
- *cause = "IMA-signature-required";
+ if (iint->flags & IMA_VERITY_REQUIRED)
+ *cause = "verity-signature-required";
+ else
+ *cause = "IMA-signature-required";
*status = INTEGRITY_FAIL;
break;
}
@@ -274,6 +321,20 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
break;
case EVM_IMA_XATTR_DIGSIG:
set_bit(IMA_DIGSIG, &iint->atomic_flags);
+
+ mask = IMA_DIGSIG_REQUIRED | IMA_VERITY_REQUIRED;
+ if ((iint->flags & mask) == mask) {
+ *cause = "verity-signature-required";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+
+ sig = (typeof(sig))xattr_value;
+ if (sig->version >= 3) {
+ *cause = "invalid-signature-version";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
(const char *)xattr_value,
xattr_len,
@@ -297,6 +358,44 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
*status = INTEGRITY_PASS;
}
break;
+ case IMA_VERITY_DIGSIG:
+ set_bit(IMA_DIGSIG, &iint->atomic_flags);
+
+ if (iint->flags & IMA_DIGSIG_REQUIRED) {
+ if (!(iint->flags & IMA_VERITY_REQUIRED)) {
+ *cause = "IMA-signature-required";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+ }
+
+ sig = (typeof(sig))xattr_value;
+ if (sig->version != 3) {
+ *cause = "invalid-signature-version";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+
+ rc = calc_file_id_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo,
+ iint->ima_hash->digest, &hash.hdr);
+ if (rc) {
+ *cause = "sigv3-hashing-error";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+
+ rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
+ (const char *)xattr_value,
+ xattr_len, hash.digest,
+ hash.hdr.length);
+ if (rc) {
+ *cause = "invalid-verity-signature";
+ *status = INTEGRITY_FAIL;
+ } else {
+ *status = INTEGRITY_PASS;
+ }
+
+ break;
default:
*status = INTEGRITY_UNKNOWN;
*cause = "unknown-ima-data";
@@ -396,8 +495,15 @@ int ima_appraise_measurement(enum ima_hooks func,
if (rc && rc != -ENODATA)
goto out;
- cause = iint->flags & IMA_DIGSIG_REQUIRED ?
- "IMA-signature-required" : "missing-hash";
+ if (iint->flags & IMA_DIGSIG_REQUIRED) {
+ if (iint->flags & IMA_VERITY_REQUIRED)
+ cause = "verity-signature-required";
+ else
+ cause = "IMA-signature-required";
+ } else {
+ cause = "missing-hash";
+ }
+
status = INTEGRITY_NOLABEL;
if (file->f_mode & FMODE_CREATED)
iint->flags |= IMA_NEW_FILE;
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 390a8faa77f9..73917413365b 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1310,6 +1310,18 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
!(entry->flags & IMA_MODSIG_ALLOWED))
return false;
+ /*
+ * Unlike for regular IMA 'appraise' policy rules where security.ima
+ * xattr may contain either a file hash or signature, the security.ima
+ * xattr for fsverity must contain a file signature (sigv3). Ensure
+ * that 'appraise' rules for fsverity require file signatures by
+ * checking the IMA_DIGSIG_REQUIRED flag is set.
+ */
+ if (entry->action == APPRAISE &&
+ (entry->flags & IMA_VERITY_REQUIRED) &&
+ !(entry->flags & IMA_DIGSIG_REQUIRED))
+ return false;
+
return true;
}
@@ -1727,21 +1739,37 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
break;
case Opt_digest_type:
ima_log_string(ab, "digest_type", args[0].from);
- if ((strcmp(args[0].from, "verity")) == 0)
+ if (entry->flags & IMA_DIGSIG_REQUIRED)
+ result = -EINVAL;
+ else if ((strcmp(args[0].from, "verity")) == 0)
entry->flags |= IMA_VERITY_REQUIRED;
else
result = -EINVAL;
break;
case Opt_appraise_type:
ima_log_string(ab, "appraise_type", args[0].from);
- if ((strcmp(args[0].from, "imasig")) == 0)
- entry->flags |= IMA_DIGSIG_REQUIRED;
- else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
- strcmp(args[0].from, "imasig|modsig") == 0)
- entry->flags |= IMA_DIGSIG_REQUIRED |
+
+ if ((strcmp(args[0].from, "imasig")) == 0) {
+ if (entry->flags & IMA_VERITY_REQUIRED)
+ result = -EINVAL;
+ else
+ entry->flags |= IMA_DIGSIG_REQUIRED;
+ } else if (strcmp(args[0].from, "sigv3") == 0) {
+ /* Only fsverity supports sigv3 for now */
+ if (entry->flags & IMA_VERITY_REQUIRED)
+ entry->flags |= IMA_DIGSIG_REQUIRED;
+ else
+ result = -EINVAL;
+ } else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
+ strcmp(args[0].from, "imasig|modsig") == 0) {
+ if (entry->flags & IMA_VERITY_REQUIRED)
+ result = -EINVAL;
+ else
+ entry->flags |= IMA_DIGSIG_REQUIRED |
IMA_MODSIG_ALLOWED;
- else
+ } else {
result = -EINVAL;
+ }
break;
case Opt_appraise_flag:
ima_log_string(ab, "appraise_flag", args[0].from);
@@ -2183,7 +2211,9 @@ int ima_policy_show(struct seq_file *m, void *v)
if (entry->template)
seq_printf(m, "template=%s ", entry->template->name);
if (entry->flags & IMA_DIGSIG_REQUIRED) {
- if (entry->flags & IMA_MODSIG_ALLOWED)
+ if (entry->flags & IMA_VERITY_REQUIRED)
+ seq_puts(m, "appraise_type=sigv3 ");
+ else if (entry->flags & IMA_MODSIG_ALLOWED)
seq_puts(m, "appraise_type=imasig|modsig ");
else
seq_puts(m, "appraise_type=imasig ");
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 08fd74217e2c..c877f01a5471 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -535,7 +535,9 @@ 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))
+ if (!xattr_value ||
+ (xattr_value->type != EVM_IMA_XATTR_DIGSIG &&
+ xattr_value->type != IMA_VERITY_DIGSIG))
return ima_eventevmsig_init(event_data, field_data);
return ima_write_template_field_data(xattr_value, event_data->xattr_len,