Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security

Pull security subsystem updates from James Morris:
 "This is basically a maintenance update for the TPM driver and EVM/IMA"

Fix up conflicts in lib/digsig.c and security/integrity/ima/ima_main.c

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (45 commits)
  tpm/ibmvtpm: build only when IBM pseries is configured
  ima: digital signature verification using asymmetric keys
  ima: rename hash calculation functions
  ima: use new crypto_shash API instead of old crypto_hash
  ima: add policy support for file system uuid
  evm: add file system uuid to EVM hmac
  tpm_tis: check pnp_acpi_device return code
  char/tpm/tpm_i2c_stm_st33: drop temporary variable for return value
  char/tpm/tpm_i2c_stm_st33: remove dead assignment in tpm_st33_i2c_probe
  char/tpm/tpm_i2c_stm_st33: Remove __devexit attribute
  char/tpm/tpm_i2c_stm_st33: Don't use memcpy for one byte assignment
  tpm_i2c_stm_st33: removed unused variables/code
  TPM: Wait for TPM_ACCESS tpmRegValidSts to go high at startup
  tpm: Fix cancellation of TPM commands (interrupt mode)
  tpm: Fix cancellation of TPM commands (polling mode)
  tpm: Store TPM vendor ID
  TPM: Work around buggy TPMs that block during continue self test
  tpm_i2c_stm_st33: fix oops when i2c client is unavailable
  char/tpm: Use struct dev_pm_ops for power management
  TPM: STMicroelectronics ST33 I2C BUILD STUFF
  ...
This commit is contained in:
Linus Torvalds
2013-02-21 08:18:12 -08:00
35 changed files with 1918 additions and 312 deletions

View File

@@ -17,5 +17,17 @@ config INTEGRITY_SIGNATURE
This is useful for evm and module keyrings, when keys are
usually only added from initramfs.
config INTEGRITY_ASYMMETRIC_KEYS
boolean "Enable asymmetric keys support"
depends on INTEGRITY_SIGNATURE
default n
select ASYMMETRIC_KEY_TYPE
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
select PUBLIC_KEY_ALGO_RSA
select X509_CERTIFICATE_PARSER
help
This option enables digital signature verification using
asymmetric keys.
source security/integrity/ima/Kconfig
source security/integrity/evm/Kconfig

View File

@@ -4,6 +4,7 @@
obj-$(CONFIG_INTEGRITY) += integrity.o
obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
integrity-y := iint.o

View File

@@ -44,5 +44,14 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
}
}
return digsig_verify(keyring[id], sig, siglen, digest, digestlen);
switch (sig[0]) {
case 1:
return digsig_verify(keyring[id], sig, siglen,
digest, digestlen);
case 2:
return asymmetric_verify(keyring[id], sig, siglen,
digest, digestlen);
}
return -EOPNOTSUPP;
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2013 Intel Corporation
*
* Author:
* Dmitry Kasatkin <dmitry.kasatkin@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/err.h>
#include <linux/key-type.h>
#include <crypto/public_key.h>
#include <keys/asymmetric-type.h>
#include "integrity.h"
/*
* signature format v2 - for using with asymmetric keys
*/
struct signature_v2_hdr {
uint8_t version; /* signature format version */
uint8_t hash_algo; /* Digest algorithm [enum pkey_hash_algo] */
uint32_t keyid; /* IMA key identifier - not X509/PGP specific*/
uint16_t sig_size; /* signature size */
uint8_t sig[0]; /* signature payload */
} __packed;
/*
* Request an asymmetric key.
*/
static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
{
struct key *key;
char name[12];
sprintf(name, "id:%x", keyid);
pr_debug("key search: \"%s\"\n", name);
if (keyring) {
/* search in specific keyring */
key_ref_t kref;
kref = keyring_search(make_key_ref(keyring, 1),
&key_type_asymmetric, name);
if (IS_ERR(kref))
key = ERR_CAST(kref);
else
key = key_ref_to_ptr(kref);
} else {
key = request_key(&key_type_asymmetric, name, NULL);
}
if (IS_ERR(key)) {
pr_warn("Request for unknown key '%s' err %ld\n",
name, PTR_ERR(key));
switch (PTR_ERR(key)) {
/* Hide some search errors */
case -EACCES:
case -ENOTDIR:
case -EAGAIN:
return ERR_PTR(-ENOKEY);
default:
return key;
}
}
pr_debug("%s() = 0 [%x]\n", __func__, key_serial(key));
return key;
}
int asymmetric_verify(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen)
{
struct public_key_signature pks;
struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
struct key *key;
int ret = -ENOMEM;
if (siglen <= sizeof(*hdr))
return -EBADMSG;
siglen -= sizeof(*hdr);
if (siglen != __be16_to_cpu(hdr->sig_size))
return -EBADMSG;
if (hdr->hash_algo >= PKEY_HASH__LAST)
return -ENOPKG;
key = request_asymmetric_key(keyring, __be32_to_cpu(hdr->keyid));
if (IS_ERR(key))
return PTR_ERR(key);
memset(&pks, 0, sizeof(pks));
pks.pkey_hash_algo = hdr->hash_algo;
pks.digest = (u8 *)data;
pks.digest_size = datalen;
pks.nr_mpi = 1;
pks.rsa.s = mpi_read_raw_data(hdr->sig, siglen);
if (pks.rsa.s)
ret = verify_signature(key, &pks);
mpi_free(pks.rsa.s);
key_put(key);
pr_debug("%s() = %d\n", __func__, ret);
return ret;
}

View File

@@ -11,3 +11,16 @@ config EVM
integrity attacks.
If you are unsure how to answer this question, answer N.
config EVM_HMAC_VERSION
int "EVM HMAC version"
depends on EVM
default 2
help
This options adds EVM HMAC version support.
1 - original version
2 - add per filesystem unique identifier (UUID) (default)
WARNING: changing the HMAC calculation method or adding
additional info to the calculation, requires existing EVM
labeled file systems to be relabeled.

View File

@@ -24,6 +24,7 @@
extern int evm_initialized;
extern char *evm_hmac;
extern char *evm_hash;
extern int evm_hmac_version;
extern struct crypto_shash *hmac_tfm;
extern struct crypto_shash *hash_tfm;
@@ -45,6 +46,5 @@ extern int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
char *hmac_val);
extern int evm_init_secfs(void);
extern void evm_cleanup_secfs(void);
#endif

View File

@@ -110,6 +110,9 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
hmac_misc.mode = inode->i_mode;
crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc);
if (evm_hmac_version > 1)
crypto_shash_update(desc, inode->i_sb->s_uuid,
sizeof(inode->i_sb->s_uuid));
crypto_shash_final(desc, digest);
}

View File

@@ -26,6 +26,7 @@ int evm_initialized;
char *evm_hmac = "hmac(sha1)";
char *evm_hash = "sha1";
int evm_hmac_version = CONFIG_EVM_HMAC_VERSION;
char *evm_config_xattrnames[] = {
#ifdef CONFIG_SECURITY_SELINUX
@@ -427,15 +428,6 @@ err:
return error;
}
static void __exit cleanup_evm(void)
{
evm_cleanup_secfs();
if (hmac_tfm)
crypto_free_shash(hmac_tfm);
if (hash_tfm)
crypto_free_shash(hash_tfm);
}
/*
* evm_display_config - list the EVM protected security extended attributes
*/

View File

@@ -100,9 +100,3 @@ int __init evm_init_secfs(void)
error = -EFAULT;
return error;
}
void __exit evm_cleanup_secfs(void)
{
if (evm_init_tpm)
securityfs_remove(evm_init_tpm);
}

View File

@@ -72,7 +72,10 @@ static void iint_free(struct integrity_iint_cache *iint)
{
iint->version = 0;
iint->flags = 0UL;
iint->ima_status = INTEGRITY_UNKNOWN;
iint->ima_file_status = INTEGRITY_UNKNOWN;
iint->ima_mmap_status = INTEGRITY_UNKNOWN;
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
iint->ima_module_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
kmem_cache_free(iint_cache, iint);
}
@@ -149,7 +152,10 @@ static void init_once(void *foo)
memset(iint, 0, sizeof *iint);
iint->version = 0;
iint->flags = 0UL;
iint->ima_status = INTEGRITY_UNKNOWN;
iint->ima_file_status = INTEGRITY_UNKNOWN;
iint->ima_mmap_status = INTEGRITY_UNKNOWN;
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
iint->ima_module_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
}

View File

@@ -84,11 +84,12 @@ void ima_fs_cleanup(void);
int ima_inode_alloc(struct inode *inode);
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
const char *op, struct inode *inode);
int ima_calc_hash(struct file *file, char *digest);
int ima_calc_template_hash(int template_len, void *template, char *digest);
int ima_calc_file_hash(struct file *file, char *digest);
int ima_calc_buffer_hash(const void *data, int len, char *digest);
int ima_calc_boot_aggregate(char *digest);
void ima_add_violation(struct inode *inode, const unsigned char *filename,
const char *op, const char *cause);
int ima_init_crypto(void);
/*
* used to protect h_table and sha_table
@@ -119,6 +120,7 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
int ima_store_template(struct ima_template_entry *entry, int violation,
struct inode *inode);
void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
const char *ima_d_path(struct path *path, char **pathbuf);
/* rbtree tree calls to lookup, insert, delete
* integrity data associated with an inode.
@@ -127,7 +129,7 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
/* IMA policy related functions */
enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, MODULE_CHECK, POST_SETATTR };
enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, POST_SETATTR };
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
int flags);
@@ -142,13 +144,16 @@ void ima_delete_rules(void);
#define IMA_APPRAISE_MODULES 0x04
#ifdef CONFIG_IMA_APPRAISE
int ima_appraise_measurement(struct integrity_iint_cache *iint,
int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename);
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
int func);
#else
static inline int ima_appraise_measurement(struct integrity_iint_cache *iint,
static inline int ima_appraise_measurement(int func,
struct integrity_iint_cache *iint,
struct file *file,
const unsigned char *filename)
{
@@ -165,6 +170,12 @@ static inline void ima_update_xattr(struct integrity_iint_cache *iint,
struct file *file)
{
}
static inline enum integrity_status ima_get_cache_status(struct integrity_iint_cache
*iint, int func)
{
return INTEGRITY_UNKNOWN;
}
#endif
/* LSM based policy rules require audit */

View File

@@ -50,8 +50,8 @@ int ima_store_template(struct ima_template_entry *entry,
entry->template_len = sizeof(entry->template);
if (!violation) {
result = ima_calc_template_hash(entry->template_len,
&entry->template,
result = ima_calc_buffer_hash(&entry->template,
entry->template_len,
entry->digest);
if (result < 0) {
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
@@ -100,12 +100,12 @@ err_out:
* ima_get_action - appraise & measure decision based on policy.
* @inode: pointer to inode to measure
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
* @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP, MODULE_CHECK)
* @function: calling function (FILE_CHECK, BPRM_CHECK, MMAP_CHECK, MODULE_CHECK)
*
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
* subj,obj, and type: are LSM specific.
* func: FILE_CHECK | BPRM_CHECK | FILE_MMAP | MODULE_CHECK
* func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK
* mask: contains the permission mask
* fsmagic: hex value
*
@@ -148,7 +148,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
u64 i_version = file->f_dentry->d_inode->i_version;
iint->ima_xattr.type = IMA_XATTR_DIGEST;
result = ima_calc_hash(file, iint->ima_xattr.digest);
result = ima_calc_file_hash(file, iint->ima_xattr.digest);
if (!result) {
iint->version = i_version;
iint->flags |= IMA_COLLECTED;
@@ -237,3 +237,20 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
iint->flags |= IMA_AUDITED;
}
const char *ima_d_path(struct path *path, char **pathbuf)
{
char *pathname = NULL;
/* We will allow 11 spaces for ' (deleted)' to be appended */
*pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
if (*pathbuf) {
pathname = d_path(path, *pathbuf, PATH_MAX + 11);
if (IS_ERR(pathname)) {
kfree(*pathbuf);
*pathbuf = NULL;
pathname = NULL;
}
}
return pathname;
}

View File

@@ -42,12 +42,69 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
return ima_match_policy(inode, func, mask, IMA_APPRAISE);
}
static void ima_fix_xattr(struct dentry *dentry,
static int ima_fix_xattr(struct dentry *dentry,
struct integrity_iint_cache *iint)
{
iint->ima_xattr.type = IMA_XATTR_DIGEST;
__vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, (u8 *)&iint->ima_xattr,
sizeof iint->ima_xattr, 0);
return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
(u8 *)&iint->ima_xattr,
sizeof(iint->ima_xattr), 0);
}
/* Return specific func appraised cached result */
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
int func)
{
switch(func) {
case MMAP_CHECK:
return iint->ima_mmap_status;
case BPRM_CHECK:
return iint->ima_bprm_status;
case MODULE_CHECK:
return iint->ima_module_status;
case FILE_CHECK:
default:
return iint->ima_file_status;
}
}
static void ima_set_cache_status(struct integrity_iint_cache *iint,
int func, enum integrity_status status)
{
switch(func) {
case MMAP_CHECK:
iint->ima_mmap_status = status;
break;
case BPRM_CHECK:
iint->ima_bprm_status = status;
break;
case MODULE_CHECK:
iint->ima_module_status = status;
break;
case FILE_CHECK:
default:
iint->ima_file_status = status;
break;
}
}
static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
{
switch(func) {
case MMAP_CHECK:
iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);
break;
case BPRM_CHECK:
iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED);
break;
case MODULE_CHECK:
iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED);
break;
case FILE_CHECK:
default:
iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
break;
}
}
/*
@@ -58,7 +115,7 @@ static void ima_fix_xattr(struct dentry *dentry,
*
* Return 0 on success, error code otherwise
*/
int ima_appraise_measurement(struct integrity_iint_cache *iint,
int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename)
{
struct dentry *dentry = file->f_dentry;
@@ -74,9 +131,6 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint,
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) {
@@ -98,19 +152,18 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint,
cause = "invalid-HMAC";
goto out;
}
switch (xattr_value->type) {
case IMA_XATTR_DIGEST:
if (iint->flags & IMA_DIGSIG_REQUIRED) {
cause = "IMA signature required";
status = INTEGRITY_FAIL;
break;
}
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;
@@ -141,15 +194,15 @@ out:
if ((ima_appraise & IMA_APPRAISE_FIX) &&
(!xattr_value ||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
ima_fix_xattr(dentry, iint);
status = INTEGRITY_PASS;
if (!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;
ima_cache_flags(iint, func);
}
iint->ima_status = status;
ima_set_cache_status(iint, func, status);
kfree(xattr_value);
return status;
}
@@ -195,10 +248,11 @@ void ima_inode_post_setattr(struct dentry *dentry)
must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
iint = integrity_iint_find(inode);
if (iint) {
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
IMA_ACTION_FLAGS);
if (must_appraise)
iint->flags |= IMA_APPRAISE;
else
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED);
}
if (!must_appraise)
rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);

View File

@@ -19,38 +19,41 @@
#include <linux/scatterlist.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <crypto/hash.h>
#include "ima.h"
static int init_desc(struct hash_desc *desc)
{
int rc;
static struct crypto_shash *ima_shash_tfm;
desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(desc->tfm)) {
pr_info("IMA: failed to load %s transform: %ld\n",
ima_hash, PTR_ERR(desc->tfm));
rc = PTR_ERR(desc->tfm);
int ima_init_crypto(void)
{
long rc;
ima_shash_tfm = crypto_alloc_shash(ima_hash, 0, 0);
if (IS_ERR(ima_shash_tfm)) {
rc = PTR_ERR(ima_shash_tfm);
pr_err("Can not allocate %s (reason: %ld)\n", ima_hash, rc);
return rc;
}
desc->flags = 0;
rc = crypto_hash_init(desc);
if (rc)
crypto_free_hash(desc->tfm);
return rc;
return 0;
}
/*
* Calculate the MD5/SHA1 file digest
*/
int ima_calc_hash(struct file *file, char *digest)
int ima_calc_file_hash(struct file *file, char *digest)
{
struct hash_desc desc;
struct scatterlist sg[1];
loff_t i_size, offset = 0;
char *rbuf;
int rc, read = 0;
struct {
struct shash_desc shash;
char ctx[crypto_shash_descsize(ima_shash_tfm)];
} desc;
rc = init_desc(&desc);
desc.shash.tfm = ima_shash_tfm;
desc.shash.flags = 0;
rc = crypto_shash_init(&desc.shash);
if (rc != 0)
return rc;
@@ -75,41 +78,34 @@ int ima_calc_hash(struct file *file, char *digest)
if (rbuf_len == 0)
break;
offset += rbuf_len;
sg_init_one(sg, rbuf, rbuf_len);
rc = crypto_hash_update(&desc, sg, rbuf_len);
rc = crypto_shash_update(&desc.shash, rbuf, rbuf_len);
if (rc)
break;
}
kfree(rbuf);
if (!rc)
rc = crypto_hash_final(&desc, digest);
rc = crypto_shash_final(&desc.shash, digest);
if (read)
file->f_mode &= ~FMODE_READ;
out:
crypto_free_hash(desc.tfm);
return rc;
}
/*
* Calculate the hash of a given template
* Calculate the hash of a given buffer
*/
int ima_calc_template_hash(int template_len, void *template, char *digest)
int ima_calc_buffer_hash(const void *data, int len, char *digest)
{
struct hash_desc desc;
struct scatterlist sg[1];
int rc;
struct {
struct shash_desc shash;
char ctx[crypto_shash_descsize(ima_shash_tfm)];
} desc;
rc = init_desc(&desc);
if (rc != 0)
return rc;
desc.shash.tfm = ima_shash_tfm;
desc.shash.flags = 0;
sg_init_one(sg, template, template_len);
rc = crypto_hash_update(&desc, sg, template_len);
if (!rc)
rc = crypto_hash_final(&desc, digest);
crypto_free_hash(desc.tfm);
return rc;
return crypto_shash_digest(&desc.shash, data, len, digest);
}
static void __init ima_pcrread(int idx, u8 *pcr)
@@ -126,12 +122,17 @@ static void __init ima_pcrread(int idx, u8 *pcr)
*/
int __init ima_calc_boot_aggregate(char *digest)
{
struct hash_desc desc;
struct scatterlist sg;
u8 pcr_i[IMA_DIGEST_SIZE];
int rc, i;
struct {
struct shash_desc shash;
char ctx[crypto_shash_descsize(ima_shash_tfm)];
} desc;
rc = init_desc(&desc);
desc.shash.tfm = ima_shash_tfm;
desc.shash.flags = 0;
rc = crypto_shash_init(&desc.shash);
if (rc != 0)
return rc;
@@ -139,11 +140,9 @@ int __init ima_calc_boot_aggregate(char *digest)
for (i = TPM_PCR0; i < TPM_PCR8; i++) {
ima_pcrread(i, pcr_i);
/* now accumulate with current aggregate */
sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE);
rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
rc = crypto_shash_update(&desc.shash, pcr_i, IMA_DIGEST_SIZE);
}
if (!rc)
crypto_hash_final(&desc, digest);
crypto_free_hash(desc.tfm);
crypto_shash_final(&desc.shash, digest);
return rc;
}

View File

@@ -85,6 +85,9 @@ int __init ima_init(void)
if (!ima_used_chip)
pr_info("IMA: No TPM chip found, activating TPM-bypass!\n");
rc = ima_init_crypto();
if (rc)
return rc;
ima_add_boot_aggregate(); /* boot aggregate must be first entry */
ima_init_policy();

View File

@@ -61,7 +61,8 @@ static void ima_rdwr_violation_check(struct file *file)
fmode_t mode = file->f_mode;
int must_measure;
bool send_tomtou = false, send_writers = false;
unsigned char *pathname = NULL, *pathbuf = NULL;
char *pathbuf = NULL;
const char *pathname;
if (!S_ISREG(inode->i_mode) || !ima_initialized)
return;
@@ -86,22 +87,15 @@ out:
if (!send_tomtou && !send_writers)
return;
/* We will allow 11 spaces for ' (deleted)' to be appended */
pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
if (pathbuf) {
pathname = d_path(&file->f_path, pathbuf, PATH_MAX + 11);
if (IS_ERR(pathname))
pathname = NULL;
else if (strlen(pathname) > IMA_EVENT_NAME_LEN_MAX)
pathname = NULL;
}
pathname = ima_d_path(&file->f_path, &pathbuf);
if (!pathname || strlen(pathname) > IMA_EVENT_NAME_LEN_MAX)
pathname = dentry->d_name.name;
if (send_tomtou)
ima_add_violation(inode,
!pathname ? dentry->d_name.name : pathname,
ima_add_violation(inode, pathname,
"invalid_pcr", "ToMToU");
if (send_writers)
ima_add_violation(inode,
!pathname ? dentry->d_name.name : pathname,
ima_add_violation(inode, pathname,
"invalid_pcr", "open_writers");
kfree(pathbuf);
}
@@ -145,25 +139,31 @@ void ima_file_free(struct file *file)
ima_check_last_writer(iint, inode, file);
}
static int process_measurement(struct file *file, const unsigned char *filename,
static int process_measurement(struct file *file, const char *filename,
int mask, int function)
{
struct inode *inode = file->f_dentry->d_inode;
struct integrity_iint_cache *iint;
unsigned char *pathname = NULL, *pathbuf = NULL;
int rc = -ENOMEM, action, must_appraise;
char *pathbuf = NULL;
const char *pathname = NULL;
int rc = -ENOMEM, action, must_appraise, _func;
if (!ima_initialized || !S_ISREG(inode->i_mode))
return 0;
/* Determine if in appraise/audit/measurement policy,
* returns IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT bitmask. */
/* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
* bitmask based on the appraise/audit/measurement policy.
* Included is the appraise submask.
*/
action = ima_get_action(inode, mask, function);
if (!action)
return 0;
must_appraise = action & IMA_APPRAISE;
/* Is the appraise rule hook specific? */
_func = (action & IMA_FILE_APPRAISE) ? FILE_CHECK : function;
mutex_lock(&inode->i_mutex);
iint = integrity_inode_get(inode);
@@ -171,44 +171,45 @@ static int process_measurement(struct file *file, const unsigned char *filename,
goto out;
/* Determine if already appraised/measured based on bitmask
* (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED,
* IMA_AUDIT, IMA_AUDITED) */
* (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
* IMA_AUDIT, IMA_AUDITED)
*/
iint->flags |= action;
action &= IMA_DO_MASK;
action &= ~((iint->flags & IMA_DONE_MASK) >> 1);
/* Nothing to do, just return existing appraised status */
if (!action) {
if (iint->flags & IMA_APPRAISED)
rc = iint->ima_status;
goto out;
if (must_appraise)
rc = ima_get_cache_status(iint, _func);
goto out_digsig;
}
rc = ima_collect_measurement(iint, file);
if (rc != 0)
goto out;
goto out_digsig;
if (function != BPRM_CHECK)
pathname = ima_d_path(&file->f_path, &pathbuf);
if (!pathname)
pathname = filename;
if (function != BPRM_CHECK) {
/* We will allow 11 spaces for ' (deleted)' to be appended */
pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
if (pathbuf) {
pathname =
d_path(&file->f_path, pathbuf, PATH_MAX + 11);
if (IS_ERR(pathname))
pathname = NULL;
}
}
if (action & IMA_MEASURE)
ima_store_measurement(iint, file,
!pathname ? filename : pathname);
if (action & IMA_APPRAISE)
rc = ima_appraise_measurement(iint, file,
!pathname ? filename : pathname);
ima_store_measurement(iint, file, pathname);
if (action & IMA_APPRAISE_SUBMASK)
rc = ima_appraise_measurement(_func, iint, file, pathname);
if (action & IMA_AUDIT)
ima_audit_measurement(iint, !pathname ? filename : pathname);
ima_audit_measurement(iint, pathname);
kfree(pathbuf);
out_digsig:
if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG))
rc = -EACCES;
out:
mutex_unlock(&inode->i_mutex);
return (rc && must_appraise) ? -EACCES : 0;
if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
return -EACCES;
return 0;
}
/**
@@ -219,19 +220,15 @@ out:
* Measure files being mmapped executable based on the ima_must_measure()
* policy decision.
*
* Return 0 on success, an error code on failure.
* (Based on the results of appraise_measurement().)
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
int ima_file_mmap(struct file *file, unsigned long prot)
{
int rc = 0;
if (!file)
return 0;
if (prot & PROT_EXEC)
rc = process_measurement(file, file->f_dentry->d_name.name,
MAY_EXEC, FILE_MMAP);
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
if (file && (prot & PROT_EXEC))
return process_measurement(file, file->f_dentry->d_name.name,
MAY_EXEC, MMAP_CHECK);
return 0;
}
/**
@@ -244,18 +241,15 @@ int ima_file_mmap(struct file *file, unsigned long prot)
* So we can be certain that what we verify and measure here is actually
* what is being executed.
*
* Return 0 on success, an error code on failure.
* (Based on the results of appraise_measurement().)
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
int ima_bprm_check(struct linux_binprm *bprm)
{
int rc;
rc = process_measurement(bprm->file,
return process_measurement(bprm->file,
(strcmp(bprm->filename, bprm->interp) == 0) ?
bprm->filename : bprm->interp,
MAY_EXEC, BPRM_CHECK);
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
}
/**
@@ -265,18 +259,15 @@ int ima_bprm_check(struct linux_binprm *bprm)
*
* Measure files based on the ima_must_measure() policy decision.
*
* Always return 0 and audit dentry_open failures.
* (Return code will be based upon measurement appraisal.)
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
int ima_file_check(struct file *file, int mask)
{
int rc;
ima_rdwr_violation_check(file);
rc = process_measurement(file, file->f_dentry->d_name.name,
return process_measurement(file, file->f_dentry->d_name.name,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
FILE_CHECK);
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
}
EXPORT_SYMBOL_GPL(ima_file_check);
@@ -286,23 +277,20 @@ EXPORT_SYMBOL_GPL(ima_file_check);
*
* Measure/appraise kernel modules based on policy.
*
* Always return 0 and audit dentry_open failures.
* Return code is based upon measurement appraisal.
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
int ima_module_check(struct file *file)
{
int rc = 0;
if (!file) {
if (ima_appraise & IMA_APPRAISE_MODULES) {
#ifndef CONFIG_MODULE_SIG_FORCE
rc = -EACCES; /* INTEGRITY_UNKNOWN */
if (ima_appraise & IMA_APPRAISE_MODULES)
return -EACCES; /* INTEGRITY_UNKNOWN */
#endif
}
} else
rc = process_measurement(file, file->f_dentry->d_name.name,
MAY_EXEC, MODULE_CHECK);
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
return 0; /* We rely on module signature checking */
}
return process_measurement(file, file->f_dentry->d_name.name,
MAY_EXEC, MODULE_CHECK);
}
static int __init init_ima(void)

View File

@@ -16,6 +16,7 @@
#include <linux/magic.h>
#include <linux/parser.h>
#include <linux/slab.h>
#include <linux/genhd.h>
#include "ima.h"
@@ -25,6 +26,7 @@
#define IMA_FSMAGIC 0x0004
#define IMA_UID 0x0008
#define IMA_FOWNER 0x0010
#define IMA_FSUUID 0x0020
#define UNKNOWN 0
#define MEASURE 0x0001 /* same as IMA_MEASURE */
@@ -45,10 +47,12 @@ struct ima_rule_entry {
enum ima_hooks func;
int mask;
unsigned long fsmagic;
u8 fsuuid[16];
kuid_t uid;
kuid_t fowner;
struct {
void *rule; /* LSM file metadata specific */
void *args_p; /* audit value */
int type; /* audit type */
} lsm[MAX_LSM_RULES];
};
@@ -74,7 +78,7 @@ static struct ima_rule_entry default_rules[] = {
{.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
{.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC},
{.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
{.action = MEASURE,.func = MMAP_CHECK,.mask = MAY_EXEC,
.flags = IMA_FUNC | IMA_MASK},
{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
.flags = IMA_FUNC | IMA_MASK},
@@ -119,6 +123,35 @@ static int __init default_appraise_policy_setup(char *str)
}
__setup("ima_appraise_tcb", default_appraise_policy_setup);
/*
* Although the IMA policy does not change, the LSM policy can be
* reloaded, leaving the IMA LSM based rules referring to the old,
* stale LSM policy.
*
* Update the IMA LSM based rules to reflect the reloaded LSM policy.
* We assume the rules still exist; and BUG_ON() if they don't.
*/
static void ima_lsm_update_rules(void)
{
struct ima_rule_entry *entry, *tmp;
int result;
int i;
mutex_lock(&ima_rules_mutex);
list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
for (i = 0; i < MAX_LSM_RULES; i++) {
if (!entry->lsm[i].rule)
continue;
result = security_filter_rule_init(entry->lsm[i].type,
Audit_equal,
entry->lsm[i].args_p,
&entry->lsm[i].rule);
BUG_ON(!entry->lsm[i].rule);
}
}
mutex_unlock(&ima_rules_mutex);
}
/**
* ima_match_rules - determine whether an inode matches the measure rule.
* @rule: a pointer to a rule
@@ -142,6 +175,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
if ((rule->flags & IMA_FSMAGIC)
&& rule->fsmagic != inode->i_sb->s_magic)
return false;
if ((rule->flags & IMA_FSUUID) &&
memcmp(rule->fsuuid, inode->i_sb->s_uuid, sizeof(rule->fsuuid)))
return false;
if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid))
return false;
if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid))
@@ -149,10 +185,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
for (i = 0; i < MAX_LSM_RULES; i++) {
int rc = 0;
u32 osid, sid;
int retried = 0;
if (!rule->lsm[i].rule)
continue;
retry:
switch (i) {
case LSM_OBJ_USER:
case LSM_OBJ_ROLE:
@@ -176,12 +213,39 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
default:
break;
}
if ((rc < 0) && (!retried)) {
retried = 1;
ima_lsm_update_rules();
goto retry;
}
if (!rc)
return false;
}
return true;
}
/*
* In addition to knowing that we need to appraise the file in general,
* we need to differentiate between calling hooks, for hook specific rules.
*/
static int get_subaction(struct ima_rule_entry *rule, int func)
{
if (!(rule->flags & IMA_FUNC))
return IMA_FILE_APPRAISE;
switch(func) {
case MMAP_CHECK:
return IMA_MMAP_APPRAISE;
case BPRM_CHECK:
return IMA_BPRM_APPRAISE;
case MODULE_CHECK:
return IMA_MODULE_APPRAISE;
case FILE_CHECK:
default:
return IMA_FILE_APPRAISE;
}
}
/**
* ima_match_policy - decision based on LSM and other conditions
* @inode: pointer to an inode for which the policy decision is being made
@@ -209,7 +273,12 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
if (!ima_match_rules(entry, inode, func, mask))
continue;
action |= entry->flags & IMA_ACTION_FLAGS;
action |= entry->action & IMA_DO_MASK;
if (entry->action & IMA_APPRAISE)
action |= get_subaction(entry, func);
if (entry->action & IMA_DO_MASK)
actmask &= ~(entry->action | entry->action << 1);
else
@@ -282,7 +351,8 @@ enum {
Opt_audit,
Opt_obj_user, Opt_obj_role, Opt_obj_type,
Opt_subj_user, Opt_subj_role, Opt_subj_type,
Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner
Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner,
Opt_appraise_type, Opt_fsuuid
};
static match_table_t policy_tokens = {
@@ -300,25 +370,35 @@ static match_table_t policy_tokens = {
{Opt_func, "func=%s"},
{Opt_mask, "mask=%s"},
{Opt_fsmagic, "fsmagic=%s"},
{Opt_fsuuid, "fsuuid=%s"},
{Opt_uid, "uid=%s"},
{Opt_fowner, "fowner=%s"},
{Opt_appraise_type, "appraise_type=%s"},
{Opt_err, NULL}
};
static int ima_lsm_rule_init(struct ima_rule_entry *entry,
char *args, int lsm_rule, int audit_type)
substring_t *args, int lsm_rule, int audit_type)
{
int result;
if (entry->lsm[lsm_rule].rule)
return -EINVAL;
entry->lsm[lsm_rule].args_p = match_strdup(args);
if (!entry->lsm[lsm_rule].args_p)
return -ENOMEM;
entry->lsm[lsm_rule].type = audit_type;
result = security_filter_rule_init(entry->lsm[lsm_rule].type,
Audit_equal, args,
Audit_equal,
entry->lsm[lsm_rule].args_p,
&entry->lsm[lsm_rule].rule);
if (!entry->lsm[lsm_rule].rule)
if (!entry->lsm[lsm_rule].rule) {
kfree(entry->lsm[lsm_rule].args_p);
return -EINVAL;
}
return result;
}
@@ -404,8 +484,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->func = FILE_CHECK;
else if (strcmp(args[0].from, "MODULE_CHECK") == 0)
entry->func = MODULE_CHECK;
else if (strcmp(args[0].from, "FILE_MMAP") == 0)
entry->func = FILE_MMAP;
else if ((strcmp(args[0].from, "FILE_MMAP") == 0)
|| (strcmp(args[0].from, "MMAP_CHECK") == 0))
entry->func = MMAP_CHECK;
else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
entry->func = BPRM_CHECK;
else
@@ -445,6 +526,19 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
if (!result)
entry->flags |= IMA_FSMAGIC;
break;
case Opt_fsuuid:
ima_log_string(ab, "fsuuid", args[0].from);
if (memchr_inv(entry->fsuuid, 0x00,
sizeof(entry->fsuuid))) {
result = -EINVAL;
break;
}
part_pack_uuid(args[0].from, entry->fsuuid);
entry->flags |= IMA_FSUUID;
result = 0;
break;
case Opt_uid:
ima_log_string(ab, "uid", args[0].from);
@@ -481,40 +575,52 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
break;
case Opt_obj_user:
ima_log_string(ab, "obj_user", args[0].from);
result = ima_lsm_rule_init(entry, args[0].from,
result = ima_lsm_rule_init(entry, args,
LSM_OBJ_USER,
AUDIT_OBJ_USER);
break;
case Opt_obj_role:
ima_log_string(ab, "obj_role", args[0].from);
result = ima_lsm_rule_init(entry, args[0].from,
result = ima_lsm_rule_init(entry, args,
LSM_OBJ_ROLE,
AUDIT_OBJ_ROLE);
break;
case Opt_obj_type:
ima_log_string(ab, "obj_type", args[0].from);
result = ima_lsm_rule_init(entry, args[0].from,
result = ima_lsm_rule_init(entry, args,
LSM_OBJ_TYPE,
AUDIT_OBJ_TYPE);
break;
case Opt_subj_user:
ima_log_string(ab, "subj_user", args[0].from);
result = ima_lsm_rule_init(entry, args[0].from,
result = ima_lsm_rule_init(entry, args,
LSM_SUBJ_USER,
AUDIT_SUBJ_USER);
break;
case Opt_subj_role:
ima_log_string(ab, "subj_role", args[0].from);
result = ima_lsm_rule_init(entry, args[0].from,
result = ima_lsm_rule_init(entry, args,
LSM_SUBJ_ROLE,
AUDIT_SUBJ_ROLE);
break;
case Opt_subj_type:
ima_log_string(ab, "subj_type", args[0].from);
result = ima_lsm_rule_init(entry, args[0].from,
result = ima_lsm_rule_init(entry, args,
LSM_SUBJ_TYPE,
AUDIT_SUBJ_TYPE);
break;
case Opt_appraise_type:
if (entry->action != APPRAISE) {
result = -EINVAL;
break;
}
ima_log_string(ab, "appraise_type", args[0].from);
if ((strcmp(args[0].from, "imasig")) == 0)
entry->flags |= IMA_DIGSIG_REQUIRED;
else
result = -EINVAL;
break;
case Opt_err:
ima_log_string(ab, "UNKNOWN", p);
result = -EINVAL;
@@ -590,9 +696,13 @@ ssize_t ima_parse_add_rule(char *rule)
void ima_delete_rules(void)
{
struct ima_rule_entry *entry, *tmp;
int i;
mutex_lock(&ima_rules_mutex);
list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
for (i = 0; i < MAX_LSM_RULES; i++)
kfree(entry->lsm[i].args_p);
list_del(&entry->list);
kfree(entry);
}

View File

@@ -14,23 +14,41 @@
#include <linux/types.h>
#include <linux/integrity.h>
#include <crypto/sha.h>
#include <linux/key.h>
/* iint action cache flags */
#define IMA_MEASURE 0x0001
#define IMA_MEASURED 0x0002
#define IMA_APPRAISE 0x0004
#define IMA_APPRAISED 0x0008
/*#define IMA_COLLECT 0x0010 do not use this flag */
#define IMA_COLLECTED 0x0020
#define IMA_AUDIT 0x0040
#define IMA_AUDITED 0x0080
#define IMA_MEASURE 0x00000001
#define IMA_MEASURED 0x00000002
#define IMA_APPRAISE 0x00000004
#define IMA_APPRAISED 0x00000008
/*#define IMA_COLLECT 0x00000010 do not use this flag */
#define IMA_COLLECTED 0x00000020
#define IMA_AUDIT 0x00000040
#define IMA_AUDITED 0x00000080
/* iint cache flags */
#define IMA_DIGSIG 0x0100
#define IMA_ACTION_FLAGS 0xff000000
#define IMA_DIGSIG 0x01000000
#define IMA_DIGSIG_REQUIRED 0x02000000
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT)
#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED \
| IMA_COLLECTED)
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
IMA_APPRAISE_SUBMASK)
#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
IMA_COLLECTED | IMA_APPRAISED_SUBMASK)
/* iint subaction appraise cache flags */
#define IMA_FILE_APPRAISE 0x00000100
#define IMA_FILE_APPRAISED 0x00000200
#define IMA_MMAP_APPRAISE 0x00000400
#define IMA_MMAP_APPRAISED 0x00000800
#define IMA_BPRM_APPRAISE 0x00001000
#define IMA_BPRM_APPRAISED 0x00002000
#define IMA_MODULE_APPRAISE 0x00004000
#define IMA_MODULE_APPRAISED 0x00008000
#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE)
#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED)
enum evm_ima_xattr_type {
IMA_XATTR_DIGEST = 0x01,
@@ -48,10 +66,13 @@ struct integrity_iint_cache {
struct rb_node rb_node; /* rooted in integrity_iint_tree */
struct inode *inode; /* back pointer to inode in question */
u64 version; /* track inode changes */
unsigned short flags;
unsigned long flags;
struct evm_ima_xattr_data ima_xattr;
enum integrity_status ima_status;
enum integrity_status evm_status;
enum integrity_status ima_file_status:4;
enum integrity_status ima_mmap_status:4;
enum integrity_status ima_bprm_status:4;
enum integrity_status ima_module_status:4;
enum integrity_status evm_status:4;
};
/* rbtree tree calls to lookup, insert, delete
@@ -81,5 +102,16 @@ static inline int integrity_digsig_verify(const unsigned int id,
#endif /* CONFIG_INTEGRITY_SIGNATURE */
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
int asymmetric_verify(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen);
#else
static inline int asymmetric_verify(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen)
{
return -EOPNOTSUPP;
}
#endif
/* set during initialization */
extern int iint_initialized;