forked from Minki/linux
Merge branch 'next-ima-appraisal' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity into next
As requested by Mimi, this adds the IMA Appraisal feature.
This commit is contained in:
commit
9ddf6aa8cb
@ -12,11 +12,14 @@ Description:
|
||||
then closing the file. The new policy takes effect after
|
||||
the file ima/policy is closed.
|
||||
|
||||
IMA appraisal, if configured, uses these file measurements
|
||||
for local measurement appraisal.
|
||||
|
||||
rule format: action [condition ...]
|
||||
|
||||
action: measure | dont_measure
|
||||
action: measure | dont_measure | appraise | dont_appraise
|
||||
condition:= base | lsm
|
||||
base: [[func=] [mask=] [fsmagic=] [uid=]]
|
||||
base: [[func=] [mask=] [fsmagic=] [uid=] [fowner]]
|
||||
lsm: [[subj_user=] [subj_role=] [subj_type=]
|
||||
[obj_user=] [obj_role=] [obj_type=]]
|
||||
|
||||
@ -24,36 +27,50 @@ Description:
|
||||
mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
|
||||
fsmagic:= hex value
|
||||
uid:= decimal value
|
||||
fowner:=decimal value
|
||||
lsm: are LSM specific
|
||||
|
||||
default policy:
|
||||
# PROC_SUPER_MAGIC
|
||||
dont_measure fsmagic=0x9fa0
|
||||
dont_appraise fsmagic=0x9fa0
|
||||
# SYSFS_MAGIC
|
||||
dont_measure fsmagic=0x62656572
|
||||
dont_appraise fsmagic=0x62656572
|
||||
# DEBUGFS_MAGIC
|
||||
dont_measure fsmagic=0x64626720
|
||||
dont_appraise fsmagic=0x64626720
|
||||
# TMPFS_MAGIC
|
||||
dont_measure fsmagic=0x01021994
|
||||
dont_appraise fsmagic=0x01021994
|
||||
# RAMFS_MAGIC
|
||||
dont_measure fsmagic=0x858458f6
|
||||
dont_appraise fsmagic=0x858458f6
|
||||
# SECURITYFS_MAGIC
|
||||
dont_measure fsmagic=0x73636673
|
||||
dont_appraise fsmagic=0x73636673
|
||||
|
||||
measure func=BPRM_CHECK
|
||||
measure func=FILE_MMAP mask=MAY_EXEC
|
||||
measure func=FILE_CHECK mask=MAY_READ uid=0
|
||||
appraise fowner=0
|
||||
|
||||
The default policy measures all executables in bprm_check,
|
||||
all files mmapped executable in file_mmap, and all files
|
||||
open for read by root in do_filp_open.
|
||||
open for read by root in do_filp_open. The default appraisal
|
||||
policy appraises all files owned by root.
|
||||
|
||||
Examples of LSM specific definitions:
|
||||
|
||||
SELinux:
|
||||
# SELINUX_MAGIC
|
||||
dont_measure fsmagic=0xF97CFF8C
|
||||
dont_measure fsmagic=0xf97cff8c
|
||||
dont_appraise fsmagic=0xf97cff8c
|
||||
|
||||
dont_measure obj_type=var_log_t
|
||||
dont_appraise obj_type=var_log_t
|
||||
dont_measure obj_type=auditd_log_t
|
||||
dont_appraise obj_type=auditd_log_t
|
||||
measure subj_user=system_u func=FILE_CHECK mask=MAY_READ
|
||||
measure subj_role=system_r func=FILE_CHECK mask=MAY_READ
|
||||
|
||||
|
@ -1051,6 +1051,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
ihash_entries= [KNL]
|
||||
Set number of hash buckets for inode cache.
|
||||
|
||||
ima_appraise= [IMA] appraise integrity measurements
|
||||
Format: { "off" | "enforce" | "fix" }
|
||||
default: "enforce"
|
||||
|
||||
ima_appraise_tcb [IMA]
|
||||
The builtin appraise policy appraises all files
|
||||
owned by uid=0.
|
||||
|
||||
ima_audit= [IMA]
|
||||
Format: { "0" | "1" }
|
||||
0 -- integrity auditing messages. (Default)
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/evm.h>
|
||||
#include <linux/ima.h>
|
||||
|
||||
/**
|
||||
* inode_change_ok - check if attribute changes to an inode are allowed
|
||||
@ -247,6 +248,7 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
|
||||
|
||||
if (!error) {
|
||||
fsnotify_change(dentry, ia_valid);
|
||||
ima_inode_post_setattr(dentry);
|
||||
evm_inode_post_setattr(dentry, ia_valid);
|
||||
}
|
||||
|
||||
|
@ -243,10 +243,10 @@ static void __fput(struct file *file)
|
||||
if (file->f_op && file->f_op->fasync)
|
||||
file->f_op->fasync(-1, file, 0);
|
||||
}
|
||||
ima_file_free(file);
|
||||
if (file->f_op && file->f_op->release)
|
||||
file->f_op->release(inode, file);
|
||||
security_file_free(file);
|
||||
ima_file_free(file);
|
||||
if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL &&
|
||||
!(file->f_mode & FMODE_PATH))) {
|
||||
cdev_put(inode->i_cdev);
|
||||
|
10
fs/xattr.c
10
fs/xattr.c
@ -295,11 +295,13 @@ vfs_removexattr(struct dentry *dentry, const char *name)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = security_inode_removexattr(dentry, name);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
error = security_inode_removexattr(dentry, name);
|
||||
if (error) {
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = inode->i_op->removexattr(dentry, name);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
|
@ -39,5 +39,32 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IMA_H */
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
extern void ima_inode_post_setattr(struct dentry *dentry);
|
||||
extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||
const void *xattr_value, size_t xattr_value_len);
|
||||
extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name);
|
||||
#else
|
||||
static inline void ima_inode_post_setattr(struct dentry *dentry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int ima_inode_setxattr(struct dentry *dentry,
|
||||
const char *xattr_name,
|
||||
const void *xattr_value,
|
||||
size_t xattr_value_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ima_inode_removexattr(struct dentry *dentry,
|
||||
const char *xattr_name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IMA_APPRAISE_H */
|
||||
#endif /* _LINUX_IMA_H */
|
||||
|
@ -22,13 +22,14 @@ enum integrity_status {
|
||||
|
||||
/* List of EVM protected security xattrs */
|
||||
#ifdef CONFIG_INTEGRITY
|
||||
extern int integrity_inode_alloc(struct inode *inode);
|
||||
extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode);
|
||||
extern void integrity_inode_free(struct inode *inode);
|
||||
|
||||
#else
|
||||
static inline int integrity_inode_alloc(struct inode *inode)
|
||||
static inline struct integrity_iint_cache *
|
||||
integrity_inode_get(struct inode *inode)
|
||||
{
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void integrity_inode_free(struct inode *inode)
|
||||
|
@ -33,6 +33,9 @@
|
||||
#define XATTR_EVM_SUFFIX "evm"
|
||||
#define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX
|
||||
|
||||
#define XATTR_IMA_SUFFIX "ima"
|
||||
#define XATTR_NAME_IMA XATTR_SECURITY_PREFIX XATTR_IMA_SUFFIX
|
||||
|
||||
#define XATTR_SELINUX_SUFFIX "selinux"
|
||||
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
|
||||
|
||||
|
@ -33,6 +33,9 @@ char *evm_config_xattrnames[] = {
|
||||
#endif
|
||||
#ifdef CONFIG_SECURITY_SMACK
|
||||
XATTR_NAME_SMACK,
|
||||
#endif
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
XATTR_NAME_IMA,
|
||||
#endif
|
||||
XATTR_NAME_CAPS,
|
||||
NULL
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "integrity.h"
|
||||
|
||||
static struct rb_root integrity_iint_tree = RB_ROOT;
|
||||
static DEFINE_SPINLOCK(integrity_iint_lock);
|
||||
static DEFINE_RWLOCK(integrity_iint_lock);
|
||||
static struct kmem_cache *iint_cache __read_mostly;
|
||||
|
||||
int iint_initialized;
|
||||
@ -35,8 +35,6 @@ static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
|
||||
struct integrity_iint_cache *iint;
|
||||
struct rb_node *n = integrity_iint_tree.rb_node;
|
||||
|
||||
assert_spin_locked(&integrity_iint_lock);
|
||||
|
||||
while (n) {
|
||||
iint = rb_entry(n, struct integrity_iint_cache, rb_node);
|
||||
|
||||
@ -63,9 +61,9 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
|
||||
if (!IS_IMA(inode))
|
||||
return NULL;
|
||||
|
||||
spin_lock(&integrity_iint_lock);
|
||||
read_lock(&integrity_iint_lock);
|
||||
iint = __integrity_iint_find(inode);
|
||||
spin_unlock(&integrity_iint_lock);
|
||||
read_unlock(&integrity_iint_lock);
|
||||
|
||||
return iint;
|
||||
}
|
||||
@ -74,59 +72,53 @@ static void iint_free(struct integrity_iint_cache *iint)
|
||||
{
|
||||
iint->version = 0;
|
||||
iint->flags = 0UL;
|
||||
iint->ima_status = INTEGRITY_UNKNOWN;
|
||||
iint->evm_status = INTEGRITY_UNKNOWN;
|
||||
kmem_cache_free(iint_cache, iint);
|
||||
}
|
||||
|
||||
/**
|
||||
* integrity_inode_alloc - allocate an iint associated with an inode
|
||||
* integrity_inode_get - find or allocate an iint associated with an inode
|
||||
* @inode: pointer to the inode
|
||||
* @return: allocated iint
|
||||
*
|
||||
* Caller must lock i_mutex
|
||||
*/
|
||||
int integrity_inode_alloc(struct inode *inode)
|
||||
struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
|
||||
{
|
||||
struct rb_node **p;
|
||||
struct rb_node *new_node, *parent = NULL;
|
||||
struct integrity_iint_cache *new_iint, *test_iint;
|
||||
int rc;
|
||||
struct rb_node *node, *parent = NULL;
|
||||
struct integrity_iint_cache *iint, *test_iint;
|
||||
|
||||
new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
|
||||
if (!new_iint)
|
||||
return -ENOMEM;
|
||||
iint = integrity_iint_find(inode);
|
||||
if (iint)
|
||||
return iint;
|
||||
|
||||
new_iint->inode = inode;
|
||||
new_node = &new_iint->rb_node;
|
||||
iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
|
||||
if (!iint)
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&inode->i_mutex); /* i_flags */
|
||||
spin_lock(&integrity_iint_lock);
|
||||
write_lock(&integrity_iint_lock);
|
||||
|
||||
p = &integrity_iint_tree.rb_node;
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
test_iint = rb_entry(parent, struct integrity_iint_cache,
|
||||
rb_node);
|
||||
rc = -EEXIST;
|
||||
if (inode < test_iint->inode)
|
||||
p = &(*p)->rb_left;
|
||||
else if (inode > test_iint->inode)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
goto out_err;
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
iint->inode = inode;
|
||||
node = &iint->rb_node;
|
||||
inode->i_flags |= S_IMA;
|
||||
rb_link_node(new_node, parent, p);
|
||||
rb_insert_color(new_node, &integrity_iint_tree);
|
||||
rb_link_node(node, parent, p);
|
||||
rb_insert_color(node, &integrity_iint_tree);
|
||||
|
||||
spin_unlock(&integrity_iint_lock);
|
||||
mutex_unlock(&inode->i_mutex); /* i_flags */
|
||||
|
||||
return 0;
|
||||
out_err:
|
||||
spin_unlock(&integrity_iint_lock);
|
||||
mutex_unlock(&inode->i_mutex); /* i_flags */
|
||||
iint_free(new_iint);
|
||||
|
||||
return rc;
|
||||
write_unlock(&integrity_iint_lock);
|
||||
return iint;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,10 +134,10 @@ void integrity_inode_free(struct inode *inode)
|
||||
if (!IS_IMA(inode))
|
||||
return;
|
||||
|
||||
spin_lock(&integrity_iint_lock);
|
||||
write_lock(&integrity_iint_lock);
|
||||
iint = __integrity_iint_find(inode);
|
||||
rb_erase(&iint->rb_node, &integrity_iint_tree);
|
||||
spin_unlock(&integrity_iint_lock);
|
||||
write_unlock(&integrity_iint_lock);
|
||||
|
||||
iint_free(iint);
|
||||
}
|
||||
@ -157,7 +149,7 @@ static void init_once(void *foo)
|
||||
memset(iint, 0, sizeof *iint);
|
||||
iint->version = 0;
|
||||
iint->flags = 0UL;
|
||||
mutex_init(&iint->mutex);
|
||||
iint->ima_status = INTEGRITY_UNKNOWN;
|
||||
iint->evm_status = INTEGRITY_UNKNOWN;
|
||||
}
|
||||
|
||||
|
@ -56,3 +56,18 @@ config IMA_LSM_RULES
|
||||
default y
|
||||
help
|
||||
Disabling this option will disregard LSM based policy rules.
|
||||
|
||||
config IMA_APPRAISE
|
||||
bool "Appraise integrity measurements"
|
||||
depends on IMA
|
||||
default n
|
||||
help
|
||||
This option enables local measurement integrity appraisal.
|
||||
It requires the system to be labeled with a security extended
|
||||
attribute containing the file hash measurement. To protect
|
||||
the security extended attributes from offline attack, enable
|
||||
and configure EVM.
|
||||
|
||||
For more information on integrity appraisal refer to:
|
||||
<http://linux-ima.sourceforge.net>
|
||||
If unsure, say N.
|
||||
|
@ -8,3 +8,4 @@ obj-$(CONFIG_IMA) += ima.o
|
||||
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
|
||||
ima_policy.o
|
||||
ima-$(CONFIG_IMA_AUDIT) += ima_audit.o
|
||||
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
|
||||
|
@ -40,6 +40,7 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
|
||||
extern int ima_initialized;
|
||||
extern int ima_used_chip;
|
||||
extern char *ima_hash;
|
||||
extern int ima_appraise;
|
||||
|
||||
/* IMA inode template definition */
|
||||
struct ima_template_data {
|
||||
@ -107,6 +108,7 @@ static inline unsigned long ima_hash_key(u8 *digest)
|
||||
}
|
||||
|
||||
/* LIM API function definitions */
|
||||
int ima_must_appraise_or_measure(struct inode *inode, int mask, int function);
|
||||
int ima_must_measure(struct inode *inode, int mask, int function);
|
||||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file);
|
||||
@ -123,14 +125,45 @@ 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 };
|
||||
enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, POST_SETATTR };
|
||||
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
|
||||
int flags);
|
||||
void ima_init_policy(void);
|
||||
void ima_update_policy(void);
|
||||
ssize_t ima_parse_add_rule(char *);
|
||||
void ima_delete_rules(void);
|
||||
|
||||
/* Appraise integrity measurements */
|
||||
#define IMA_APPRAISE_ENFORCE 0x01
|
||||
#define IMA_APPRAISE_FIX 0x02
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
int ima_appraise_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename);
|
||||
int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask);
|
||||
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
|
||||
|
||||
#else
|
||||
static inline int ima_appraise_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file,
|
||||
const unsigned char *filename)
|
||||
{
|
||||
return INTEGRITY_UNKNOWN;
|
||||
}
|
||||
|
||||
static inline int ima_must_appraise(struct inode *inode,
|
||||
enum ima_hooks func, int mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ima_update_xattr(struct integrity_iint_cache *iint,
|
||||
struct file *file)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* LSM based policy rules require audit */
|
||||
#ifdef CONFIG_IMA_LSM_RULES
|
||||
|
||||
|
@ -9,13 +9,17 @@
|
||||
* License.
|
||||
*
|
||||
* File: ima_api.c
|
||||
* Implements must_measure, collect_measurement, store_measurement,
|
||||
* and store_template.
|
||||
* Implements must_appraise_or_measure, collect_measurement,
|
||||
* appraise_measurement, store_measurement and store_template.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/evm.h>
|
||||
#include "ima.h"
|
||||
|
||||
static const char *IMA_TEMPLATE_NAME = "ima";
|
||||
|
||||
/*
|
||||
@ -93,7 +97,7 @@ err_out:
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_must_measure - measure decision based on policy.
|
||||
* ima_must_appraise_or_measure - 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)
|
||||
@ -105,15 +109,22 @@ err_out:
|
||||
* mask: contains the permission mask
|
||||
* fsmagic: hex value
|
||||
*
|
||||
* Return 0 to measure. For matching a DONT_MEASURE policy, no policy,
|
||||
* or other error, return an error code.
|
||||
*/
|
||||
* Returns IMA_MEASURE, IMA_APPRAISE mask.
|
||||
*
|
||||
*/
|
||||
int ima_must_appraise_or_measure(struct inode *inode, int mask, int function)
|
||||
{
|
||||
int flags = IMA_MEASURE | IMA_APPRAISE;
|
||||
|
||||
if (!ima_appraise)
|
||||
flags &= ~IMA_APPRAISE;
|
||||
|
||||
return ima_match_policy(inode, function, mask, flags);
|
||||
}
|
||||
|
||||
int ima_must_measure(struct inode *inode, int mask, int function)
|
||||
{
|
||||
int must_measure;
|
||||
|
||||
must_measure = ima_match_policy(inode, function, mask);
|
||||
return must_measure ? 0 : -EACCES;
|
||||
return ima_match_policy(inode, function, mask, IMA_MEASURE);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -129,16 +140,24 @@ int ima_must_measure(struct inode *inode, int mask, int function)
|
||||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file)
|
||||
{
|
||||
int result = -EEXIST;
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
const char *filename = file->f_dentry->d_name.name;
|
||||
int result = 0;
|
||||
|
||||
if (!(iint->flags & IMA_MEASURED)) {
|
||||
if (!(iint->flags & IMA_COLLECTED)) {
|
||||
u64 i_version = file->f_dentry->d_inode->i_version;
|
||||
|
||||
memset(iint->digest, 0, IMA_DIGEST_SIZE);
|
||||
result = ima_calc_hash(file, iint->digest);
|
||||
if (!result)
|
||||
iint->ima_xattr.type = IMA_XATTR_DIGEST;
|
||||
result = ima_calc_hash(file, iint->ima_xattr.digest);
|
||||
if (!result) {
|
||||
iint->version = i_version;
|
||||
iint->flags |= IMA_COLLECTED;
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
|
||||
filename, "collect_data", "failed",
|
||||
result, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -167,6 +186,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
||||
struct ima_template_entry *entry;
|
||||
int violation = 0;
|
||||
|
||||
if (iint->flags & IMA_MEASURED)
|
||||
return;
|
||||
|
||||
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry) {
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
|
||||
@ -174,7 +196,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
||||
return;
|
||||
}
|
||||
memset(&entry->template, 0, sizeof(entry->template));
|
||||
memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE);
|
||||
memcpy(entry->template.digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE);
|
||||
strcpy(entry->template.file_name,
|
||||
(strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ?
|
||||
file->f_dentry->d_name.name : filename);
|
||||
|
263
security/integrity/ima/ima_appraise.c
Normal file
263
security/integrity/ima/ima_appraise.c
Normal file
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (C) 2011 IBM Corporation
|
||||
*
|
||||
* Author:
|
||||
* Mimi Zohar <zohar@us.ibm.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.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/ima.h>
|
||||
#include <linux/evm.h>
|
||||
|
||||
#include "ima.h"
|
||||
|
||||
static int __init default_appraise_setup(char *str)
|
||||
{
|
||||
if (strncmp(str, "off", 3) == 0)
|
||||
ima_appraise = 0;
|
||||
else if (strncmp(str, "fix", 3) == 0)
|
||||
ima_appraise = IMA_APPRAISE_FIX;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("ima_appraise=", default_appraise_setup);
|
||||
|
||||
/*
|
||||
* ima_must_appraise - set appraise flag
|
||||
*
|
||||
* Return 1 to appraise
|
||||
*/
|
||||
int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask)
|
||||
{
|
||||
if (!ima_appraise)
|
||||
return 0;
|
||||
|
||||
return ima_match_policy(inode, func, mask, IMA_APPRAISE);
|
||||
}
|
||||
|
||||
static void 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_appraise_measurement - appraise file measurement
|
||||
*
|
||||
* Call evm_verifyxattr() to verify the integrity of 'security.ima'.
|
||||
* Assuming success, compare the xattr hash with the collected measurement.
|
||||
*
|
||||
* Return 0 on success, error code otherwise
|
||||
*/
|
||||
int ima_appraise_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename)
|
||||
{
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct evm_ima_xattr_data *xattr_value = NULL;
|
||||
enum integrity_status status = INTEGRITY_UNKNOWN;
|
||||
const char *op = "appraise_data";
|
||||
char *cause = "unknown";
|
||||
int rc;
|
||||
|
||||
if (!ima_appraise)
|
||||
return 0;
|
||||
if (!inode->i_op->getxattr)
|
||||
return INTEGRITY_UNKNOWN;
|
||||
|
||||
if (iint->flags & IMA_APPRAISED)
|
||||
return iint->ima_status;
|
||||
|
||||
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value,
|
||||
0, GFP_NOFS);
|
||||
if (rc <= 0) {
|
||||
if (rc && rc != -ENODATA)
|
||||
goto out;
|
||||
|
||||
cause = "missing-hash";
|
||||
status =
|
||||
(inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
|
||||
if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) {
|
||||
if ((status == INTEGRITY_NOLABEL)
|
||||
|| (status == INTEGRITY_NOXATTRS))
|
||||
cause = "missing-HMAC";
|
||||
else if (status == INTEGRITY_FAIL)
|
||||
cause = "invalid-HMAC";
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (xattr_value->type) {
|
||||
case IMA_XATTR_DIGEST:
|
||||
rc = memcmp(xattr_value->digest, iint->ima_xattr.digest,
|
||||
IMA_DIGEST_SIZE);
|
||||
if (rc) {
|
||||
cause = "invalid-hash";
|
||||
status = INTEGRITY_FAIL;
|
||||
print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE,
|
||||
xattr_value, sizeof(*xattr_value));
|
||||
print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE,
|
||||
(u8 *)&iint->ima_xattr,
|
||||
sizeof iint->ima_xattr);
|
||||
break;
|
||||
}
|
||||
status = INTEGRITY_PASS;
|
||||
break;
|
||||
case EVM_IMA_XATTR_DIGSIG:
|
||||
iint->flags |= IMA_DIGSIG;
|
||||
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
|
||||
xattr_value->digest, rc - 1,
|
||||
iint->ima_xattr.digest,
|
||||
IMA_DIGEST_SIZE);
|
||||
if (rc == -EOPNOTSUPP) {
|
||||
status = INTEGRITY_UNKNOWN;
|
||||
} else if (rc) {
|
||||
cause = "invalid-signature";
|
||||
status = INTEGRITY_FAIL;
|
||||
} else {
|
||||
status = INTEGRITY_PASS;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
status = INTEGRITY_UNKNOWN;
|
||||
cause = "unknown-ima-data";
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if (status != INTEGRITY_PASS) {
|
||||
if ((ima_appraise & IMA_APPRAISE_FIX) &&
|
||||
(!xattr_value ||
|
||||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
|
||||
ima_fix_xattr(dentry, iint);
|
||||
status = INTEGRITY_PASS;
|
||||
}
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
|
||||
op, cause, rc, 0);
|
||||
} else {
|
||||
iint->flags |= IMA_APPRAISED;
|
||||
}
|
||||
iint->ima_status = status;
|
||||
kfree(xattr_value);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_update_xattr - update 'security.ima' hash value
|
||||
*/
|
||||
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
|
||||
{
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
int rc = 0;
|
||||
|
||||
/* do not collect and update hash for digital signatures */
|
||||
if (iint->flags & IMA_DIGSIG)
|
||||
return;
|
||||
|
||||
rc = ima_collect_measurement(iint, file);
|
||||
if (rc < 0)
|
||||
return;
|
||||
|
||||
ima_fix_xattr(dentry, iint);
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_inode_post_setattr - reflect file metadata changes
|
||||
* @dentry: pointer to the affected dentry
|
||||
*
|
||||
* Changes to a dentry's metadata might result in needing to appraise.
|
||||
*
|
||||
* This function is called from notify_change(), which expects the caller
|
||||
* to lock the inode's i_mutex.
|
||||
*/
|
||||
void ima_inode_post_setattr(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct integrity_iint_cache *iint;
|
||||
int must_appraise, rc;
|
||||
|
||||
if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)
|
||||
|| !inode->i_op->removexattr)
|
||||
return;
|
||||
|
||||
must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
|
||||
iint = integrity_iint_find(inode);
|
||||
if (iint) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_protect_xattr - protect 'security.ima'
|
||||
*
|
||||
* Ensure that not just anyone can modify or remove 'security.ima'.
|
||||
*/
|
||||
static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
|
||||
const void *xattr_value, size_t xattr_value_len)
|
||||
{
|
||||
if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) {
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ima_reset_appraise_flags(struct inode *inode)
|
||||
{
|
||||
struct integrity_iint_cache *iint;
|
||||
|
||||
if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode))
|
||||
return;
|
||||
|
||||
iint = integrity_iint_find(inode);
|
||||
if (!iint)
|
||||
return;
|
||||
|
||||
iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED);
|
||||
return;
|
||||
}
|
||||
|
||||
int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||
const void *xattr_value, size_t xattr_value_len)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
|
||||
xattr_value_len);
|
||||
if (result == 1) {
|
||||
ima_reset_appraise_flags(dentry->d_inode);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
|
||||
if (result == 1) {
|
||||
ima_reset_appraise_flags(dentry->d_inode);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
@ -48,7 +48,7 @@ int ima_calc_hash(struct file *file, char *digest)
|
||||
struct scatterlist sg[1];
|
||||
loff_t i_size, offset = 0;
|
||||
char *rbuf;
|
||||
int rc;
|
||||
int rc, read = 0;
|
||||
|
||||
rc = init_desc(&desc);
|
||||
if (rc != 0)
|
||||
@ -59,6 +59,10 @@ int ima_calc_hash(struct file *file, char *digest)
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!(file->f_mode & FMODE_READ)) {
|
||||
file->f_mode |= FMODE_READ;
|
||||
read = 1;
|
||||
}
|
||||
i_size = i_size_read(file->f_dentry->d_inode);
|
||||
while (offset < i_size) {
|
||||
int rbuf_len;
|
||||
@ -80,6 +84,8 @@ int ima_calc_hash(struct file *file, char *digest)
|
||||
kfree(rbuf);
|
||||
if (!rc)
|
||||
rc = crypto_hash_final(&desc, digest);
|
||||
if (read)
|
||||
file->f_mode &= ~FMODE_READ;
|
||||
out:
|
||||
crypto_free_hash(desc.tfm);
|
||||
return rc;
|
||||
|
@ -22,12 +22,19 @@
|
||||
#include <linux/mount.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/ima.h>
|
||||
|
||||
#include "ima.h"
|
||||
|
||||
int ima_initialized;
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
int ima_appraise = IMA_APPRAISE_ENFORCE;
|
||||
#else
|
||||
int ima_appraise;
|
||||
#endif
|
||||
|
||||
char *ima_hash = "sha1";
|
||||
static int __init hash_setup(char *str)
|
||||
{
|
||||
@ -52,7 +59,7 @@ static void ima_rdwr_violation_check(struct file *file)
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
fmode_t mode = file->f_mode;
|
||||
int rc;
|
||||
int must_measure;
|
||||
bool send_tomtou = false, send_writers = false;
|
||||
unsigned char *pathname = NULL, *pathbuf = NULL;
|
||||
|
||||
@ -67,8 +74,8 @@ static void ima_rdwr_violation_check(struct file *file)
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ima_must_measure(inode, MAY_READ, FILE_CHECK);
|
||||
if (rc < 0)
|
||||
must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK);
|
||||
if (!must_measure)
|
||||
goto out;
|
||||
|
||||
if (atomic_read(&inode->i_writecount) > 0)
|
||||
@ -100,17 +107,21 @@ out:
|
||||
}
|
||||
|
||||
static void ima_check_last_writer(struct integrity_iint_cache *iint,
|
||||
struct inode *inode,
|
||||
struct file *file)
|
||||
struct inode *inode, struct file *file)
|
||||
{
|
||||
fmode_t mode = file->f_mode;
|
||||
|
||||
mutex_lock(&iint->mutex);
|
||||
if (mode & FMODE_WRITE &&
|
||||
atomic_read(&inode->i_writecount) == 1 &&
|
||||
iint->version != inode->i_version)
|
||||
iint->flags &= ~IMA_MEASURED;
|
||||
mutex_unlock(&iint->mutex);
|
||||
if (!(mode & FMODE_WRITE))
|
||||
return;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
if (atomic_read(&inode->i_writecount) == 1 &&
|
||||
iint->version != inode->i_version) {
|
||||
iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED);
|
||||
if (iint->flags & IMA_APPRAISE)
|
||||
ima_update_xattr(iint, file);
|
||||
}
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -140,29 +151,37 @@ static int process_measurement(struct file *file, const unsigned char *filename,
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
struct integrity_iint_cache *iint;
|
||||
unsigned char *pathname = NULL, *pathbuf = NULL;
|
||||
int rc = 0;
|
||||
int rc = -ENOMEM, action, must_appraise;
|
||||
|
||||
if (!ima_initialized || !S_ISREG(inode->i_mode))
|
||||
return 0;
|
||||
|
||||
rc = ima_must_measure(inode, mask, function);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
retry:
|
||||
iint = integrity_iint_find(inode);
|
||||
if (!iint) {
|
||||
rc = integrity_inode_alloc(inode);
|
||||
if (!rc || rc == -EEXIST)
|
||||
goto retry;
|
||||
return rc;
|
||||
}
|
||||
/* Determine if in appraise/measurement policy,
|
||||
* returns IMA_MEASURE, IMA_APPRAISE bitmask. */
|
||||
action = ima_must_appraise_or_measure(inode, mask, function);
|
||||
if (!action)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&iint->mutex);
|
||||
must_appraise = action & IMA_APPRAISE;
|
||||
|
||||
rc = iint->flags & IMA_MEASURED ? 1 : 0;
|
||||
if (rc != 0)
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
iint = integrity_inode_get(inode);
|
||||
if (!iint)
|
||||
goto out;
|
||||
|
||||
/* Determine if already appraised/measured based on bitmask
|
||||
* (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED) */
|
||||
iint->flags |= action;
|
||||
action &= ~((iint->flags & (IMA_MEASURED | IMA_APPRAISED)) >> 1);
|
||||
|
||||
/* Nothing to do, just return existing appraised status */
|
||||
if (!action) {
|
||||
if (iint->flags & IMA_APPRAISED)
|
||||
rc = iint->ima_status;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ima_collect_measurement(iint, file);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
@ -177,11 +196,16 @@ retry:
|
||||
pathname = NULL;
|
||||
}
|
||||
}
|
||||
ima_store_measurement(iint, file, !pathname ? filename : pathname);
|
||||
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);
|
||||
kfree(pathbuf);
|
||||
out:
|
||||
mutex_unlock(&iint->mutex);
|
||||
return rc;
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return (rc && must_appraise) ? -EACCES : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -197,14 +221,14 @@ out:
|
||||
*/
|
||||
int ima_file_mmap(struct file *file, unsigned long prot)
|
||||
{
|
||||
int rc;
|
||||
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 0;
|
||||
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,7 +252,7 @@ int ima_bprm_check(struct linux_binprm *bprm)
|
||||
(strcmp(bprm->filename, bprm->interp) == 0) ?
|
||||
bprm->filename : bprm->interp,
|
||||
MAY_EXEC, BPRM_CHECK);
|
||||
return 0;
|
||||
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -249,7 +273,7 @@ int ima_file_check(struct file *file, int mask)
|
||||
rc = process_measurement(file, file->f_dentry->d_name.name,
|
||||
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
|
||||
FILE_CHECK);
|
||||
return 0;
|
||||
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ima_file_check);
|
||||
|
||||
|
@ -24,22 +24,30 @@
|
||||
#define IMA_MASK 0x0002
|
||||
#define IMA_FSMAGIC 0x0004
|
||||
#define IMA_UID 0x0008
|
||||
#define IMA_FOWNER 0x0010
|
||||
|
||||
enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE };
|
||||
#define UNKNOWN 0
|
||||
#define MEASURE 1 /* same as IMA_MEASURE */
|
||||
#define DONT_MEASURE 2
|
||||
#define MEASURE_MASK 3
|
||||
#define APPRAISE 4 /* same as IMA_APPRAISE */
|
||||
#define DONT_APPRAISE 8
|
||||
#define APPRAISE_MASK 12
|
||||
|
||||
#define MAX_LSM_RULES 6
|
||||
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
|
||||
LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
|
||||
};
|
||||
|
||||
struct ima_measure_rule_entry {
|
||||
struct ima_rule_entry {
|
||||
struct list_head list;
|
||||
enum ima_action action;
|
||||
int action;
|
||||
unsigned int flags;
|
||||
enum ima_hooks func;
|
||||
int mask;
|
||||
unsigned long fsmagic;
|
||||
uid_t uid;
|
||||
uid_t fowner;
|
||||
struct {
|
||||
void *rule; /* LSM file metadata specific */
|
||||
int type; /* audit type */
|
||||
@ -48,7 +56,7 @@ struct ima_measure_rule_entry {
|
||||
|
||||
/*
|
||||
* Without LSM specific knowledge, the default policy can only be
|
||||
* written in terms of .action, .func, .mask, .fsmagic, and .uid
|
||||
* written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -57,7 +65,7 @@ struct ima_measure_rule_entry {
|
||||
* normal users can easily run the machine out of memory simply building
|
||||
* and running executables.
|
||||
*/
|
||||
static struct ima_measure_rule_entry default_rules[] = {
|
||||
static struct ima_rule_entry default_rules[] = {
|
||||
{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
@ -75,19 +83,41 @@ static struct ima_measure_rule_entry default_rules[] = {
|
||||
.flags = IMA_FUNC | IMA_MASK | IMA_UID},
|
||||
};
|
||||
|
||||
static LIST_HEAD(measure_default_rules);
|
||||
static LIST_HEAD(measure_policy_rules);
|
||||
static struct list_head *ima_measure;
|
||||
static struct ima_rule_entry default_appraise_rules[] = {
|
||||
{.action = DONT_APPRAISE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = CGROUP_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = APPRAISE,.fowner = 0,.flags = IMA_FOWNER},
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(ima_measure_mutex);
|
||||
static LIST_HEAD(ima_default_rules);
|
||||
static LIST_HEAD(ima_policy_rules);
|
||||
static struct list_head *ima_rules;
|
||||
|
||||
static DEFINE_MUTEX(ima_rules_mutex);
|
||||
|
||||
static bool ima_use_tcb __initdata;
|
||||
static int __init default_policy_setup(char *str)
|
||||
static int __init default_measure_policy_setup(char *str)
|
||||
{
|
||||
ima_use_tcb = 1;
|
||||
return 1;
|
||||
}
|
||||
__setup("ima_tcb", default_policy_setup);
|
||||
__setup("ima_tcb", default_measure_policy_setup);
|
||||
|
||||
static bool ima_use_appraise_tcb __initdata;
|
||||
static int __init default_appraise_policy_setup(char *str)
|
||||
{
|
||||
ima_use_appraise_tcb = 1;
|
||||
return 1;
|
||||
}
|
||||
__setup("ima_appraise_tcb", default_appraise_policy_setup);
|
||||
|
||||
/**
|
||||
* ima_match_rules - determine whether an inode matches the measure rule.
|
||||
@ -98,7 +128,7 @@ __setup("ima_tcb", default_policy_setup);
|
||||
*
|
||||
* Returns true on rule match, false on failure.
|
||||
*/
|
||||
static bool ima_match_rules(struct ima_measure_rule_entry *rule,
|
||||
static bool ima_match_rules(struct ima_rule_entry *rule,
|
||||
struct inode *inode, enum ima_hooks func, int mask)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
@ -114,6 +144,8 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
|
||||
return false;
|
||||
if ((rule->flags & IMA_UID) && rule->uid != cred->uid)
|
||||
return false;
|
||||
if ((rule->flags & IMA_FOWNER) && rule->fowner != inode->i_uid)
|
||||
return false;
|
||||
for (i = 0; i < MAX_LSM_RULES; i++) {
|
||||
int rc = 0;
|
||||
u32 osid, sid;
|
||||
@ -163,39 +195,58 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
|
||||
* as elements in the list are never deleted, nor does the list
|
||||
* change.)
|
||||
*/
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
|
||||
int flags)
|
||||
{
|
||||
struct ima_measure_rule_entry *entry;
|
||||
struct ima_rule_entry *entry;
|
||||
int action = 0, actmask = flags | (flags << 1);
|
||||
|
||||
list_for_each_entry(entry, ima_measure, list) {
|
||||
bool rc;
|
||||
list_for_each_entry(entry, ima_rules, list) {
|
||||
|
||||
rc = ima_match_rules(entry, inode, func, mask);
|
||||
if (rc)
|
||||
return entry->action;
|
||||
if (!(entry->action & actmask))
|
||||
continue;
|
||||
|
||||
if (!ima_match_rules(entry, inode, func, mask))
|
||||
continue;
|
||||
|
||||
action |= (entry->action & (IMA_APPRAISE | IMA_MEASURE));
|
||||
actmask &= (entry->action & APPRAISE_MASK) ?
|
||||
~APPRAISE_MASK : ~MEASURE_MASK;
|
||||
if (!actmask)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_init_policy - initialize the default measure rules.
|
||||
*
|
||||
* ima_measure points to either the measure_default_rules or the
|
||||
* the new measure_policy_rules.
|
||||
* ima_rules points to either the ima_default_rules or the
|
||||
* the new ima_policy_rules.
|
||||
*/
|
||||
void __init ima_init_policy(void)
|
||||
{
|
||||
int i, entries;
|
||||
int i, measure_entries, appraise_entries;
|
||||
|
||||
/* if !ima_use_tcb set entries = 0 so we load NO default rules */
|
||||
if (ima_use_tcb)
|
||||
entries = ARRAY_SIZE(default_rules);
|
||||
else
|
||||
entries = 0;
|
||||
measure_entries = ima_use_tcb ? ARRAY_SIZE(default_rules) : 0;
|
||||
appraise_entries = ima_use_appraise_tcb ?
|
||||
ARRAY_SIZE(default_appraise_rules) : 0;
|
||||
|
||||
for (i = 0; i < measure_entries + appraise_entries; i++) {
|
||||
if (i < measure_entries)
|
||||
list_add_tail(&default_rules[i].list,
|
||||
&ima_default_rules);
|
||||
else {
|
||||
int j = i - measure_entries;
|
||||
|
||||
for (i = 0; i < entries; i++)
|
||||
list_add_tail(&default_rules[i].list, &measure_default_rules);
|
||||
ima_measure = &measure_default_rules;
|
||||
list_add_tail(&default_appraise_rules[j].list,
|
||||
&ima_default_rules);
|
||||
}
|
||||
}
|
||||
|
||||
ima_rules = &ima_default_rules;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -212,8 +263,8 @@ void ima_update_policy(void)
|
||||
int result = 1;
|
||||
int audit_info = 0;
|
||||
|
||||
if (ima_measure == &measure_default_rules) {
|
||||
ima_measure = &measure_policy_rules;
|
||||
if (ima_rules == &ima_default_rules) {
|
||||
ima_rules = &ima_policy_rules;
|
||||
cause = "complete";
|
||||
result = 0;
|
||||
}
|
||||
@ -224,14 +275,17 @@ void ima_update_policy(void)
|
||||
enum {
|
||||
Opt_err = -1,
|
||||
Opt_measure = 1, Opt_dont_measure,
|
||||
Opt_appraise, Opt_dont_appraise,
|
||||
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_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner
|
||||
};
|
||||
|
||||
static match_table_t policy_tokens = {
|
||||
{Opt_measure, "measure"},
|
||||
{Opt_dont_measure, "dont_measure"},
|
||||
{Opt_appraise, "appraise"},
|
||||
{Opt_dont_appraise, "dont_appraise"},
|
||||
{Opt_obj_user, "obj_user=%s"},
|
||||
{Opt_obj_role, "obj_role=%s"},
|
||||
{Opt_obj_type, "obj_type=%s"},
|
||||
@ -242,10 +296,11 @@ static match_table_t policy_tokens = {
|
||||
{Opt_mask, "mask=%s"},
|
||||
{Opt_fsmagic, "fsmagic=%s"},
|
||||
{Opt_uid, "uid=%s"},
|
||||
{Opt_fowner, "fowner=%s"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
|
||||
static int ima_lsm_rule_init(struct ima_rule_entry *entry,
|
||||
char *args, int lsm_rule, int audit_type)
|
||||
{
|
||||
int result;
|
||||
@ -269,7 +324,7 @@ static void ima_log_string(struct audit_buffer *ab, char *key, char *value)
|
||||
audit_log_format(ab, " ");
|
||||
}
|
||||
|
||||
static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
||||
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
{
|
||||
struct audit_buffer *ab;
|
||||
char *p;
|
||||
@ -278,6 +333,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE);
|
||||
|
||||
entry->uid = -1;
|
||||
entry->fowner = -1;
|
||||
entry->action = UNKNOWN;
|
||||
while ((p = strsep(&rule, " \t")) != NULL) {
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
@ -306,11 +362,27 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
||||
|
||||
entry->action = DONT_MEASURE;
|
||||
break;
|
||||
case Opt_appraise:
|
||||
ima_log_string(ab, "action", "appraise");
|
||||
|
||||
if (entry->action != UNKNOWN)
|
||||
result = -EINVAL;
|
||||
|
||||
entry->action = APPRAISE;
|
||||
break;
|
||||
case Opt_dont_appraise:
|
||||
ima_log_string(ab, "action", "dont_appraise");
|
||||
|
||||
if (entry->action != UNKNOWN)
|
||||
result = -EINVAL;
|
||||
|
||||
entry->action = DONT_APPRAISE;
|
||||
break;
|
||||
case Opt_func:
|
||||
ima_log_string(ab, "func", args[0].from);
|
||||
|
||||
if (entry->func)
|
||||
result = -EINVAL;
|
||||
result = -EINVAL;
|
||||
|
||||
if (strcmp(args[0].from, "FILE_CHECK") == 0)
|
||||
entry->func = FILE_CHECK;
|
||||
@ -375,6 +447,23 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
||||
entry->flags |= IMA_UID;
|
||||
}
|
||||
break;
|
||||
case Opt_fowner:
|
||||
ima_log_string(ab, "fowner", args[0].from);
|
||||
|
||||
if (entry->fowner != -1) {
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
result = strict_strtoul(args[0].from, 10, &lnum);
|
||||
if (!result) {
|
||||
entry->fowner = (uid_t) lnum;
|
||||
if (entry->fowner != lnum)
|
||||
result = -EINVAL;
|
||||
else
|
||||
entry->flags |= IMA_FOWNER;
|
||||
}
|
||||
break;
|
||||
case Opt_obj_user:
|
||||
ima_log_string(ab, "obj_user", args[0].from);
|
||||
result = ima_lsm_rule_init(entry, args[0].from,
|
||||
@ -426,7 +515,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_parse_add_rule - add a rule to measure_policy_rules
|
||||
* ima_parse_add_rule - add a rule to ima_policy_rules
|
||||
* @rule - ima measurement policy rule
|
||||
*
|
||||
* Uses a mutex to protect the policy list from multiple concurrent writers.
|
||||
@ -436,12 +525,12 @@ ssize_t ima_parse_add_rule(char *rule)
|
||||
{
|
||||
const char *op = "update_policy";
|
||||
char *p;
|
||||
struct ima_measure_rule_entry *entry;
|
||||
struct ima_rule_entry *entry;
|
||||
ssize_t result, len;
|
||||
int audit_info = 0;
|
||||
|
||||
/* Prevent installed policy from changing */
|
||||
if (ima_measure != &measure_default_rules) {
|
||||
if (ima_rules != &ima_default_rules) {
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
|
||||
NULL, op, "already exists",
|
||||
-EACCES, audit_info);
|
||||
@ -474,9 +563,9 @@ ssize_t ima_parse_add_rule(char *rule)
|
||||
return result;
|
||||
}
|
||||
|
||||
mutex_lock(&ima_measure_mutex);
|
||||
list_add_tail(&entry->list, &measure_policy_rules);
|
||||
mutex_unlock(&ima_measure_mutex);
|
||||
mutex_lock(&ima_rules_mutex);
|
||||
list_add_tail(&entry->list, &ima_policy_rules);
|
||||
mutex_unlock(&ima_rules_mutex);
|
||||
|
||||
return len;
|
||||
}
|
||||
@ -484,12 +573,12 @@ ssize_t ima_parse_add_rule(char *rule)
|
||||
/* ima_delete_rules called to cleanup invalid policy */
|
||||
void ima_delete_rules(void)
|
||||
{
|
||||
struct ima_measure_rule_entry *entry, *tmp;
|
||||
struct ima_rule_entry *entry, *tmp;
|
||||
|
||||
mutex_lock(&ima_measure_mutex);
|
||||
list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) {
|
||||
mutex_lock(&ima_rules_mutex);
|
||||
list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
mutex_unlock(&ima_measure_mutex);
|
||||
mutex_unlock(&ima_rules_mutex);
|
||||
}
|
||||
|
@ -16,7 +16,12 @@
|
||||
#include <crypto/sha.h>
|
||||
|
||||
/* iint cache flags */
|
||||
#define IMA_MEASURED 0x01
|
||||
#define IMA_MEASURE 0x01
|
||||
#define IMA_MEASURED 0x02
|
||||
#define IMA_APPRAISE 0x04
|
||||
#define IMA_APPRAISED 0x08
|
||||
#define IMA_COLLECTED 0x10
|
||||
#define IMA_DIGSIG 0x20
|
||||
|
||||
enum evm_ima_xattr_type {
|
||||
IMA_XATTR_DIGEST = 0x01,
|
||||
@ -35,8 +40,8 @@ struct integrity_iint_cache {
|
||||
struct inode *inode; /* back pointer to inode in question */
|
||||
u64 version; /* track inode changes */
|
||||
unsigned char flags;
|
||||
u8 digest[SHA1_DIGEST_SIZE];
|
||||
struct mutex mutex; /* protects: version, flags, digest */
|
||||
struct evm_ima_xattr_data ima_xattr;
|
||||
enum integrity_status ima_status;
|
||||
enum integrity_status evm_status;
|
||||
};
|
||||
|
||||
|
@ -571,6 +571,9 @@ int security_inode_setxattr(struct dentry *dentry, const char *name,
|
||||
if (unlikely(IS_PRIVATE(dentry->d_inode)))
|
||||
return 0;
|
||||
ret = security_ops->inode_setxattr(dentry, name, value, size, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ima_inode_setxattr(dentry, name, value, size);
|
||||
if (ret)
|
||||
return ret;
|
||||
return evm_inode_setxattr(dentry, name, value, size);
|
||||
@ -606,6 +609,9 @@ int security_inode_removexattr(struct dentry *dentry, const char *name)
|
||||
if (unlikely(IS_PRIVATE(dentry->d_inode)))
|
||||
return 0;
|
||||
ret = security_ops->inode_removexattr(dentry, name);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ima_inode_removexattr(dentry, name);
|
||||
if (ret)
|
||||
return ret;
|
||||
return evm_inode_removexattr(dentry, name);
|
||||
|
Loading…
Reference in New Issue
Block a user