/* * evm_verify_hmac - calculate and compare the HMAC with the EVM xattr * * Compute the HMAC on the dentry's protected set of extended attributes * and compare it against the stored security.evm xattr. * * For performance: * - use the previoulsy retrieved xattr value and length to calculate the * HMAC.) * - cache the verification result in the iint, when available. * * Returns integrity status */ static enum integrity_status evm_verify_hmac(struct dentry *dentry, const char *xattr_name, char *xattr_value, size_t xattr_value_len, struct integrity_iint_cache *iint) { struct evm_ima_xattr_data *xattr_data = NULL; struct evm_ima_xattr_data calc; enum integrity_status evm_status = INTEGRITY_PASS; int rc, xattr_len; if (iint && iint->evm_status == INTEGRITY_PASS) return iint->evm_status; /* if status is not PASS, try to check again - against -ENOMEM */ /* first need to know the sig type */ rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0, GFP_NOFS); if (rc <= 0) { if (rc == 0) evm_status = INTEGRITY_FAIL; /* empty */ else if (rc == -ENODATA) { rc = evm_find_protected_xattrs(dentry); if (rc > 0) evm_status = INTEGRITY_NOLABEL; else if (rc == 0) evm_status = INTEGRITY_NOXATTRS; /* new file */ } goto out; } xattr_len = rc - 1; /* check value type */ switch (xattr_data->type) { case EVM_XATTR_HMAC: rc = evm_calc_hmac(dentry, xattr_name, xattr_value, xattr_value_len, calc.digest); if (rc) break; rc = memcmp(xattr_data->digest, calc.digest, sizeof(calc.digest)); if (rc) rc = -EINVAL; break; case EVM_IMA_XATTR_DIGSIG: rc = evm_calc_hash(dentry, xattr_name, xattr_value, xattr_value_len, calc.digest); if (rc) break; rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, xattr_data->digest, xattr_len, calc.digest, sizeof(calc.digest)); if (!rc) { /* we probably want to replace rsa with hmac here */ evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); } break; default: rc = -EINVAL; break; } if (rc) evm_status = (rc == -ENODATA) ? INTEGRITY_NOXATTRS : INTEGRITY_FAIL; out: if (iint) iint->evm_status = evm_status; kfree(xattr_data); return evm_status; }
/* * evm_verify_hmac - calculate and compare the HMAC with the EVM xattr * * Compute the HMAC on the dentry's protected set of extended attributes * and compare it against the stored security.evm xattr. * * For performance: * - use the previoulsy retrieved xattr value and length to calculate the * HMAC.) * - cache the verification result in the iint, when available. * * Returns integrity status */ static enum integrity_status evm_verify_hmac(struct dentry *dentry, const char *xattr_name, char *xattr_value, size_t xattr_value_len, struct integrity_iint_cache *iint) { struct evm_ima_xattr_data *xattr_data = NULL; struct evm_ima_xattr_data calc; enum integrity_status evm_status = INTEGRITY_PASS; int rc, xattr_len; if (iint && iint->evm_status == INTEGRITY_PASS) return iint->evm_status; /* if status is not PASS, try to check again - against -ENOMEM */ /* first need to know the sig type */ rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0, GFP_NOFS); if (rc <= 0) { evm_status = INTEGRITY_FAIL; if (rc == -ENODATA) { rc = evm_find_protected_xattrs(dentry); if (rc > 0) evm_status = INTEGRITY_NOLABEL; else if (rc == 0) evm_status = INTEGRITY_NOXATTRS; /* new file */ } else if (rc == -EOPNOTSUPP) { evm_status = INTEGRITY_UNKNOWN; } goto out; } xattr_len = rc; /* check value type */ switch (xattr_data->type) { case EVM_XATTR_HMAC: rc = evm_calc_hmac(dentry, xattr_name, xattr_value, xattr_value_len, calc.digest); if (rc) break; rc = memcmp(xattr_data->digest, calc.digest, sizeof(calc.digest)); if (rc) rc = -EINVAL; break; case EVM_IMA_XATTR_DIGSIG: rc = evm_calc_hash(dentry, xattr_name, xattr_value, xattr_value_len, calc.digest); if (rc) break; rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, (const char *)xattr_data, xattr_len, calc.digest, sizeof(calc.digest)); if (!rc) { /* Replace RSA with HMAC if not mounted readonly and * not immutable */ if (!IS_RDONLY(d_backing_inode(dentry)) && !IS_IMMUTABLE(d_backing_inode(dentry))) evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); } break; default: rc = -EINVAL; break; } if (rc) evm_status = (rc == -ENODATA) ? INTEGRITY_NOXATTRS : INTEGRITY_FAIL; out: if (iint) iint->evm_status = evm_status; kfree(xattr_data); return evm_status; }
/* * ima_appraise_measurement - appraise file measurement * * Call evm_verifyxattr() to verify the integrity of 'security.ima'. * Assuming success, compare the xattr hash with the collected measurement. * * Return 0 on success, error code otherwise */ int ima_appraise_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; struct evm_ima_xattr_data *xattr_value = NULL; enum integrity_status status = INTEGRITY_UNKNOWN; const char *op = "appraise_data"; char *cause = "unknown"; int rc; if (!ima_appraise) return 0; if (!inode->i_op->getxattr) return INTEGRITY_UNKNOWN; if (iint->flags & IMA_APPRAISED) return iint->ima_status; rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value, 0, GFP_NOFS); if (rc <= 0) { if (rc && rc != -ENODATA) goto out; cause = "missing-hash"; status = (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL; goto out; } status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint); if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) { if ((status == INTEGRITY_NOLABEL) || (status == INTEGRITY_NOXATTRS)) cause = "missing-HMAC"; else if (status == INTEGRITY_FAIL) cause = "invalid-HMAC"; goto out; } switch (xattr_value->type) { case IMA_XATTR_DIGEST: rc = memcmp(xattr_value->digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE); if (rc) { cause = "invalid-hash"; status = INTEGRITY_FAIL; print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE, xattr_value, sizeof(*xattr_value)); print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE, (u8 *)&iint->ima_xattr, sizeof iint->ima_xattr); break; } status = INTEGRITY_PASS; break; case EVM_IMA_XATTR_DIGSIG: iint->flags |= IMA_DIGSIG; rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, xattr_value->digest, rc - 1, iint->ima_xattr.digest, IMA_DIGEST_SIZE); if (rc == -EOPNOTSUPP) { status = INTEGRITY_UNKNOWN; } else if (rc) { cause = "invalid-signature"; status = INTEGRITY_FAIL; } else { status = INTEGRITY_PASS; } break; default: status = INTEGRITY_UNKNOWN; cause = "unknown-ima-data"; break; } out: if (status != INTEGRITY_PASS) { if ((ima_appraise & IMA_APPRAISE_FIX) && (!xattr_value || xattr_value->type != EVM_IMA_XATTR_DIGSIG)) { ima_fix_xattr(dentry, iint); status = INTEGRITY_PASS; } integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, op, cause, rc, 0); } else { iint->flags |= IMA_APPRAISED; } iint->ima_status = status; kfree(xattr_value); return status; }
/* * evm_verify_hmac - calculate and compare the HMAC with the EVM xattr * * Compute the HMAC on the dentry's protected set of extended attributes * and compare it against the stored security.evm xattr. * * For performance: * - use the previoulsy retrieved xattr value and length to calculate the * HMAC.) * - cache the verification result in the iint, when available. * * Returns integrity status */ static enum integrity_status evm_verify_hmac(struct dentry *dentry, const char *xattr_name, char *xattr_value, size_t xattr_value_len, struct integrity_iint_cache *iint) { struct evm_ima_xattr_data *xattr_data = NULL; struct evm_ima_xattr_data calc; enum integrity_status evm_status = INTEGRITY_PASS; struct inode *inode; int rc, xattr_len; if (iint && (iint->evm_status == INTEGRITY_PASS || iint->evm_status == INTEGRITY_PASS_IMMUTABLE)) return iint->evm_status; /* if status is not PASS, try to check again - against -ENOMEM */ /* first need to know the sig type */ rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0, GFP_NOFS); if (rc <= 0) { evm_status = INTEGRITY_FAIL; if (rc == -ENODATA) { rc = evm_find_protected_xattrs(dentry); if (rc > 0) evm_status = INTEGRITY_NOLABEL; else if (rc == 0) evm_status = INTEGRITY_NOXATTRS; /* new file */ } else if (rc == -EOPNOTSUPP) { evm_status = INTEGRITY_UNKNOWN; } goto out; } xattr_len = rc; /* check value type */ switch (xattr_data->type) { case EVM_XATTR_HMAC: if (xattr_len != sizeof(struct evm_ima_xattr_data)) { evm_status = INTEGRITY_FAIL; goto out; } rc = evm_calc_hmac(dentry, xattr_name, xattr_value, xattr_value_len, calc.digest); if (rc) break; rc = crypto_memneq(xattr_data->digest, calc.digest, sizeof(calc.digest)); if (rc) rc = -EINVAL; break; case EVM_IMA_XATTR_DIGSIG: case EVM_XATTR_PORTABLE_DIGSIG: rc = evm_calc_hash(dentry, xattr_name, xattr_value, xattr_value_len, xattr_data->type, calc.digest); if (rc) break; rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, (const char *)xattr_data, xattr_len, calc.digest, sizeof(calc.digest)); if (!rc) { inode = d_backing_inode(dentry); if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) { if (iint) iint->flags |= EVM_IMMUTABLE_DIGSIG; evm_status = INTEGRITY_PASS_IMMUTABLE; } else if (!IS_RDONLY(inode) && !(inode->i_sb->s_readonly_remount) && !IS_IMMUTABLE(inode)) { evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); } } break; default: rc = -EINVAL; break; } if (rc) evm_status = (rc == -ENODATA) ? INTEGRITY_NOXATTRS : INTEGRITY_FAIL; out: if (iint) iint->evm_status = evm_status; kfree(xattr_data); return evm_status; }