Skip to content

Commit

Permalink
Merge branch 'next-ima-appraisal' of git://git.kernel.org/pub/scm/lin…
Browse files Browse the repository at this point in the history
…ux/kernel/git/zohar/linux-integrity into next

As requested by Mimi, this adds the IMA Appraisal feature.
  • Loading branch information
James Morris committed Sep 12, 2012
2 parents b25b09e + 8606404 commit 9ddf6aa
Show file tree
Hide file tree
Showing 20 changed files with 668 additions and 149 deletions.
25 changes: 21 additions & 4 deletions Documentation/ABI/testing/ima_policy
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,65 @@ 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=]]

base: func:= [BPRM_CHECK][FILE_MMAP][FILE_CHECK]
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

Expand Down
8 changes: 8 additions & 0 deletions Documentation/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions fs/attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion fs/file_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 4 additions & 2 deletions fs/xattr.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,13 @@ vfs_removexattr(struct dentry *dentry, const char *name)
if (error)
return error;

mutex_lock(&inode->i_mutex);
error = security_inode_removexattr(dentry, name);
if (error)
if (error) {
mutex_unlock(&inode->i_mutex);
return error;
}

mutex_lock(&inode->i_mutex);
error = inode->i_op->removexattr(dentry, name);
mutex_unlock(&inode->i_mutex);

Expand Down
27 changes: 27 additions & 0 deletions include/linux/ima.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
7 changes: 4 additions & 3 deletions include/linux/integrity.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions include/linux/xattr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions security/integrity/evm/evm_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
64 changes: 28 additions & 36 deletions security/integrity/iint.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);

Expand All @@ -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;
}
Expand All @@ -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;
}

/**
Expand All @@ -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);
}
Expand All @@ -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;
}

Expand Down
15 changes: 15 additions & 0 deletions security/integrity/ima/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions security/integrity/ima/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit 9ddf6aa

Please sign in to comment.