1
0
Fork 0

Merge branch 'next-evm' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/ima-2.6 into next

Conflicts:
	fs/attr.c

Resolve conflict manually.

Signed-off-by: James Morris <jmorris@namei.org>
wifi-calibration
James Morris 2011-08-09 10:31:03 +10:00
commit 5a2f3a02ae
39 changed files with 1540 additions and 410 deletions

View File

@ -0,0 +1,23 @@
What: security/evm
Date: March 2011
Contact: Mimi Zohar <zohar@us.ibm.com>
Description:
EVM protects a file's security extended attributes(xattrs)
against integrity attacks. The initial method maintains an
HMAC-sha1 value across the extended attributes, storing the
value as the extended attribute 'security.evm'.
EVM depends on the Kernel Key Retention System to provide it
with a trusted/encrypted key for the HMAC-sha1 operation.
The key is loaded onto the root's keyring using keyctl. Until
EVM receives notification that the key has been successfully
loaded onto the keyring (echo 1 > <securityfs>/evm), EVM
can not create or validate the 'security.evm' xattr, but
returns INTEGRITY_UNKNOWN. Loading the key and signaling EVM
should be done as early as possible. Normally this is done
in the initramfs, which has already been measured as part
of the trusted boot. For more information on creating and
loading existing trusted/encrypted keys, refer to:
Documentation/keys-trusted-encrypted.txt. (A sample dracut
patch, which loads the trusted/encrypted key and enables
EVM, is available from http://linux-ima.sourceforge.net/#EVM.)

View File

@ -48,6 +48,7 @@ parameter is applicable:
EDD BIOS Enhanced Disk Drive Services (EDD) is enabled
EFI EFI Partitioning (GPT) is enabled
EIDE EIDE/ATAPI support is enabled.
EVM Extended Verification Module
FB The frame buffer device is enabled.
GCOV GCOV profiling is enabled.
HW Appropriate hardware is enabled.
@ -758,6 +759,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
This option is obsoleted by the "netdev=" option, which
has equivalent usage. See its documentation for details.
evm= [EVM]
Format: { "fix" }
Permit 'security.evm' to be updated regardless of
current integrity status.
failslab=
fail_page_alloc=
fail_make_request=[KNL]

View File

@ -13,6 +13,7 @@
#include <linux/fsnotify.h>
#include <linux/fcntl.h>
#include <linux/security.h>
#include <linux/evm.h>
/**
* inode_change_ok - check if attribute changes to an inode are allowed
@ -237,8 +238,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
else
error = simple_setattr(dentry, attr);
if (!error)
if (!error) {
fsnotify_change(dentry, ia_valid);
evm_inode_post_setattr(dentry, ia_valid);
}
return error;
}

View File

@ -374,36 +374,36 @@ int btrfs_removexattr(struct dentry *dentry, const char *name)
XATTR_REPLACE);
}
int btrfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
void *fs_info)
{
const struct xattr *xattr;
struct btrfs_trans_handle *trans = fs_info;
char *name;
int err = 0;
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
strlen(xattr->name) + 1, GFP_NOFS);
if (!name) {
err = -ENOMEM;
break;
}
strcpy(name, XATTR_SECURITY_PREFIX);
strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
err = __btrfs_setxattr(trans, inode, name,
xattr->value, xattr->value_len, 0);
kfree(name);
if (err < 0)
break;
}
return err;
}
int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
struct inode *inode, struct inode *dir,
const struct qstr *qstr)
{
int err;
size_t len;
void *value;
char *suffix;
char *name;
err = security_inode_init_security(inode, dir, qstr, &suffix, &value,
&len);
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
name = kmalloc(XATTR_SECURITY_PREFIX_LEN + strlen(suffix) + 1,
GFP_NOFS);
if (!name) {
err = -ENOMEM;
} else {
strcpy(name, XATTR_SECURITY_PREFIX);
strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix);
err = __btrfs_setxattr(trans, inode, name, value, len, 0);
kfree(name);
}
kfree(suffix);
kfree(value);
return err;
return security_inode_init_security(inode, dir, qstr,
&btrfs_initxattrs, trans);
}

View File

@ -46,26 +46,28 @@ ext2_xattr_security_set(struct dentry *dentry, const char *name,
value, size, flags);
}
int ext2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
void *fs_info)
{
const struct xattr *xattr;
int err = 0;
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,
xattr->name, xattr->value,
xattr->value_len, 0);
if (err < 0)
break;
}
return err;
}
int
ext2_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr)
{
int err;
size_t len;
void *value;
char *name;
err = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,
name, value, len, 0);
kfree(name);
kfree(value);
return err;
return security_inode_init_security(inode, dir, qstr,
&ext2_initxattrs, NULL);
}
const struct xattr_handler ext2_xattr_security_handler = {

View File

@ -48,26 +48,30 @@ ext3_xattr_security_set(struct dentry *dentry, const char *name,
name, value, size, flags);
}
int ext3_initxattrs(struct inode *inode, const struct xattr *xattr_array,
void *fs_info)
{
const struct xattr *xattr;
handle_t *handle = fs_info;
int err = 0;
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
err = ext3_xattr_set_handle(handle, inode,
EXT3_XATTR_INDEX_SECURITY,
xattr->name, xattr->value,
xattr->value_len, 0);
if (err < 0)
break;
}
return err;
}
int
ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
const struct qstr *qstr)
{
int err;
size_t len;
void *value;
char *name;
err = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
err = ext3_xattr_set_handle(handle, inode, EXT3_XATTR_INDEX_SECURITY,
name, value, len, 0);
kfree(name);
kfree(value);
return err;
return security_inode_init_security(inode, dir, qstr,
&ext3_initxattrs, handle);
}
const struct xattr_handler ext3_xattr_security_handler = {

View File

@ -48,26 +48,30 @@ ext4_xattr_security_set(struct dentry *dentry, const char *name,
name, value, size, flags);
}
int ext4_initxattrs(struct inode *inode, const struct xattr *xattr_array,
void *fs_info)
{
const struct xattr *xattr;
handle_t *handle = fs_info;
int err = 0;
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
err = ext4_xattr_set_handle(handle, inode,
EXT4_XATTR_INDEX_SECURITY,
xattr->name, xattr->value,
xattr->value_len, 0);
if (err < 0)
break;
}
return err;
}
int
ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
const struct qstr *qstr)
{
int err;
size_t len;
void *value;
char *name;
err = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
err = ext4_xattr_set_handle(handle, inode, EXT4_XATTR_INDEX_SECURITY,
name, value, len, 0);
kfree(name);
kfree(value);
return err;
return security_inode_init_security(inode, dir, qstr,
&ext4_initxattrs, handle);
}
const struct xattr_handler ext4_xattr_security_handler = {

View File

@ -624,29 +624,27 @@ fail:
return error;
}
int gfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
void *fs_info)
{
const struct xattr *xattr;
int err = 0;
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
err = __gfs2_xattr_set(inode, xattr->name, xattr->value,
xattr->value_len, 0,
GFS2_EATYPE_SECURITY);
if (err < 0)
break;
}
return err;
}
static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip,
const struct qstr *qstr)
{
int err;
size_t len;
void *value;
char *name;
err = security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr,
&name, &value, &len);
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
err = __gfs2_xattr_set(&ip->i_inode, name, value, len, 0,
GFS2_EATYPE_SECURITY);
kfree(value);
kfree(name);
return err;
return security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr,
&gfs2_initxattrs, NULL);
}
/**

View File

@ -22,26 +22,29 @@
#include <linux/security.h>
#include "nodelist.h"
/* ---- Initial Security Label Attachment -------------- */
/* ---- Initial Security Label(s) Attachment callback --- */
int jffs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
void *fs_info)
{
const struct xattr *xattr;
int err = 0;
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
err = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY,
xattr->name, xattr->value,
xattr->value_len, 0);
if (err < 0)
break;
}
return err;
}
/* ---- Initial Security Label(s) Attachment ----------- */
int jffs2_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr)
{
int rc;
size_t len;
void *value;
char *name;
rc = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
if (rc) {
if (rc == -EOPNOTSUPP)
return 0;
return rc;
}
rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0);
kfree(name);
kfree(value);
return rc;
return security_inode_init_security(inode, dir, qstr,
&jffs2_initxattrs, NULL);
}
/* ---- XATTR Handler for "security.*" ----------------- */

View File

@ -1089,38 +1089,37 @@ int jfs_removexattr(struct dentry *dentry, const char *name)
}
#ifdef CONFIG_JFS_SECURITY
int jfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
void *fs_info)
{
const struct xattr *xattr;
tid_t *tid = fs_info;
char *name;
int err = 0;
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
strlen(xattr->name) + 1, GFP_NOFS);
if (!name) {
err = -ENOMEM;
break;
}
strcpy(name, XATTR_SECURITY_PREFIX);
strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
err = __jfs_setxattr(*tid, inode, name,
xattr->value, xattr->value_len, 0);
kfree(name);
if (err < 0)
break;
}
return err;
}
int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir,
const struct qstr *qstr)
{
int rc;
size_t len;
void *value;
char *suffix;
char *name;
rc = security_inode_init_security(inode, dir, qstr, &suffix, &value,
&len);
if (rc) {
if (rc == -EOPNOTSUPP)
return 0;
return rc;
}
name = kmalloc(XATTR_SECURITY_PREFIX_LEN + 1 + strlen(suffix),
GFP_NOFS);
if (!name) {
rc = -ENOMEM;
goto kmalloc_failed;
}
strcpy(name, XATTR_SECURITY_PREFIX);
strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix);
rc = __jfs_setxattr(tid, inode, name, value, len, 0);
kfree(name);
kmalloc_failed:
kfree(suffix);
kfree(value);
return rc;
return security_inode_init_security(inode, dir, qstr,
&jfs_initxattrs, &tid);
}
#endif

View File

@ -7185,20 +7185,9 @@ int ocfs2_init_security_and_acl(struct inode *dir,
{
int ret = 0;
struct buffer_head *dir_bh = NULL;
struct ocfs2_security_xattr_info si = {
.enable = 1,
};
ret = ocfs2_init_security_get(inode, dir, qstr, &si);
ret = ocfs2_init_security_get(inode, dir, qstr, NULL);
if (!ret) {
ret = ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_SECURITY,
si.name, si.value, si.value_len,
XATTR_CREATE);
if (ret) {
mlog_errno(ret);
goto leave;
}
} else if (ret != -EOPNOTSUPP) {
mlog_errno(ret);
goto leave;
}
@ -7255,6 +7244,22 @@ static int ocfs2_xattr_security_set(struct dentry *dentry, const char *name,
name, value, size, flags);
}
int ocfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
void *fs_info)
{
const struct xattr *xattr;
int err = 0;
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
err = ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_SECURITY,
xattr->name, xattr->value,
xattr->value_len, XATTR_CREATE);
if (err)
break;
}
return err;
}
int ocfs2_init_security_get(struct inode *inode,
struct inode *dir,
const struct qstr *qstr,
@ -7263,8 +7268,13 @@ int ocfs2_init_security_get(struct inode *inode,
/* check whether ocfs2 support feature xattr */
if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb)))
return -EOPNOTSUPP;
return security_inode_init_security(inode, dir, qstr, &si->name,
&si->value, &si->value_len);
if (si)
return security_old_inode_init_security(inode, dir, qstr,
&si->name, &si->value,
&si->value_len);
return security_inode_init_security(inode, dir, qstr,
&ocfs2_initxattrs, NULL);
}
int ocfs2_init_security_set(handle_t *handle,

View File

@ -66,8 +66,8 @@ int reiserfs_security_init(struct inode *dir, struct inode *inode,
if (IS_PRIVATE(dir))
return 0;
error = security_inode_init_security(inode, dir, qstr, &sec->name,
&sec->value, &sec->length);
error = security_old_inode_init_security(inode, dir, qstr, &sec->name,
&sec->value, &sec->length);
if (error) {
if (error == -EOPNOTSUPP)
error = 0;

View File

@ -14,6 +14,7 @@
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/security.h>
#include <linux/evm.h>
#include <linux/syscalls.h>
#include <linux/module.h>
#include <linux/fsnotify.h>
@ -166,6 +167,64 @@ out_noalloc:
}
EXPORT_SYMBOL_GPL(xattr_getsecurity);
/*
* vfs_getxattr_alloc - allocate memory, if necessary, before calling getxattr
*
* Allocate memory, if not already allocated, or re-allocate correct size,
* before retrieving the extended attribute.
*
* Returns the result of alloc, if failed, or the getxattr operation.
*/
ssize_t
vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
size_t xattr_size, gfp_t flags)
{
struct inode *inode = dentry->d_inode;
char *value = *xattr_value;
int error;
error = xattr_permission(inode, name, MAY_READ);
if (error)
return error;
if (!inode->i_op->getxattr)
return -EOPNOTSUPP;
error = inode->i_op->getxattr(dentry, name, NULL, 0);
if (error < 0)
return error;
if (!value || (error > xattr_size)) {
value = krealloc(*xattr_value, error + 1, flags);
if (!value)
return -ENOMEM;
memset(value, 0, error + 1);
}
error = inode->i_op->getxattr(dentry, name, value, error);
*xattr_value = value;
return error;
}
/* Compare an extended attribute value with the given value */
int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
const char *value, size_t size, gfp_t flags)
{
char *xattr_value = NULL;
int rc;
rc = vfs_getxattr_alloc(dentry, xattr_name, &xattr_value, 0, flags);
if (rc < 0)
return rc;
if ((rc != size) || (memcmp(xattr_value, value, rc) != 0))
rc = -EINVAL;
else
rc = 0;
kfree(xattr_value);
return rc;
}
ssize_t
vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
{
@ -243,8 +302,10 @@ vfs_removexattr(struct dentry *dentry, const char *name)
error = inode->i_op->removexattr(dentry, name);
mutex_unlock(&inode->i_mutex);
if (!error)
if (!error) {
fsnotify_xattr(dentry);
evm_inode_post_removexattr(dentry, name);
}
return error;
}
EXPORT_SYMBOL_GPL(vfs_removexattr);

View File

@ -94,37 +94,38 @@ xfs_mark_inode_dirty(
mark_inode_dirty(inode);
}
int xfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
void *fs_info)
{
const struct xattr *xattr;
struct xfs_inode *ip = XFS_I(inode);
int error = 0;
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
error = xfs_attr_set(ip, xattr->name, xattr->value,
xattr->value_len, ATTR_SECURE);
if (error < 0)
break;
}
return error;
}
/*
* Hook in SELinux. This is not quite correct yet, what we really need
* here (as we do for default ACLs) is a mechanism by which creation of
* these attrs can be journalled at inode creation time (along with the
* inode, of course, such that log replay can't cause these to be lost).
*/
STATIC int
xfs_init_security(
struct inode *inode,
struct inode *dir,
const struct qstr *qstr)
{
struct xfs_inode *ip = XFS_I(inode);
size_t length;
void *value;
unsigned char *name;
int error;
error = security_inode_init_security(inode, dir, qstr, (char **)&name,
&value, &length);
if (error) {
if (error == -EOPNOTSUPP)
return 0;
return -error;
}
error = xfs_attr_set(ip, name, value, length, ATTR_SECURE);
kfree(name);
kfree(value);
return error;
return security_inode_init_security(inode, dir, qstr,
&xfs_initxattrs, NULL);
}
static void

View File

@ -0,0 +1,92 @@
/*
* evm.h
*
* Copyright (c) 2009 IBM Corporation
* Author: Mimi Zohar <zohar@us.ibm.com>
*/
#ifndef _LINUX_EVM_H
#define _LINUX_EVM_H
#include <linux/integrity.h>
#include <linux/xattr.h>
struct integrity_iint_cache;
#ifdef CONFIG_EVM
extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
const char *xattr_name,
void *xattr_value,
size_t xattr_value_len,
struct integrity_iint_cache *iint);
extern int evm_inode_setattr(struct dentry *dentry, struct iattr *attr);
extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid);
extern int evm_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size);
extern void evm_inode_post_setxattr(struct dentry *dentry,
const char *xattr_name,
const void *xattr_value,
size_t xattr_value_len);
extern int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name);
extern void evm_inode_post_removexattr(struct dentry *dentry,
const char *xattr_name);
extern int evm_inode_init_security(struct inode *inode,
const struct xattr *xattr_array,
struct xattr *evm);
#else
#ifdef CONFIG_INTEGRITY
static inline enum integrity_status evm_verifyxattr(struct dentry *dentry,
const char *xattr_name,
void *xattr_value,
size_t xattr_value_len,
struct integrity_iint_cache *iint)
{
return INTEGRITY_UNKNOWN;
}
#endif
static int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
{
return 0;
}
static inline void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
{
return;
}
static inline int evm_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size)
{
return 0;
}
static inline void evm_inode_post_setxattr(struct dentry *dentry,
const char *xattr_name,
const void *xattr_value,
size_t xattr_value_len)
{
return;
}
static inline int evm_inode_removexattr(struct dentry *dentry,
const char *xattr_name)
{
return 0;
}
static inline void evm_inode_post_removexattr(struct dentry *dentry,
const char *xattr_name)
{
return;
}
static inline int evm_inode_init_security(struct inode *inode,
const struct xattr *xattr_array,
struct xattr *evm)
{
return -EOPNOTSUPP;
}
#endif /* CONFIG_EVM_H */
#endif /* LINUX_EVM_H */

View File

@ -15,8 +15,6 @@ struct linux_binprm;
#ifdef CONFIG_IMA
extern int ima_bprm_check(struct linux_binprm *bprm);
extern int ima_inode_alloc(struct inode *inode);
extern void ima_inode_free(struct inode *inode);
extern int ima_file_check(struct file *file, int mask);
extern void ima_file_free(struct file *file);
extern int ima_file_mmap(struct file *file, unsigned long prot);
@ -27,16 +25,6 @@ static inline int ima_bprm_check(struct linux_binprm *bprm)
return 0;
}
static inline int ima_inode_alloc(struct inode *inode)
{
return 0;
}
static inline void ima_inode_free(struct inode *inode)
{
return;
}
static inline int ima_file_check(struct file *file, int mask)
{
return 0;
@ -51,6 +39,5 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
{
return 0;
}
#endif /* CONFIG_IMA_H */
#endif /* _LINUX_IMA_H */

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2009 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.
*/
#ifndef _LINUX_INTEGRITY_H
#define _LINUX_INTEGRITY_H
#include <linux/fs.h>
enum integrity_status {
INTEGRITY_PASS = 0,
INTEGRITY_FAIL,
INTEGRITY_NOLABEL,
INTEGRITY_UNKNOWN,
};
/* List of EVM protected security xattrs */
#ifdef CONFIG_INTEGRITY
extern int integrity_inode_alloc(struct inode *inode);
extern void integrity_inode_free(struct inode *inode);
#else
static inline int integrity_inode_alloc(struct inode *inode)
{
return 0;
}
static inline void integrity_inode_free(struct inode *inode)
{
return;
}
#endif /* CONFIG_INTEGRITY_H */
#endif /* _LINUX_INTEGRITY_H */

View File

@ -36,6 +36,7 @@
#include <linux/key.h>
#include <linux/xfrm.h>
#include <linux/slab.h>
#include <linux/xattr.h>
#include <net/flow.h>
/* Maximum number of letters for an LSM name string */
@ -147,6 +148,10 @@ extern int mmap_min_addr_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos);
#endif
/* security_inode_init_security callback function to write xattrs */
typedef int (*initxattrs) (struct inode *inode,
const struct xattr *xattr_array, void *fs_data);
#ifdef CONFIG_SECURITY
struct security_mnt_opts {
@ -1704,8 +1709,11 @@ int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
int security_inode_alloc(struct inode *inode);
void security_inode_free(struct inode *inode);
int security_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, char **name,
void **value, size_t *len);
const struct qstr *qstr,
initxattrs initxattrs, void *fs_data);
int security_old_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, char **name,
void **value, size_t *len);
int security_inode_create(struct inode *dir, struct dentry *dentry, int mode);
int security_inode_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry);
@ -2034,9 +2042,8 @@ static inline void security_inode_free(struct inode *inode)
static inline int security_inode_init_security(struct inode *inode,
struct inode *dir,
const struct qstr *qstr,
char **name,
void **value,
size_t *len)
initxattrs initxattrs,
void *fs_data)
{
return -EOPNOTSUPP;
}

View File

@ -30,6 +30,9 @@
#define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1)
/* Security namespace */
#define XATTR_EVM_SUFFIX "evm"
#define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
@ -67,6 +70,12 @@ struct xattr_handler {
size_t size, int flags, int handler_flags);
};
struct xattr {
char *name;
void *value;
size_t value_len;
};
ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
@ -78,7 +87,10 @@ ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer,
ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
int generic_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags);
int generic_removexattr(struct dentry *dentry, const char *name);
ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name,
char **xattr_value, size_t size, gfp_t flags);
int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
const char *value, size_t size, gfp_t flags);
#endif /* __KERNEL__ */
#endif /* _LINUX_XATTR_H */

View File

@ -1458,7 +1458,7 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);
if (inode) {
error = security_inode_init_security(inode, dir,
&dentry->d_name, NULL,
&dentry->d_name,
NULL, NULL);
if (error) {
if (error != -EOPNOTSUPP) {
@ -1598,7 +1598,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
if (!inode)
return -ENOSPC;
error = security_inode_init_security(inode, dir, &dentry->d_name, NULL,
error = security_inode_init_security(inode, dir, &dentry->d_name,
NULL, NULL);
if (error) {
if (error != -EOPNOTSUPP) {

View File

@ -186,7 +186,7 @@ source security/smack/Kconfig
source security/tomoyo/Kconfig
source security/apparmor/Kconfig
source security/integrity/ima/Kconfig
source security/integrity/Kconfig
choice
prompt "Default security module"

View File

@ -24,5 +24,5 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
# Object integrity file lists
subdir-$(CONFIG_IMA) += integrity/ima
obj-$(CONFIG_IMA) += integrity/ima/built-in.o
subdir-$(CONFIG_INTEGRITY) += integrity
obj-$(CONFIG_INTEGRITY) += integrity/built-in.o

View File

@ -0,0 +1,7 @@
#
config INTEGRITY
def_bool y
depends on IMA || EVM
source security/integrity/ima/Kconfig
source security/integrity/evm/Kconfig

View File

@ -0,0 +1,12 @@
#
# Makefile for caching inode integrity data (iint)
#
obj-$(CONFIG_INTEGRITY) += integrity.o
integrity-y := iint.o
subdir-$(CONFIG_IMA) += ima
obj-$(CONFIG_IMA) += ima/built-in.o
subdir-$(CONFIG_EVM) += evm
obj-$(CONFIG_EVM) += evm/built-in.o

View File

@ -0,0 +1,12 @@
config EVM
boolean "EVM support"
depends on SECURITY && KEYS && ENCRYPTED_KEYS
select CRYPTO_HMAC
select CRYPTO_MD5
select CRYPTO_SHA1
default n
help
EVM protects a file's security extended attributes against
integrity attacks.
If you are unsure how to answer this question, answer N.

View File

@ -0,0 +1,6 @@
#
# Makefile for building the Extended Verification Module(EVM)
#
obj-$(CONFIG_EVM) += evm.o
evm-y := evm_main.o evm_crypto.o evm_secfs.o

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2005-2010 IBM Corporation
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
* Kylene Hall <kjhall@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.
*
* File: evm.h
*
*/
#include <linux/xattr.h>
#include <linux/security.h>
#include "../integrity.h"
extern int evm_initialized;
extern char *evm_hmac;
extern struct crypto_shash *hmac_tfm;
/* List of EVM protected security xattrs */
extern char *evm_config_xattrnames[];
extern int evm_init_key(void);
extern int evm_update_evmxattr(struct dentry *dentry,
const char *req_xattr_name,
const char *req_xattr_value,
size_t req_xattr_value_len);
extern int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value,
size_t req_xattr_value_len, char *digest);
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);

View File

@ -0,0 +1,216 @@
/*
* Copyright (C) 2005-2010 IBM Corporation
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
* Kylene Hall <kjhall@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.
*
* File: evm_crypto.c
* Using root's kernel master key (kmk), calculate the HMAC
*/
#include <linux/module.h>
#include <linux/crypto.h>
#include <linux/xattr.h>
#include <keys/encrypted-type.h>
#include <crypto/hash.h>
#include "evm.h"
#define EVMKEY "evm-key"
#define MAX_KEY_SIZE 128
static unsigned char evmkey[MAX_KEY_SIZE];
static int evmkey_len = MAX_KEY_SIZE;
struct crypto_shash *hmac_tfm;
static struct shash_desc *init_desc(void)
{
int rc;
struct shash_desc *desc;
if (hmac_tfm == NULL) {
hmac_tfm = crypto_alloc_shash(evm_hmac, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(hmac_tfm)) {
pr_err("Can not allocate %s (reason: %ld)\n",
evm_hmac, PTR_ERR(hmac_tfm));
rc = PTR_ERR(hmac_tfm);
hmac_tfm = NULL;
return ERR_PTR(rc);
}
}
desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(hmac_tfm),
GFP_KERNEL);
if (!desc)
return ERR_PTR(-ENOMEM);
desc->tfm = hmac_tfm;
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
rc = crypto_shash_setkey(hmac_tfm, evmkey, evmkey_len);
if (rc)
goto out;
rc = crypto_shash_init(desc);
out:
if (rc) {
kfree(desc);
return ERR_PTR(rc);
}
return desc;
}
/* Protect against 'cutting & pasting' security.evm xattr, include inode
* specific info.
*
* (Additional directory/file metadata needs to be added for more complete
* protection.)
*/
static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
char *digest)
{
struct h_misc {
unsigned long ino;
__u32 generation;
uid_t uid;
gid_t gid;
umode_t mode;
} hmac_misc;
memset(&hmac_misc, 0, sizeof hmac_misc);
hmac_misc.ino = inode->i_ino;
hmac_misc.generation = inode->i_generation;
hmac_misc.uid = inode->i_uid;
hmac_misc.gid = inode->i_gid;
hmac_misc.mode = inode->i_mode;
crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc);
crypto_shash_final(desc, digest);
}
/*
* Calculate the HMAC value across the set of protected security xattrs.
*
* Instead of retrieving the requested xattr, for performance, calculate
* the hmac using the requested xattr value. Don't alloc/free memory for
* each xattr, but attempt to re-use the previously allocated memory.
*/
int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, size_t req_xattr_value_len,
char *digest)
{
struct inode *inode = dentry->d_inode;
struct shash_desc *desc;
char **xattrname;
size_t xattr_size = 0;
char *xattr_value = NULL;
int error;
int size;
if (!inode->i_op || !inode->i_op->getxattr)
return -EOPNOTSUPP;
desc = init_desc();
if (IS_ERR(desc))
return PTR_ERR(desc);
error = -ENODATA;
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
if ((req_xattr_name && req_xattr_value)
&& !strcmp(*xattrname, req_xattr_name)) {
error = 0;
crypto_shash_update(desc, (const u8 *)req_xattr_value,
req_xattr_value_len);
continue;
}
size = vfs_getxattr_alloc(dentry, *xattrname,
&xattr_value, xattr_size, GFP_NOFS);
if (size == -ENOMEM) {
error = -ENOMEM;
goto out;
}
if (size < 0)
continue;
error = 0;
xattr_size = size;
crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
}
hmac_add_misc(desc, inode, digest);
out:
kfree(xattr_value);
kfree(desc);
return error;
}
/*
* Calculate the hmac and update security.evm xattr
*
* Expects to be called with i_mutex locked.
*/
int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
const char *xattr_value, size_t xattr_value_len)
{
struct inode *inode = dentry->d_inode;
struct evm_ima_xattr_data xattr_data;
int rc = 0;
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, xattr_data.digest);
if (rc == 0) {
xattr_data.type = EVM_XATTR_HMAC;
rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM,
&xattr_data,
sizeof(xattr_data), 0);
}
else if (rc == -ENODATA)
rc = inode->i_op->removexattr(dentry, XATTR_NAME_EVM);
return rc;
}
int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
char *hmac_val)
{
struct shash_desc *desc;
desc = init_desc();
if (IS_ERR(desc)) {
printk(KERN_INFO "init_desc failed\n");
return PTR_ERR(desc);
}
crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len);
hmac_add_misc(desc, inode, hmac_val);
kfree(desc);
return 0;
}
/*
* Get the key from the TPM for the SHA1-HMAC
*/
int evm_init_key(void)
{
struct key *evm_key;
struct encrypted_key_payload *ekp;
int rc = 0;
evm_key = request_key(&key_type_encrypted, EVMKEY, NULL);
if (IS_ERR(evm_key))
return -ENOENT;
down_read(&evm_key->sem);
ekp = evm_key->payload.data;
if (ekp->decrypted_datalen > MAX_KEY_SIZE) {
rc = -EINVAL;
goto out;
}
memcpy(evmkey, ekp->decrypted_data, ekp->decrypted_datalen);
out:
/* burn the original key contents */
memset(ekp->decrypted_data, 0, ekp->decrypted_datalen);
up_read(&evm_key->sem);
key_put(evm_key);
return rc;
}

View File

@ -0,0 +1,384 @@
/*
* Copyright (C) 2005-2010 IBM Corporation
*
* Author:
* Mimi Zohar <zohar@us.ibm.com>
* Kylene Hall <kjhall@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.
*
* File: evm_main.c
* implements evm_inode_setxattr, evm_inode_post_setxattr,
* evm_inode_removexattr, and evm_verifyxattr
*/
#include <linux/module.h>
#include <linux/crypto.h>
#include <linux/xattr.h>
#include <linux/integrity.h>
#include <linux/evm.h>
#include <crypto/hash.h>
#include "evm.h"
int evm_initialized;
char *evm_hmac = "hmac(sha1)";
char *evm_config_xattrnames[] = {
#ifdef CONFIG_SECURITY_SELINUX
XATTR_NAME_SELINUX,
#endif
#ifdef CONFIG_SECURITY_SMACK
XATTR_NAME_SMACK,
#endif
XATTR_NAME_CAPS,
NULL
};
static int evm_fixmode;
static int __init evm_set_fixmode(char *str)
{
if (strncmp(str, "fix", 3) == 0)
evm_fixmode = 1;
return 0;
}
__setup("evm=", evm_set_fixmode);
/*
* evm_verify_hmac - calculate and compare the HMAC with the EVM xattr
*
* Compute the HMAC on the dentry's protected set of extended attributes
* and compare it against the stored security.evm xattr.
*
* For performance:
* - use the previoulsy retrieved xattr value and length to calculate the
* HMAC.)
* - cache the verification result in the iint, when available.
*
* Returns integrity status
*/
static enum integrity_status evm_verify_hmac(struct dentry *dentry,
const char *xattr_name,
char *xattr_value,
size_t xattr_value_len,
struct integrity_iint_cache *iint)
{
struct evm_ima_xattr_data xattr_data;
enum integrity_status evm_status;
int rc;
if (iint && iint->evm_status == INTEGRITY_PASS)
return iint->evm_status;
/* if status is not PASS, try to check again - against -ENOMEM */
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, xattr_data.digest);
if (rc < 0)
goto err_out;
xattr_data.type = EVM_XATTR_HMAC;
rc = vfs_xattr_cmp(dentry, XATTR_NAME_EVM, (u8 *)&xattr_data,
sizeof xattr_data, GFP_NOFS);
if (rc < 0)
goto err_out;
evm_status = INTEGRITY_PASS;
goto out;
err_out:
switch (rc) {
case -ENODATA: /* file not labelled */
evm_status = INTEGRITY_NOLABEL;
break;
default:
evm_status = INTEGRITY_FAIL;
}
out:
if (iint)
iint->evm_status = evm_status;
return evm_status;
}
static int evm_protected_xattr(const char *req_xattr_name)
{
char **xattrname;
int namelen;
int found = 0;
namelen = strlen(req_xattr_name);
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
if ((strlen(*xattrname) == namelen)
&& (strncmp(req_xattr_name, *xattrname, namelen) == 0)) {
found = 1;
break;
}
if (strncmp(req_xattr_name,
*xattrname + XATTR_SECURITY_PREFIX_LEN,
strlen(req_xattr_name)) == 0) {
found = 1;
break;
}
}
return found;
}
/**
* evm_verifyxattr - verify the integrity of the requested xattr
* @dentry: object of the verify xattr
* @xattr_name: requested xattr
* @xattr_value: requested xattr value
* @xattr_value_len: requested xattr value length
*
* Calculate the HMAC for the given dentry and verify it against the stored
* security.evm xattr. For performance, use the xattr value and length
* previously retrieved to calculate the HMAC.
*
* Returns the xattr integrity status.
*
* This function requires the caller to lock the inode's i_mutex before it
* is executed.
*/
enum integrity_status evm_verifyxattr(struct dentry *dentry,
const char *xattr_name,
void *xattr_value, size_t xattr_value_len,
struct integrity_iint_cache *iint)
{
if (!evm_initialized || !evm_protected_xattr(xattr_name))
return INTEGRITY_UNKNOWN;
if (!iint) {
iint = integrity_iint_find(dentry->d_inode);
if (!iint)
return INTEGRITY_UNKNOWN;
}
return evm_verify_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, iint);
}
EXPORT_SYMBOL_GPL(evm_verifyxattr);
/*
* evm_protect_xattr - protect the EVM extended attribute
*
* Prevent security.evm from being modified or removed.
*/
static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
}
return 0;
}
/*
* evm_verify_current_integrity - verify the dentry's metadata integrity
* @dentry: pointer to the affected dentry
*
* Verify and return the dentry's metadata integrity. The exceptions are
* before EVM is initialized or in 'fix' mode.
*/
static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
if (!evm_initialized || !S_ISREG(inode->i_mode) || evm_fixmode)
return 0;
return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
}
/**
* evm_inode_setxattr - protect the EVM extended attribute
* @dentry: pointer to the affected dentry
* @xattr_name: pointer to the affected extended attribute name
* @xattr_value: pointer to the new extended attribute value
* @xattr_value_len: pointer to the new extended attribute value length
*
* Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that
* the current value is valid.
*/
int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
enum integrity_status evm_status;
int ret;
ret = evm_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len);
if (ret)
return ret;
evm_status = evm_verify_current_integrity(dentry);
return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
}
/**
* evm_inode_removexattr - protect the EVM extended attribute
* @dentry: pointer to the affected dentry
* @xattr_name: pointer to the affected extended attribute name
*
* Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that
* the current value is valid.
*/
int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name)
{
enum integrity_status evm_status;
int ret;
ret = evm_protect_xattr(dentry, xattr_name, NULL, 0);
if (ret)
return ret;
evm_status = evm_verify_current_integrity(dentry);
return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
}
/**
* evm_inode_post_setxattr - update 'security.evm' to reflect the changes
* @dentry: pointer to the affected dentry
* @xattr_name: pointer to the affected extended attribute name
* @xattr_value: pointer to the new extended attribute value
* @xattr_value_len: pointer to the new extended attribute value length
*
* Update the HMAC stored in 'security.evm' to reflect the change.
*
* No need to take the i_mutex lock here, as this function is called from
* __vfs_setxattr_noperm(). The caller of which has taken the inode's
* i_mutex lock.
*/
void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
if (!evm_initialized || !evm_protected_xattr(xattr_name))
return;
evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len);
return;
}
/**
* evm_inode_post_removexattr - update 'security.evm' after removing the xattr
* @dentry: pointer to the affected dentry
* @xattr_name: pointer to the affected extended attribute name
*
* Update the HMAC stored in 'security.evm' to reflect removal of the xattr.
*/
void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
{
struct inode *inode = dentry->d_inode;
if (!evm_initialized || !evm_protected_xattr(xattr_name))
return;
mutex_lock(&inode->i_mutex);
evm_update_evmxattr(dentry, xattr_name, NULL, 0);
mutex_unlock(&inode->i_mutex);
return;
}
/**
* evm_inode_setattr - prevent updating an invalid EVM extended attribute
* @dentry: pointer to the affected dentry
*/
int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;
enum integrity_status evm_status;
if (ia_valid & ~(ATTR_MODE | ATTR_UID | ATTR_GID))
return 0;
evm_status = evm_verify_current_integrity(dentry);
return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
}
/**
* evm_inode_post_setattr - update 'security.evm' after modifying metadata
* @dentry: pointer to the affected dentry
* @ia_valid: for the UID and GID status
*
* For now, update the HMAC stored in 'security.evm' to reflect UID/GID
* changes.
*
* This function is called from notify_change(), which expects the caller
* to lock the inode's i_mutex.
*/
void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
{
if (!evm_initialized)
return;
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
evm_update_evmxattr(dentry, NULL, NULL, 0);
return;
}
/*
* evm_inode_init_security - initializes security.evm
*/
int evm_inode_init_security(struct inode *inode,
const struct xattr *lsm_xattr,
struct xattr *evm_xattr)
{
struct evm_ima_xattr_data *xattr_data;
int rc;
if (!evm_initialized || !evm_protected_xattr(lsm_xattr->name))
return -EOPNOTSUPP;
xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS);
if (!xattr_data)
return -ENOMEM;
xattr_data->type = EVM_XATTR_HMAC;
rc = evm_init_hmac(inode, lsm_xattr, xattr_data->digest);
if (rc < 0)
goto out;
evm_xattr->value = xattr_data;
evm_xattr->value_len = sizeof(*xattr_data);
evm_xattr->name = kstrdup(XATTR_EVM_SUFFIX, GFP_NOFS);
return 0;
out:
kfree(xattr_data);
return rc;
}
EXPORT_SYMBOL_GPL(evm_inode_init_security);
static int __init init_evm(void)
{
int error;
error = evm_init_secfs();
if (error < 0) {
printk(KERN_INFO "EVM: Error registering secfs\n");
goto err;
}
err:
return error;
}
static void __exit cleanup_evm(void)
{
evm_cleanup_secfs();
if (hmac_tfm)
crypto_free_shash(hmac_tfm);
}
/*
* evm_display_config - list the EVM protected security extended attributes
*/
static int __init evm_display_config(void)
{
char **xattrname;
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++)
printk(KERN_INFO "EVM: %s\n", *xattrname);
return 0;
}
pure_initcall(evm_display_config);
late_initcall(init_evm);
MODULE_DESCRIPTION("Extended Verification Module");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2010 IBM Corporation
*
* Authors:
* 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.
*
* File: evm_secfs.c
* - Used to signal when key is on keyring
* - Get the key and enable EVM
*/
#include <linux/uaccess.h>
#include <linux/module.h>
#include "evm.h"
static struct dentry *evm_init_tpm;
/**
* evm_read_key - read() for <securityfs>/evm
*
* @filp: file pointer, not actually used
* @buf: where to put the result
* @count: maximum to send along
* @ppos: where to start
*
* Returns number of bytes read or error code, as appropriate
*/
static ssize_t evm_read_key(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
char temp[80];
ssize_t rc;
if (*ppos != 0)
return 0;
sprintf(temp, "%d", evm_initialized);
rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
return rc;
}
/**
* evm_write_key - write() for <securityfs>/evm
* @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
*
* Used to signal that key is on the kernel key ring.
* - get the integrity hmac key from the kernel key ring
* - create list of hmac protected extended attributes
* Returns number of bytes written or error code, as appropriate
*/
static ssize_t evm_write_key(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char temp[80];
int i, error;
if (!capable(CAP_SYS_ADMIN) || evm_initialized)
return -EPERM;
if (count >= sizeof(temp) || count == 0)
return -EINVAL;
if (copy_from_user(temp, buf, count) != 0)
return -EFAULT;
temp[count] = '\0';
if ((sscanf(temp, "%d", &i) != 1) || (i != 1))
return -EINVAL;
error = evm_init_key();
if (!error) {
evm_initialized = 1;
pr_info("EVM: initialized\n");
} else
pr_err("EVM: initialization failed\n");
return count;
}
static const struct file_operations evm_key_ops = {
.read = evm_read_key,
.write = evm_write_key,
};
int __init evm_init_secfs(void)
{
int error = 0;
evm_init_tpm = securityfs_create_file("evm", S_IRUSR | S_IRGRP,
NULL, NULL, &evm_key_ops);
if (!evm_init_tpm || IS_ERR(evm_init_tpm))
error = -EFAULT;
return error;
}
void __exit evm_cleanup_secfs(void)
{
if (evm_init_tpm)
securityfs_remove(evm_init_tpm);
}

View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 2008 IBM Corporation
*
* Authors:
* 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.
*
* File: integrity_iint.c
* - implements the integrity hooks: integrity_inode_alloc,
* integrity_inode_free
* - cache integrity information associated with an inode
* using a rbtree tree.
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/rbtree.h>
#include "integrity.h"
static struct rb_root integrity_iint_tree = RB_ROOT;
static DEFINE_SPINLOCK(integrity_iint_lock);
static struct kmem_cache *iint_cache __read_mostly;
int iint_initialized;
/*
* __integrity_iint_find - return the iint associated with an inode
*/
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);
if (inode < iint->inode)
n = n->rb_left;
else if (inode > iint->inode)
n = n->rb_right;
else
break;
}
if (!n)
return NULL;
return iint;
}
/*
* integrity_iint_find - return the iint associated with an inode
*/
struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
{
struct integrity_iint_cache *iint;
if (!IS_IMA(inode))
return NULL;
spin_lock(&integrity_iint_lock);
iint = __integrity_iint_find(inode);
spin_unlock(&integrity_iint_lock);
return iint;
}
static void iint_free(struct integrity_iint_cache *iint)
{
iint->version = 0;
iint->flags = 0UL;
kmem_cache_free(iint_cache, iint);
}
/**
* integrity_inode_alloc - allocate an iint associated with an inode
* @inode: pointer to the inode
*/
int integrity_inode_alloc(struct inode *inode)
{
struct rb_node **p;
struct rb_node *new_node, *parent = NULL;
struct integrity_iint_cache *new_iint, *test_iint;
int rc;
new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
if (!new_iint)
return -ENOMEM;
new_iint->inode = inode;
new_node = &new_iint->rb_node;
mutex_lock(&inode->i_mutex); /* i_flags */
spin_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;
}
inode->i_flags |= S_IMA;
rb_link_node(new_node, parent, p);
rb_insert_color(new_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;
}
/**
* integrity_inode_free - called on security_inode_free
* @inode: pointer to the inode
*
* Free the integrity information(iint) associated with an inode.
*/
void integrity_inode_free(struct inode *inode)
{
struct integrity_iint_cache *iint;
if (!IS_IMA(inode))
return;
spin_lock(&integrity_iint_lock);
iint = __integrity_iint_find(inode);
rb_erase(&iint->rb_node, &integrity_iint_tree);
spin_unlock(&integrity_iint_lock);
iint_free(iint);
}
static void init_once(void *foo)
{
struct integrity_iint_cache *iint = foo;
memset(iint, 0, sizeof *iint);
iint->version = 0;
iint->flags = 0UL;
mutex_init(&iint->mutex);
iint->evm_status = INTEGRITY_UNKNOWN;
}
static int __init integrity_iintcache_init(void)
{
iint_cache =
kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
0, SLAB_PANIC, init_once);
iint_initialized = 1;
return 0;
}
security_initcall(integrity_iintcache_init);

View File

@ -3,6 +3,7 @@
config IMA
bool "Integrity Measurement Architecture(IMA)"
depends on SECURITY
select INTEGRITY
select SECURITYFS
select CRYPTO
select CRYPTO_HMAC

View File

@ -6,4 +6,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_iint.o ima_audit.o
ima_policy.o ima_audit.o

View File

@ -24,11 +24,13 @@
#include <linux/tpm.h>
#include <linux/audit.h>
#include "../integrity.h"
enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
/* digest size for IMA, fits SHA1 or MD5 */
#define IMA_DIGEST_SIZE 20
#define IMA_DIGEST_SIZE SHA1_DIGEST_SIZE
#define IMA_EVENT_NAME_LEN_MAX 255
#define IMA_HASH_BITS 9
@ -96,34 +98,21 @@ static inline unsigned long ima_hash_key(u8 *digest)
return hash_long(*digest, IMA_HASH_BITS);
}
/* iint cache flags */
#define IMA_MEASURED 0x01
/* integrity data associated with an inode */
struct ima_iint_cache {
struct rb_node rb_node; /* rooted in ima_iint_tree */
struct inode *inode; /* back pointer to inode in question */
u64 version; /* track inode changes */
unsigned char flags;
u8 digest[IMA_DIGEST_SIZE];
struct mutex mutex; /* protects: version, flags, digest */
};
/* LIM API function definitions */
int ima_must_measure(struct inode *inode, int mask, int function);
int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file);
void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
int ima_collect_measurement(struct integrity_iint_cache *iint,
struct file *file);
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
const unsigned char *filename);
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);
void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
/* rbtree tree calls to lookup, insert, delete
* integrity data associated with an inode.
*/
struct ima_iint_cache *ima_iint_insert(struct inode *inode);
struct ima_iint_cache *ima_iint_find(struct inode *inode);
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 };

View File

@ -126,7 +126,8 @@ int ima_must_measure(struct inode *inode, int mask, int function)
*
* Return 0 on success, error code otherwise
*/
int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
int ima_collect_measurement(struct integrity_iint_cache *iint,
struct file *file)
{
int result = -EEXIST;
@ -156,8 +157,8 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
*
* Must be called with iint->mutex held.
*/
void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
const unsigned char *filename)
void ima_store_measurement(struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename)
{
const char *op = "add_template_measure";
const char *audit_cause = "ENOMEM";

View File

@ -1,169 +0,0 @@
/*
* Copyright (C) 2008 IBM Corporation
*
* Authors:
* 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.
*
* File: ima_iint.c
* - implements the IMA hooks: ima_inode_alloc, ima_inode_free
* - cache integrity information associated with an inode
* using a rbtree tree.
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/rbtree.h>
#include "ima.h"
static struct rb_root ima_iint_tree = RB_ROOT;
static DEFINE_SPINLOCK(ima_iint_lock);
static struct kmem_cache *iint_cache __read_mostly;
int iint_initialized = 0;
/*
* __ima_iint_find - return the iint associated with an inode
*/
static struct ima_iint_cache *__ima_iint_find(struct inode *inode)
{
struct ima_iint_cache *iint;
struct rb_node *n = ima_iint_tree.rb_node;
assert_spin_locked(&ima_iint_lock);
while (n) {
iint = rb_entry(n, struct ima_iint_cache, rb_node);
if (inode < iint->inode)
n = n->rb_left;
else if (inode > iint->inode)
n = n->rb_right;
else
break;
}
if (!n)
return NULL;
return iint;
}
/*
* ima_iint_find - return the iint associated with an inode
*/
struct ima_iint_cache *ima_iint_find(struct inode *inode)
{
struct ima_iint_cache *iint;
if (!IS_IMA(inode))
return NULL;
spin_lock(&ima_iint_lock);
iint = __ima_iint_find(inode);
spin_unlock(&ima_iint_lock);
return iint;
}
static void iint_free(struct ima_iint_cache *iint)
{
iint->version = 0;
iint->flags = 0UL;
kmem_cache_free(iint_cache, iint);
}
/**
* ima_inode_alloc - allocate an iint associated with an inode
* @inode: pointer to the inode
*/
int ima_inode_alloc(struct inode *inode)
{
struct rb_node **p;
struct rb_node *new_node, *parent = NULL;
struct ima_iint_cache *new_iint, *test_iint;
int rc;
new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
if (!new_iint)
return -ENOMEM;
new_iint->inode = inode;
new_node = &new_iint->rb_node;
mutex_lock(&inode->i_mutex); /* i_flags */
spin_lock(&ima_iint_lock);
p = &ima_iint_tree.rb_node;
while (*p) {
parent = *p;
test_iint = rb_entry(parent, struct ima_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;
}
inode->i_flags |= S_IMA;
rb_link_node(new_node, parent, p);
rb_insert_color(new_node, &ima_iint_tree);
spin_unlock(&ima_iint_lock);
mutex_unlock(&inode->i_mutex); /* i_flags */
return 0;
out_err:
spin_unlock(&ima_iint_lock);
mutex_unlock(&inode->i_mutex); /* i_flags */
iint_free(new_iint);
return rc;
}
/**
* ima_inode_free - called on security_inode_free
* @inode: pointer to the inode
*
* Free the integrity information(iint) associated with an inode.
*/
void ima_inode_free(struct inode *inode)
{
struct ima_iint_cache *iint;
if (!IS_IMA(inode))
return;
spin_lock(&ima_iint_lock);
iint = __ima_iint_find(inode);
rb_erase(&iint->rb_node, &ima_iint_tree);
spin_unlock(&ima_iint_lock);
iint_free(iint);
}
static void init_once(void *foo)
{
struct ima_iint_cache *iint = foo;
memset(iint, 0, sizeof *iint);
iint->version = 0;
iint->flags = 0UL;
mutex_init(&iint->mutex);
}
static int __init ima_iintcache_init(void)
{
iint_cache =
kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
SLAB_PANIC, init_once);
iint_initialized = 1;
return 0;
}
security_initcall(ima_iintcache_init);

View File

@ -82,7 +82,7 @@ out:
"open_writers");
}
static void ima_check_last_writer(struct ima_iint_cache *iint,
static void ima_check_last_writer(struct integrity_iint_cache *iint,
struct inode *inode,
struct file *file)
{
@ -105,12 +105,12 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
void ima_file_free(struct file *file)
{
struct inode *inode = file->f_dentry->d_inode;
struct ima_iint_cache *iint;
struct integrity_iint_cache *iint;
if (!iint_initialized || !S_ISREG(inode->i_mode))
return;
iint = ima_iint_find(inode);
iint = integrity_iint_find(inode);
if (!iint)
return;
@ -121,7 +121,7 @@ static int process_measurement(struct file *file, const unsigned char *filename,
int mask, int function)
{
struct inode *inode = file->f_dentry->d_inode;
struct ima_iint_cache *iint;
struct integrity_iint_cache *iint;
int rc = 0;
if (!ima_initialized || !S_ISREG(inode->i_mode))
@ -131,9 +131,9 @@ static int process_measurement(struct file *file, const unsigned char *filename,
if (rc != 0)
return rc;
retry:
iint = ima_iint_find(inode);
iint = integrity_iint_find(inode);
if (!iint) {
rc = ima_inode_alloc(inode);
rc = integrity_inode_alloc(inode);
if (!rc || rc == -EEXIST)
goto retry;
return rc;

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2009-2010 IBM Corporation
*
* Authors:
* 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/types.h>
#include <linux/integrity.h>
#include <crypto/sha.h>
/* iint cache flags */
#define IMA_MEASURED 0x01
enum evm_ima_xattr_type {
IMA_XATTR_DIGEST = 0x01,
EVM_XATTR_HMAC,
EVM_IMA_XATTR_DIGSIG,
};
struct evm_ima_xattr_data {
u8 type;
u8 digest[SHA1_DIGEST_SIZE];
} __attribute__((packed));
/* integrity data associated with an inode */
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 char flags;
u8 digest[SHA1_DIGEST_SIZE];
struct mutex mutex; /* protects: version, flags, digest */
enum integrity_status evm_status;
};
/* rbtree tree calls to lookup, insert, delete
* integrity data associated with an inode.
*/
struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
struct integrity_iint_cache *integrity_iint_find(struct inode *inode);

View File

@ -16,7 +16,11 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/security.h>
#include <linux/integrity.h>
#include <linux/ima.h>
#include <linux/evm.h>
#define MAX_LSM_EVM_XATTR 2
/* Boot-time LSM user choice */
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
@ -334,20 +338,57 @@ int security_inode_alloc(struct inode *inode)
void security_inode_free(struct inode *inode)
{
ima_inode_free(inode);
integrity_inode_free(inode);
security_ops->inode_free_security(inode);
}
int security_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, char **name,
void **value, size_t *len)
const struct qstr *qstr,
const initxattrs initxattrs, void *fs_data)
{
struct xattr new_xattrs[MAX_LSM_EVM_XATTR + 1];
struct xattr *lsm_xattr, *evm_xattr, *xattr;
int ret;
if (unlikely(IS_PRIVATE(inode)))
return -EOPNOTSUPP;
memset(new_xattrs, 0, sizeof new_xattrs);
if (!initxattrs)
return security_ops->inode_init_security(inode, dir, qstr,
NULL, NULL, NULL);
lsm_xattr = new_xattrs;
ret = security_ops->inode_init_security(inode, dir, qstr,
&lsm_xattr->name,
&lsm_xattr->value,
&lsm_xattr->value_len);
if (ret)
goto out;
evm_xattr = lsm_xattr + 1;
ret = evm_inode_init_security(inode, lsm_xattr, evm_xattr);
if (ret)
goto out;
ret = initxattrs(inode, new_xattrs, fs_data);
out:
for (xattr = new_xattrs; xattr->name != NULL; xattr++) {
kfree(xattr->name);
kfree(xattr->value);
}
return (ret == -EOPNOTSUPP) ? 0 : ret;
}
EXPORT_SYMBOL(security_inode_init_security);
int security_old_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, char **name,
void **value, size_t *len)
{
if (unlikely(IS_PRIVATE(inode)))
return -EOPNOTSUPP;
return security_ops->inode_init_security(inode, dir, qstr, name, value,
len);
}
EXPORT_SYMBOL(security_inode_init_security);
EXPORT_SYMBOL(security_old_inode_init_security);
#ifdef CONFIG_SECURITY_PATH
int security_path_mknod(struct path *dir, struct dentry *dentry, int mode,
@ -523,9 +564,14 @@ int security_inode_permission(struct inode *inode, int mask)
int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
{
int ret;
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
return security_ops->inode_setattr(dentry, attr);
ret = security_ops->inode_setattr(dentry, attr);
if (ret)
return ret;
return evm_inode_setattr(dentry, attr);
}
EXPORT_SYMBOL_GPL(security_inode_setattr);
@ -539,9 +585,14 @@ int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
int security_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
int ret;
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
return security_ops->inode_setxattr(dentry, name, value, size, flags);
ret = security_ops->inode_setxattr(dentry, name, value, size, flags);
if (ret)
return ret;
return evm_inode_setxattr(dentry, name, value, size);
}
void security_inode_post_setxattr(struct dentry *dentry, const char *name,
@ -550,6 +601,7 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return;
security_ops->inode_post_setxattr(dentry, name, value, size, flags);
evm_inode_post_setxattr(dentry, name, value, size);
}
int security_inode_getxattr(struct dentry *dentry, const char *name)
@ -568,9 +620,14 @@ int security_inode_listxattr(struct dentry *dentry)
int security_inode_removexattr(struct dentry *dentry, const char *name)
{
int ret;
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
return security_ops->inode_removexattr(dentry, name);
ret = security_ops->inode_removexattr(dentry, name);
if (ret)
return ret;
return evm_inode_removexattr(dentry, name);
}
int security_inode_need_killpriv(struct dentry *dentry)