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:
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
115
security/integrity/digsig_asymmetric.c
Normal file
115
security/integrity/digsig_asymmetric.c
Normal 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;
|
||||
}
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
*/
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user