ima: provide support for arbitrary hash algorithms
In preparation of supporting more hash algorithms with larger hash sizes needed for signature verification, this patch replaces the 20 byte sized digest, with a more flexible structure. The new structure includes the hash algorithm, digest size, and digest. Changelog: - recalculate filedata hash for the measurement list, if the signature hash digest size is greater than 20 bytes. - use generic HASH_ALGO_ - make ima_calc_file_hash static - scripts lindent and checkpatch fixes Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com> Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
This commit is contained in:
parent
3fe78ca2fb
commit
c7c8bb237f
9 changed files with 100 additions and 35 deletions
|
@ -21,8 +21,6 @@ struct x509_certificate {
|
||||||
char *authority; /* Authority key fingerprint as hex */
|
char *authority; /* Authority key fingerprint as hex */
|
||||||
struct tm valid_from;
|
struct tm valid_from;
|
||||||
struct tm valid_to;
|
struct tm valid_to;
|
||||||
enum pkey_algo pkey_algo : 8; /* Public key algorithm */
|
|
||||||
enum hash_algo sig_hash_algo : 8; /* Signature hash algorithm */
|
|
||||||
const void *tbs; /* Signed data */
|
const void *tbs; /* Signed data */
|
||||||
unsigned tbs_size; /* Size of signed data */
|
unsigned tbs_size; /* Size of signed data */
|
||||||
unsigned raw_sig_size; /* Size of sigature */
|
unsigned raw_sig_size; /* Size of sigature */
|
||||||
|
|
|
@ -213,7 +213,8 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
||||||
cert->valid_to.tm_year + 1900, cert->valid_to.tm_mon + 1,
|
cert->valid_to.tm_year + 1900, cert->valid_to.tm_mon + 1,
|
||||||
cert->valid_to.tm_mday, cert->valid_to.tm_hour,
|
cert->valid_to.tm_mday, cert->valid_to.tm_hour,
|
||||||
cert->valid_to.tm_min, cert->valid_to.tm_sec);
|
cert->valid_to.tm_min, cert->valid_to.tm_sec);
|
||||||
pr_devel("Cert Signature: %s\n",
|
pr_devel("Cert Signature: %s + %s\n",
|
||||||
|
pkey_algo_name[cert->sig.pkey_algo],
|
||||||
hash_algo_name[cert->sig.pkey_hash_algo]);
|
hash_algo_name[cert->sig.pkey_hash_algo]);
|
||||||
|
|
||||||
if (!cert->fingerprint) {
|
if (!cert->fingerprint) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ config IMA
|
||||||
select CRYPTO_HMAC
|
select CRYPTO_HMAC
|
||||||
select CRYPTO_MD5
|
select CRYPTO_MD5
|
||||||
select CRYPTO_SHA1
|
select CRYPTO_SHA1
|
||||||
|
select CRYPTO_HASH_INFO
|
||||||
select TCG_TPM if HAS_IOMEM && !UML
|
select TCG_TPM if HAS_IOMEM && !UML
|
||||||
select TCG_TIS if TCG_TPM && X86
|
select TCG_TIS if TCG_TPM && X86
|
||||||
select TCG_IBMVTPM if TCG_TPM && PPC64
|
select TCG_IBMVTPM if TCG_TPM && PPC64
|
||||||
|
|
|
@ -39,7 +39,7 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
|
||||||
/* set during initialization */
|
/* set during initialization */
|
||||||
extern int ima_initialized;
|
extern int ima_initialized;
|
||||||
extern int ima_used_chip;
|
extern int ima_used_chip;
|
||||||
extern char *ima_hash;
|
extern int ima_hash_algo;
|
||||||
extern int ima_appraise;
|
extern int ima_appraise;
|
||||||
|
|
||||||
/* IMA inode template definition */
|
/* IMA inode template definition */
|
||||||
|
@ -70,8 +70,9 @@ void ima_fs_cleanup(void);
|
||||||
int ima_inode_alloc(struct inode *inode);
|
int ima_inode_alloc(struct inode *inode);
|
||||||
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
|
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
|
||||||
const char *op, struct inode *inode);
|
const char *op, struct inode *inode);
|
||||||
int ima_calc_file_hash(struct file *file, char *digest);
|
int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash);
|
||||||
int ima_calc_buffer_hash(const void *data, int len, char *digest);
|
int ima_calc_buffer_hash(const void *data, int len,
|
||||||
|
struct ima_digest_data *hash);
|
||||||
int ima_calc_boot_aggregate(char *digest);
|
int ima_calc_boot_aggregate(char *digest);
|
||||||
void ima_add_violation(struct inode *inode, const unsigned char *filename,
|
void ima_add_violation(struct inode *inode, const unsigned char *filename,
|
||||||
const char *op, const char *cause);
|
const char *op, const char *cause);
|
||||||
|
|
|
@ -44,6 +44,7 @@ int ima_store_template(struct ima_template_entry *entry,
|
||||||
const char *op = "add_template_measure";
|
const char *op = "add_template_measure";
|
||||||
const char *audit_cause = "hashing_error";
|
const char *audit_cause = "hashing_error";
|
||||||
int result;
|
int result;
|
||||||
|
struct ima_digest_data hash;
|
||||||
|
|
||||||
memset(entry->digest, 0, sizeof(entry->digest));
|
memset(entry->digest, 0, sizeof(entry->digest));
|
||||||
entry->template_name = IMA_TEMPLATE_NAME;
|
entry->template_name = IMA_TEMPLATE_NAME;
|
||||||
|
@ -51,14 +52,14 @@ int ima_store_template(struct ima_template_entry *entry,
|
||||||
|
|
||||||
if (!violation) {
|
if (!violation) {
|
||||||
result = ima_calc_buffer_hash(&entry->template,
|
result = ima_calc_buffer_hash(&entry->template,
|
||||||
entry->template_len,
|
entry->template_len, &hash);
|
||||||
entry->digest);
|
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
|
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
|
||||||
entry->template_name, op,
|
entry->template_name, op,
|
||||||
audit_cause, result, 0);
|
audit_cause, result, 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
memcpy(entry->digest, hash.digest, hash.length);
|
||||||
}
|
}
|
||||||
result = ima_add_template_entry(entry, violation, op, inode);
|
result = ima_add_template_entry(entry, violation, op, inode);
|
||||||
return result;
|
return result;
|
||||||
|
@ -147,8 +148,9 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||||
if (!(iint->flags & IMA_COLLECTED)) {
|
if (!(iint->flags & IMA_COLLECTED)) {
|
||||||
u64 i_version = file_inode(file)->i_version;
|
u64 i_version = file_inode(file)->i_version;
|
||||||
|
|
||||||
iint->ima_xattr.type = IMA_XATTR_DIGEST;
|
/* use default hash algorithm */
|
||||||
result = ima_calc_file_hash(file, iint->ima_xattr.digest);
|
iint->ima_hash.algo = ima_hash_algo;
|
||||||
|
result = ima_calc_file_hash(file, &iint->ima_hash);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
iint->version = i_version;
|
iint->version = i_version;
|
||||||
iint->flags |= IMA_COLLECTED;
|
iint->flags |= IMA_COLLECTED;
|
||||||
|
@ -196,7 +198,21 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memset(&entry->template, 0, sizeof(entry->template));
|
memset(&entry->template, 0, sizeof(entry->template));
|
||||||
memcpy(entry->template.digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE);
|
if (iint->ima_hash.algo != ima_hash_algo) {
|
||||||
|
struct ima_digest_data hash;
|
||||||
|
|
||||||
|
hash.algo = ima_hash_algo;
|
||||||
|
result = ima_calc_file_hash(file, &hash);
|
||||||
|
if (result)
|
||||||
|
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
|
||||||
|
filename, "collect_data", "failed",
|
||||||
|
result, 0);
|
||||||
|
else
|
||||||
|
memcpy(entry->template.digest, hash.digest,
|
||||||
|
hash.length);
|
||||||
|
} else
|
||||||
|
memcpy(entry->template.digest, iint->ima_hash.digest,
|
||||||
|
iint->ima_hash.length);
|
||||||
strcpy(entry->template.file_name,
|
strcpy(entry->template.file_name,
|
||||||
(strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ?
|
(strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ?
|
||||||
file->f_dentry->d_name.name : filename);
|
file->f_dentry->d_name.name : filename);
|
||||||
|
@ -212,14 +228,14 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||||
const unsigned char *filename)
|
const unsigned char *filename)
|
||||||
{
|
{
|
||||||
struct audit_buffer *ab;
|
struct audit_buffer *ab;
|
||||||
char hash[(IMA_DIGEST_SIZE * 2) + 1];
|
char hash[(iint->ima_hash.length * 2) + 1];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (iint->flags & IMA_AUDITED)
|
if (iint->flags & IMA_AUDITED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < IMA_DIGEST_SIZE; i++)
|
for (i = 0; i < iint->ima_hash.length; i++)
|
||||||
hex_byte_pack(hash + (i * 2), iint->ima_xattr.digest[i]);
|
hex_byte_pack(hash + (i * 2), iint->ima_hash.digest[i]);
|
||||||
hash[i * 2] = '\0';
|
hash[i * 2] = '\0';
|
||||||
|
|
||||||
ab = audit_log_start(current->audit_context, GFP_KERNEL,
|
ab = audit_log_start(current->audit_context, GFP_KERNEL,
|
||||||
|
|
|
@ -43,12 +43,12 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ima_fix_xattr(struct dentry *dentry,
|
static int ima_fix_xattr(struct dentry *dentry,
|
||||||
struct integrity_iint_cache *iint)
|
struct integrity_iint_cache *iint)
|
||||||
{
|
{
|
||||||
iint->ima_xattr.type = IMA_XATTR_DIGEST;
|
iint->ima_hash.type = IMA_XATTR_DIGEST;
|
||||||
return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
|
return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
|
||||||
(u8 *)&iint->ima_xattr,
|
&iint->ima_hash.type,
|
||||||
sizeof(iint->ima_xattr), 0);
|
1 + iint->ima_hash.length, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return specific func appraised cached result */
|
/* Return specific func appraised cached result */
|
||||||
|
@ -159,8 +159,12 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
|
||||||
status = INTEGRITY_FAIL;
|
status = INTEGRITY_FAIL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
rc = memcmp(xattr_value->digest, iint->ima_xattr.digest,
|
if (rc - 1 == iint->ima_hash.length)
|
||||||
IMA_DIGEST_SIZE);
|
rc = memcmp(xattr_value->digest,
|
||||||
|
iint->ima_hash.digest,
|
||||||
|
iint->ima_hash.length);
|
||||||
|
else
|
||||||
|
rc = -EINVAL;
|
||||||
if (rc) {
|
if (rc) {
|
||||||
cause = "invalid-hash";
|
cause = "invalid-hash";
|
||||||
status = INTEGRITY_FAIL;
|
status = INTEGRITY_FAIL;
|
||||||
|
@ -172,8 +176,8 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
|
||||||
iint->flags |= IMA_DIGSIG;
|
iint->flags |= IMA_DIGSIG;
|
||||||
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
|
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
|
||||||
xattr_value->digest, rc - 1,
|
xattr_value->digest, rc - 1,
|
||||||
iint->ima_xattr.digest,
|
iint->ima_hash.digest,
|
||||||
IMA_DIGEST_SIZE);
|
iint->ima_hash.length);
|
||||||
if (rc == -EOPNOTSUPP) {
|
if (rc == -EOPNOTSUPP) {
|
||||||
status = INTEGRITY_UNKNOWN;
|
status = INTEGRITY_UNKNOWN;
|
||||||
} else if (rc) {
|
} else if (rc) {
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <crypto/hash.h>
|
#include <crypto/hash.h>
|
||||||
|
#include <crypto/hash_info.h>
|
||||||
#include "ima.h"
|
#include "ima.h"
|
||||||
|
|
||||||
static struct crypto_shash *ima_shash_tfm;
|
static struct crypto_shash *ima_shash_tfm;
|
||||||
|
@ -28,10 +29,11 @@ int ima_init_crypto(void)
|
||||||
{
|
{
|
||||||
long rc;
|
long rc;
|
||||||
|
|
||||||
ima_shash_tfm = crypto_alloc_shash(ima_hash, 0, 0);
|
ima_shash_tfm = crypto_alloc_shash(hash_algo_name[ima_hash_algo], 0, 0);
|
||||||
if (IS_ERR(ima_shash_tfm)) {
|
if (IS_ERR(ima_shash_tfm)) {
|
||||||
rc = PTR_ERR(ima_shash_tfm);
|
rc = PTR_ERR(ima_shash_tfm);
|
||||||
pr_err("Can not allocate %s (reason: %ld)\n", ima_hash, rc);
|
pr_err("Can not allocate %s (reason: %ld)\n",
|
||||||
|
hash_algo_name[ima_hash_algo], rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -40,17 +42,19 @@ int ima_init_crypto(void)
|
||||||
/*
|
/*
|
||||||
* Calculate the MD5/SHA1 file digest
|
* Calculate the MD5/SHA1 file digest
|
||||||
*/
|
*/
|
||||||
int ima_calc_file_hash(struct file *file, char *digest)
|
static int ima_calc_file_hash_tfm(struct file *file,
|
||||||
|
struct ima_digest_data *hash,
|
||||||
|
struct crypto_shash *tfm)
|
||||||
{
|
{
|
||||||
loff_t i_size, offset = 0;
|
loff_t i_size, offset = 0;
|
||||||
char *rbuf;
|
char *rbuf;
|
||||||
int rc, read = 0;
|
int rc, read = 0;
|
||||||
struct {
|
struct {
|
||||||
struct shash_desc shash;
|
struct shash_desc shash;
|
||||||
char ctx[crypto_shash_descsize(ima_shash_tfm)];
|
char ctx[crypto_shash_descsize(tfm)];
|
||||||
} desc;
|
} desc;
|
||||||
|
|
||||||
desc.shash.tfm = ima_shash_tfm;
|
desc.shash.tfm = tfm;
|
||||||
desc.shash.flags = 0;
|
desc.shash.flags = 0;
|
||||||
|
|
||||||
rc = crypto_shash_init(&desc.shash);
|
rc = crypto_shash_init(&desc.shash);
|
||||||
|
@ -85,17 +89,42 @@ int ima_calc_file_hash(struct file *file, char *digest)
|
||||||
}
|
}
|
||||||
kfree(rbuf);
|
kfree(rbuf);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
rc = crypto_shash_final(&desc.shash, digest);
|
rc = crypto_shash_final(&desc.shash, hash->digest);
|
||||||
if (read)
|
if (read)
|
||||||
file->f_mode &= ~FMODE_READ;
|
file->f_mode &= ~FMODE_READ;
|
||||||
out:
|
out:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
|
||||||
|
{
|
||||||
|
struct crypto_shash *tfm = ima_shash_tfm;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (hash->algo != ima_hash_algo && hash->algo < HASH_ALGO__LAST) {
|
||||||
|
tfm = crypto_alloc_shash(hash_algo_name[hash->algo], 0, 0);
|
||||||
|
if (IS_ERR(tfm)) {
|
||||||
|
rc = PTR_ERR(tfm);
|
||||||
|
pr_err("Can not allocate %s (reason: %d)\n",
|
||||||
|
hash_algo_name[hash->algo], rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hash->length = crypto_shash_digestsize(tfm);
|
||||||
|
|
||||||
|
rc = ima_calc_file_hash_tfm(file, hash, tfm);
|
||||||
|
|
||||||
|
if (tfm != ima_shash_tfm)
|
||||||
|
crypto_free_shash(tfm);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate the hash of a given buffer
|
* Calculate the hash of a given buffer
|
||||||
*/
|
*/
|
||||||
int ima_calc_buffer_hash(const void *data, int len, char *digest)
|
int ima_calc_buffer_hash(const void *buf, int len, struct ima_digest_data *hash)
|
||||||
{
|
{
|
||||||
struct {
|
struct {
|
||||||
struct shash_desc shash;
|
struct shash_desc shash;
|
||||||
|
@ -105,7 +134,11 @@ int ima_calc_buffer_hash(const void *data, int len, char *digest)
|
||||||
desc.shash.tfm = ima_shash_tfm;
|
desc.shash.tfm = ima_shash_tfm;
|
||||||
desc.shash.flags = 0;
|
desc.shash.flags = 0;
|
||||||
|
|
||||||
return crypto_shash_digest(&desc.shash, data, len, digest);
|
/* this function uses default algo */
|
||||||
|
hash->algo = ima_hash_algo;
|
||||||
|
hash->length = crypto_shash_digestsize(ima_shash_tfm);
|
||||||
|
|
||||||
|
return crypto_shash_digest(&desc.shash, buf, len, hash->digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init ima_pcrread(int idx, u8 *pcr)
|
static void __init ima_pcrread(int idx, u8 *pcr)
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/xattr.h>
|
#include <linux/xattr.h>
|
||||||
#include <linux/ima.h>
|
#include <linux/ima.h>
|
||||||
|
#include <crypto/hash_info.h>
|
||||||
|
|
||||||
#include "ima.h"
|
#include "ima.h"
|
||||||
|
|
||||||
|
@ -35,11 +36,12 @@ int ima_appraise = IMA_APPRAISE_ENFORCE;
|
||||||
int ima_appraise;
|
int ima_appraise;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char *ima_hash = "sha1";
|
int ima_hash_algo = HASH_ALGO_SHA1;
|
||||||
|
|
||||||
static int __init hash_setup(char *str)
|
static int __init hash_setup(char *str)
|
||||||
{
|
{
|
||||||
if (strncmp(str, "md5", 3) == 0)
|
if (strncmp(str, "md5", 3) == 0)
|
||||||
ima_hash = "md5";
|
ima_hash_algo = HASH_ALGO_MD5;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
__setup("ima_hash=", hash_setup);
|
__setup("ima_hash=", hash_setup);
|
||||||
|
|
|
@ -59,20 +59,29 @@ enum evm_ima_xattr_type {
|
||||||
struct evm_ima_xattr_data {
|
struct evm_ima_xattr_data {
|
||||||
u8 type;
|
u8 type;
|
||||||
u8 digest[SHA1_DIGEST_SIZE];
|
u8 digest[SHA1_DIGEST_SIZE];
|
||||||
} __attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
|
#define IMA_MAX_DIGEST_SIZE 64
|
||||||
|
|
||||||
|
struct ima_digest_data {
|
||||||
|
u8 algo;
|
||||||
|
u8 length;
|
||||||
|
u8 type;
|
||||||
|
u8 digest[IMA_MAX_DIGEST_SIZE];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/* integrity data associated with an inode */
|
/* integrity data associated with an inode */
|
||||||
struct integrity_iint_cache {
|
struct integrity_iint_cache {
|
||||||
struct rb_node rb_node; /* rooted in integrity_iint_tree */
|
struct rb_node rb_node; /* rooted in integrity_iint_tree */
|
||||||
struct inode *inode; /* back pointer to inode in question */
|
struct inode *inode; /* back pointer to inode in question */
|
||||||
u64 version; /* track inode changes */
|
u64 version; /* track inode changes */
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct evm_ima_xattr_data ima_xattr;
|
|
||||||
enum integrity_status ima_file_status:4;
|
enum integrity_status ima_file_status:4;
|
||||||
enum integrity_status ima_mmap_status:4;
|
enum integrity_status ima_mmap_status:4;
|
||||||
enum integrity_status ima_bprm_status:4;
|
enum integrity_status ima_bprm_status:4;
|
||||||
enum integrity_status ima_module_status:4;
|
enum integrity_status ima_module_status:4;
|
||||||
enum integrity_status evm_status:4;
|
enum integrity_status evm_status:4;
|
||||||
|
struct ima_digest_data ima_hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* rbtree tree calls to lookup, insert, delete
|
/* rbtree tree calls to lookup, insert, delete
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue