Commit 5a2f3a02 authored by James Morris's avatar James Morris

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: default avatarJames Morris <jmorris@namei.org>
parents 1d568ab0 817b54aa
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.)
...@@ -48,6 +48,7 @@ parameter is applicable: ...@@ -48,6 +48,7 @@ parameter is applicable:
EDD BIOS Enhanced Disk Drive Services (EDD) is enabled EDD BIOS Enhanced Disk Drive Services (EDD) is enabled
EFI EFI Partitioning (GPT) is enabled EFI EFI Partitioning (GPT) is enabled
EIDE EIDE/ATAPI support is enabled. EIDE EIDE/ATAPI support is enabled.
EVM Extended Verification Module
FB The frame buffer device is enabled. FB The frame buffer device is enabled.
GCOV GCOV profiling is enabled. GCOV GCOV profiling is enabled.
HW Appropriate hardware is enabled. HW Appropriate hardware is enabled.
...@@ -758,6 +759,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ...@@ -758,6 +759,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
This option is obsoleted by the "netdev=" option, which This option is obsoleted by the "netdev=" option, which
has equivalent usage. See its documentation for details. has equivalent usage. See its documentation for details.
evm= [EVM]
Format: { "fix" }
Permit 'security.evm' to be updated regardless of
current integrity status.
failslab= failslab=
fail_page_alloc= fail_page_alloc=
fail_make_request=[KNL] fail_make_request=[KNL]
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/fsnotify.h> #include <linux/fsnotify.h>
#include <linux/fcntl.h> #include <linux/fcntl.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/evm.h>
/** /**
* inode_change_ok - check if attribute changes to an inode are allowed * 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) ...@@ -237,8 +238,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
else else
error = simple_setattr(dentry, attr); error = simple_setattr(dentry, attr);
if (!error) if (!error) {
fsnotify_change(dentry, ia_valid); fsnotify_change(dentry, ia_valid);
evm_inode_post_setattr(dentry, ia_valid);
}
return error; return error;
} }
......
...@@ -374,36 +374,36 @@ int btrfs_removexattr(struct dentry *dentry, const char *name) ...@@ -374,36 +374,36 @@ int btrfs_removexattr(struct dentry *dentry, const char *name)
XATTR_REPLACE); XATTR_REPLACE);
} }
int btrfs_xattr_security_init(struct btrfs_trans_handle *trans, int btrfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
struct inode *inode, struct inode *dir, void *fs_info)
const struct qstr *qstr)
{ {
int err; const struct xattr *xattr;
size_t len; struct btrfs_trans_handle *trans = fs_info;
void *value;
char *suffix;
char *name; char *name;
int err = 0;
err = security_inode_init_security(inode, dir, qstr, &suffix, &value, for (xattr = xattr_array; xattr->name != NULL; xattr++) {
&len); name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
if (err) { strlen(xattr->name) + 1, GFP_NOFS);
if (err == -EOPNOTSUPP)
return 0;
return err;
}
name = kmalloc(XATTR_SECURITY_PREFIX_LEN + strlen(suffix) + 1,
GFP_NOFS);
if (!name) { if (!name) {
err = -ENOMEM; err = -ENOMEM;
} else { break;
}
strcpy(name, XATTR_SECURITY_PREFIX); strcpy(name, XATTR_SECURITY_PREFIX);
strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix); strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
err = __btrfs_setxattr(trans, inode, name, value, len, 0); err = __btrfs_setxattr(trans, inode, name,
xattr->value, xattr->value_len, 0);
kfree(name); kfree(name);
if (err < 0)
break;
} }
kfree(suffix);
kfree(value);
return err; return err;
} }
int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
struct inode *inode, struct inode *dir,
const struct qstr *qstr)
{
return security_inode_init_security(inode, dir, qstr,
&btrfs_initxattrs, trans);
}
...@@ -46,28 +46,30 @@ ext2_xattr_security_set(struct dentry *dentry, const char *name, ...@@ -46,28 +46,30 @@ ext2_xattr_security_set(struct dentry *dentry, const char *name,
value, size, flags); value, size, flags);
} }
int int ext2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
ext2_init_security(struct inode *inode, struct inode *dir, void *fs_info)
const struct qstr *qstr)
{ {
int err; const struct xattr *xattr;
size_t len; int err = 0;
void *value;
char *name;
err = security_inode_init_security(inode, dir, qstr, &name, &value, &len); for (xattr = xattr_array; xattr->name != NULL; xattr++) {
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY, err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,
name, value, len, 0); xattr->name, xattr->value,
kfree(name); xattr->value_len, 0);
kfree(value); if (err < 0)
break;
}
return err; return err;
} }
int
ext2_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr)
{
return security_inode_init_security(inode, dir, qstr,
&ext2_initxattrs, NULL);
}
const struct xattr_handler ext2_xattr_security_handler = { const struct xattr_handler ext2_xattr_security_handler = {
.prefix = XATTR_SECURITY_PREFIX, .prefix = XATTR_SECURITY_PREFIX,
.list = ext2_xattr_security_list, .list = ext2_xattr_security_list,
......
...@@ -48,28 +48,32 @@ ext3_xattr_security_set(struct dentry *dentry, const char *name, ...@@ -48,28 +48,32 @@ ext3_xattr_security_set(struct dentry *dentry, const char *name,
name, value, size, flags); name, value, size, flags);
} }
int int ext3_initxattrs(struct inode *inode, const struct xattr *xattr_array,
ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir, void *fs_info)
const struct qstr *qstr)
{ {
int err; const struct xattr *xattr;
size_t len; handle_t *handle = fs_info;
void *value; int err = 0;
char *name;
err = security_inode_init_security(inode, dir, qstr, &name, &value, &len); for (xattr = xattr_array; xattr->name != NULL; xattr++) {
if (err) { err = ext3_xattr_set_handle(handle, inode,
if (err == -EOPNOTSUPP) EXT3_XATTR_INDEX_SECURITY,
return 0; xattr->name, xattr->value,
return err; xattr->value_len, 0);
if (err < 0)
break;
} }
err = ext3_xattr_set_handle(handle, inode, EXT3_XATTR_INDEX_SECURITY,
name, value, len, 0);
kfree(name);
kfree(value);
return err; return err;
} }
int
ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
const struct qstr *qstr)
{
return security_inode_init_security(inode, dir, qstr,
&ext3_initxattrs, handle);
}
const struct xattr_handler ext3_xattr_security_handler = { const struct xattr_handler ext3_xattr_security_handler = {
.prefix = XATTR_SECURITY_PREFIX, .prefix = XATTR_SECURITY_PREFIX,
.list = ext3_xattr_security_list, .list = ext3_xattr_security_list,
......
...@@ -48,28 +48,32 @@ ext4_xattr_security_set(struct dentry *dentry, const char *name, ...@@ -48,28 +48,32 @@ ext4_xattr_security_set(struct dentry *dentry, const char *name,
name, value, size, flags); name, value, size, flags);
} }
int int ext4_initxattrs(struct inode *inode, const struct xattr *xattr_array,
ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir, void *fs_info)
const struct qstr *qstr)
{ {
int err; const struct xattr *xattr;
size_t len; handle_t *handle = fs_info;
void *value; int err = 0;
char *name;
err = security_inode_init_security(inode, dir, qstr, &name, &value, &len); for (xattr = xattr_array; xattr->name != NULL; xattr++) {
if (err) { err = ext4_xattr_set_handle(handle, inode,
if (err == -EOPNOTSUPP) EXT4_XATTR_INDEX_SECURITY,
return 0; xattr->name, xattr->value,
return err; xattr->value_len, 0);
if (err < 0)
break;
} }
err = ext4_xattr_set_handle(handle, inode, EXT4_XATTR_INDEX_SECURITY,
name, value, len, 0);
kfree(name);
kfree(value);
return err; return err;
} }
int
ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
const struct qstr *qstr)
{
return security_inode_init_security(inode, dir, qstr,
&ext4_initxattrs, handle);
}
const struct xattr_handler ext4_xattr_security_handler = { const struct xattr_handler ext4_xattr_security_handler = {
.prefix = XATTR_SECURITY_PREFIX, .prefix = XATTR_SECURITY_PREFIX,
.list = ext4_xattr_security_list, .list = ext4_xattr_security_list,
......
...@@ -624,31 +624,29 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, ...@@ -624,31 +624,29 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name,
return error; return error;
} }
static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip, int gfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
const struct qstr *qstr) void *fs_info)
{ {
int err; const struct xattr *xattr;
size_t len; int err = 0;
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, for (xattr = xattr_array; xattr->name != NULL; xattr++) {
err = __gfs2_xattr_set(inode, xattr->name, xattr->value,
xattr->value_len, 0,
GFS2_EATYPE_SECURITY); GFS2_EATYPE_SECURITY);
kfree(value); if (err < 0)
kfree(name); break;
}
return err; return err;
} }
static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip,
const struct qstr *qstr)
{
return security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr,
&gfs2_initxattrs, NULL);
}
/** /**
* gfs2_create_inode - Create a new inode * gfs2_create_inode - Create a new inode
* @dir: The parent directory * @dir: The parent directory
......
...@@ -22,26 +22,29 @@ ...@@ -22,26 +22,29 @@
#include <linux/security.h> #include <linux/security.h>
#include "nodelist.h" #include "nodelist.h"
/* ---- Initial Security Label Attachment -------------- */ /* ---- Initial Security Label(s) Attachment callback --- */
int jffs2_init_security(struct inode *inode, struct inode *dir, int jffs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
const struct qstr *qstr) void *fs_info)
{ {
int rc; const struct xattr *xattr;
size_t len; int err = 0;
void *value;
char *name;
rc = security_inode_init_security(inode, dir, qstr, &name, &value, &len); for (xattr = xattr_array; xattr->name != NULL; xattr++) {
if (rc) { err = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY,
if (rc == -EOPNOTSUPP) xattr->name, xattr->value,
return 0; xattr->value_len, 0);
return rc; if (err < 0)
break;
} }
rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0); return err;
}
kfree(name); /* ---- Initial Security Label(s) Attachment ----------- */
kfree(value); int jffs2_init_security(struct inode *inode, struct inode *dir,
return rc; const struct qstr *qstr)
{
return security_inode_init_security(inode, dir, qstr,
&jffs2_initxattrs, NULL);
} }
/* ---- XATTR Handler for "security.*" ----------------- */ /* ---- XATTR Handler for "security.*" ----------------- */
......
...@@ -1089,38 +1089,37 @@ int jfs_removexattr(struct dentry *dentry, const char *name) ...@@ -1089,38 +1089,37 @@ int jfs_removexattr(struct dentry *dentry, const char *name)
} }
#ifdef CONFIG_JFS_SECURITY #ifdef CONFIG_JFS_SECURITY
int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir, int jfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
const struct qstr *qstr) void *fs_info)
{ {
int rc; const struct xattr *xattr;
size_t len; tid_t *tid = fs_info;
void *value;
char *suffix;
char *name; char *name;
int err = 0;
rc = security_inode_init_security(inode, dir, qstr, &suffix, &value, for (xattr = xattr_array; xattr->name != NULL; xattr++) {
&len); name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
if (rc) { strlen(xattr->name) + 1, GFP_NOFS);
if (rc == -EOPNOTSUPP)
return 0;
return rc;
}
name = kmalloc(XATTR_SECURITY_PREFIX_LEN + 1 + strlen(suffix),
GFP_NOFS);
if (!name) { if (!name) {
rc = -ENOMEM; err = -ENOMEM;
goto kmalloc_failed; break;
} }
strcpy(name, XATTR_SECURITY_PREFIX); strcpy(name, XATTR_SECURITY_PREFIX);
strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix); strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
rc = __jfs_setxattr(tid, inode, name, value, len, 0);
err = __jfs_setxattr(*tid, inode, name,
xattr->value, xattr->value_len, 0);
kfree(name); kfree(name);
kmalloc_failed: if (err < 0)
kfree(suffix); break;
kfree(value); }
return err;
}
return rc; int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir,
const struct qstr *qstr)
{
return security_inode_init_security(inode, dir, qstr,
&jfs_initxattrs, &tid);
} }
#endif #endif
...@@ -7185,20 +7185,9 @@ int ocfs2_init_security_and_acl(struct inode *dir, ...@@ -7185,20 +7185,9 @@ int ocfs2_init_security_and_acl(struct inode *dir,
{ {
int ret = 0; int ret = 0;
struct buffer_head *dir_bh = NULL; 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) { 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); mlog_errno(ret);
goto leave; goto leave;
} }
...@@ -7255,6 +7244,22 @@ static int ocfs2_xattr_security_set(struct dentry *dentry, const char *name, ...@@ -7255,6 +7244,22 @@ static int ocfs2_xattr_security_set(struct dentry *dentry, const char *name,
name, value, size, flags); 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, int ocfs2_init_security_get(struct inode *inode,
struct inode *dir, struct inode *dir,
const struct qstr *qstr, const struct qstr *qstr,
...@@ -7263,8 +7268,13 @@ int ocfs2_init_security_get(struct inode *inode, ...@@ -7263,8 +7268,13 @@ int ocfs2_init_security_get(struct inode *inode,
/* check whether ocfs2 support feature xattr */ /* check whether ocfs2 support feature xattr */
if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb))) if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb)))
return -EOPNOTSUPP; return -EOPNOTSUPP;
return security_inode_init_security(inode, dir, qstr, &si->name, if (si)
&si->value, &si->value_len); 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, int ocfs2_init_security_set(handle_t *handle,
......
...@@ -66,7 +66,7 @@ int reiserfs_security_init(struct inode *dir, struct inode *inode, ...@@ -66,7 +66,7 @@ int reiserfs_security_init(struct inode *dir, struct inode *inode,
if (IS_PRIVATE(dir)) if (IS_PRIVATE(dir))
return 0; return 0;
error = security_inode_init_security(inode, dir, qstr, &sec->name, error = security_old_inode_init_security(inode, dir, qstr, &sec->name,
&sec->value, &sec->length); &sec->value, &sec->length);
if (error) { if (error) {
if (error == -EOPNOTSUPP) if (error == -EOPNOTSUPP)
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/evm.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/fsnotify.h> #include <linux/fsnotify.h>
...@@ -166,6 +167,64 @@ xattr_getsecurity(struct inode *inode, const char *name, void *value, ...@@ -166,6 +167,64 @@ xattr_getsecurity(struct inode *inode, const char *name, void *value,
} }
EXPORT_SYMBOL_GPL(xattr_getsecurity); 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 ssize_t
vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) 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) ...@@ -243,8 +302,10 @@ vfs_removexattr(struct dentry *dentry, const char *name)
error = inode->i_op->removexattr(dentry, name); error = inode->i_op->removexattr(dentry, name);
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
if (!error) if (!error) {
fsnotify_xattr(dentry); fsnotify_xattr(dentry);
evm_inode_post_removexattr(dentry, name);
}
return error; return error;
} }
EXPORT_SYMBOL_GPL(vfs_removexattr); EXPORT_SYMBOL_GPL(vfs_removexattr);
......
...@@ -94,37 +94,38 @@ xfs_mark_inode_dirty( ...@@ -94,37 +94,38 @@ xfs_mark_inode_dirty(
mark_inode_dirty(inode); 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 * 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 * 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 * 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). * inode, of course, such that log replay can't cause these to be lost).
*/ */
STATIC int STATIC int
xfs_init_security( xfs_init_security(
struct inode *inode, struct inode *inode,
struct inode *dir, struct inode *dir,
const struct qstr *qstr) const struct qstr *qstr)
{ {
struct xfs_inode *ip = XFS_I(inode); return security_inode_init_security(inode, dir, qstr,
size_t length; &xfs_initxattrs, NULL);
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;
} }
static void static void
......
/*
* 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 */
...@@ -15,8 +15,6 @@ struct linux_binprm; ...@@ -15,8 +15,6 @@ struct linux_binprm;
#ifdef CONFIG_IMA #ifdef CONFIG_IMA
extern int ima_bprm_check(struct linux_binprm *bprm); 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 int ima_file_check(struct file *file, int mask);
extern void ima_file_free(struct file *file); extern void ima_file_free(struct file *file);
extern int ima_file_mmap(struct file *file, unsigned long prot); 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) ...@@ -27,16 +25,6 @@ static inline int ima_bprm_check(struct linux_binprm *bprm)
return 0; 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) static inline int ima_file_check(struct file *file, int mask)
{ {
return 0; return 0;
...@@ -51,6 +39,5 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot) ...@@ -51,6 +39,5 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
{ {
return 0; return 0;
} }
#endif /* CONFIG_IMA_H */ #endif /* CONFIG_IMA_H */
#endif /* _LINUX_IMA_H */ #endif /* _LINUX_IMA_H */
/*
* 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 */
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/key.h> #include <linux/key.h>
#include <linux/xfrm.h> #include <linux/xfrm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/xattr.h>
#include <net/flow.h> #include <net/flow.h>
/* Maximum number of letters for an LSM name string */ /* 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, ...@@ -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); void __user *buffer, size_t *lenp, loff_t *ppos);
#endif #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 #ifdef CONFIG_SECURITY
struct security_mnt_opts { struct security_mnt_opts {
...@@ -1704,6 +1709,9 @@ int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts); ...@@ -1704,6 +1709,9 @@ int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
int security_inode_alloc(struct inode *inode); int security_inode_alloc(struct inode *inode);
void security_inode_free(struct inode *inode); void security_inode_free(struct inode *inode);
int security_inode_init_security(struct inode *inode, struct inode *dir, int security_inode_init_security(struct inode *inode, struct inode *dir,
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, const struct qstr *qstr, char **name,
void **value, size_t *len); void **value, size_t *len);
int security_inode_create(struct inode *dir, struct dentry *dentry, int mode); int security_inode_create(struct inode *dir, struct dentry *dentry, int mode);
...@@ -2034,9 +2042,8 @@ static inline void security_inode_free(struct inode *inode) ...@@ -2034,9 +2042,8 @@ static inline void security_inode_free(struct inode *inode)
static inline int security_inode_init_security(struct inode *inode, static inline int security_inode_init_security(struct inode *inode,
struct inode *dir, struct inode *dir,
const struct qstr *qstr, const struct qstr *qstr,
char **name, initxattrs initxattrs,
void **value, void *fs_data)
size_t *len)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -30,6 +30,9 @@ ...@@ -30,6 +30,9 @@
#define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1) #define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1)
/* Security namespace */ /* Security namespace */
#define XATTR_EVM_SUFFIX "evm"
#define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX
#define XATTR_SELINUX_SUFFIX "selinux" #define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
...@@ -67,6 +70,12 @@ struct xattr_handler { ...@@ -67,6 +70,12 @@ struct xattr_handler {
size_t size, int flags, int handler_flags); 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 xattr_getsecurity(struct inode *, const char *, void *, size_t);
ssize_t vfs_getxattr(struct dentry *, 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); 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, ...@@ -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); 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_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags);
int generic_removexattr(struct dentry *dentry, const char *name); 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 /* __KERNEL__ */
#endif /* _LINUX_XATTR_H */ #endif /* _LINUX_XATTR_H */
...@@ -1458,7 +1458,7 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) ...@@ -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); inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);
if (inode) { if (inode) {
error = security_inode_init_security(inode, dir, error = security_inode_init_security(inode, dir,
&dentry->d_name, NULL, &dentry->d_name,
NULL, NULL); NULL, NULL);
if (error) { if (error) {
if (error != -EOPNOTSUPP) { if (error != -EOPNOTSUPP) {
...@@ -1598,7 +1598,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s ...@@ -1598,7 +1598,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
if (!inode) if (!inode)
return -ENOSPC; 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); NULL, NULL);
if (error) { if (error) {
if (error != -EOPNOTSUPP) { if (error != -EOPNOTSUPP) {
......
...@@ -186,7 +186,7 @@ source security/smack/Kconfig ...@@ -186,7 +186,7 @@ source security/smack/Kconfig
source security/tomoyo/Kconfig source security/tomoyo/Kconfig
source security/apparmor/Kconfig source security/apparmor/Kconfig
source security/integrity/ima/Kconfig source security/integrity/Kconfig
choice choice
prompt "Default security module" prompt "Default security module"
......
...@@ -24,5 +24,5 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o ...@@ -24,5 +24,5 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
# Object integrity file lists # Object integrity file lists
subdir-$(CONFIG_IMA) += integrity/ima subdir-$(CONFIG_INTEGRITY) += integrity
obj-$(CONFIG_IMA) += integrity/ima/built-in.o obj-$(CONFIG_INTEGRITY) += integrity/built-in.o
#
config INTEGRITY
def_bool y
depends on IMA || EVM
source security/integrity/ima/Kconfig
source security/integrity/evm/Kconfig
#
# 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
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.
#
# Makefile for building the Extended Verification Module(EVM)
#
obj-$(CONFIG_EVM) += evm.o
evm-y := evm_main.o evm_crypto.o evm_secfs.o
/*
* 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);
/*
* 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;
}
This diff is collapsed.
/*
* 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);
}
...@@ -9,8 +9,9 @@ ...@@ -9,8 +9,9 @@
* published by the Free Software Foundation, version 2 of the * published by the Free Software Foundation, version 2 of the
* License. * License.
* *
* File: ima_iint.c * File: integrity_iint.c
* - implements the IMA hooks: ima_inode_alloc, ima_inode_free * - implements the integrity hooks: integrity_inode_alloc,
* integrity_inode_free
* - cache integrity information associated with an inode * - cache integrity information associated with an inode
* using a rbtree tree. * using a rbtree tree.
*/ */
...@@ -18,26 +19,26 @@ ...@@ -18,26 +19,26 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include "ima.h" #include "integrity.h"
static struct rb_root ima_iint_tree = RB_ROOT; static struct rb_root integrity_iint_tree = RB_ROOT;
static DEFINE_SPINLOCK(ima_iint_lock); static DEFINE_SPINLOCK(integrity_iint_lock);
static struct kmem_cache *iint_cache __read_mostly; static struct kmem_cache *iint_cache __read_mostly;
int iint_initialized = 0; int iint_initialized;
/* /*
* __ima_iint_find - return the iint associated with an inode * __integrity_iint_find - return the iint associated with an inode
*/ */
static struct ima_iint_cache *__ima_iint_find(struct inode *inode) static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
{ {
struct ima_iint_cache *iint; struct integrity_iint_cache *iint;
struct rb_node *n = ima_iint_tree.rb_node; struct rb_node *n = integrity_iint_tree.rb_node;
assert_spin_locked(&ima_iint_lock); assert_spin_locked(&integrity_iint_lock);
while (n) { while (n) {
iint = rb_entry(n, struct ima_iint_cache, rb_node); iint = rb_entry(n, struct integrity_iint_cache, rb_node);
if (inode < iint->inode) if (inode < iint->inode)
n = n->rb_left; n = n->rb_left;
...@@ -53,23 +54,23 @@ static struct ima_iint_cache *__ima_iint_find(struct inode *inode) ...@@ -53,23 +54,23 @@ static struct ima_iint_cache *__ima_iint_find(struct inode *inode)
} }
/* /*
* ima_iint_find - return the iint associated with an inode * integrity_iint_find - return the iint associated with an inode
*/ */
struct ima_iint_cache *ima_iint_find(struct inode *inode) struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
{ {
struct ima_iint_cache *iint; struct integrity_iint_cache *iint;
if (!IS_IMA(inode)) if (!IS_IMA(inode))
return NULL; return NULL;
spin_lock(&ima_iint_lock); spin_lock(&integrity_iint_lock);
iint = __ima_iint_find(inode); iint = __integrity_iint_find(inode);
spin_unlock(&ima_iint_lock); spin_unlock(&integrity_iint_lock);
return iint; return iint;
} }
static void iint_free(struct ima_iint_cache *iint) static void iint_free(struct integrity_iint_cache *iint)
{ {
iint->version = 0; iint->version = 0;
iint->flags = 0UL; iint->flags = 0UL;
...@@ -77,14 +78,14 @@ static void iint_free(struct ima_iint_cache *iint) ...@@ -77,14 +78,14 @@ static void iint_free(struct ima_iint_cache *iint)
} }
/** /**
* ima_inode_alloc - allocate an iint associated with an inode * integrity_inode_alloc - allocate an iint associated with an inode
* @inode: pointer to the inode * @inode: pointer to the inode
*/ */
int ima_inode_alloc(struct inode *inode) int integrity_inode_alloc(struct inode *inode)
{ {
struct rb_node **p; struct rb_node **p;
struct rb_node *new_node, *parent = NULL; struct rb_node *new_node, *parent = NULL;
struct ima_iint_cache *new_iint, *test_iint; struct integrity_iint_cache *new_iint, *test_iint;
int rc; int rc;
new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS); new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
...@@ -95,13 +96,13 @@ int ima_inode_alloc(struct inode *inode) ...@@ -95,13 +96,13 @@ int ima_inode_alloc(struct inode *inode)
new_node = &new_iint->rb_node; new_node = &new_iint->rb_node;
mutex_lock(&inode->i_mutex); /* i_flags */ mutex_lock(&inode->i_mutex); /* i_flags */
spin_lock(&ima_iint_lock); spin_lock(&integrity_iint_lock);
p = &ima_iint_tree.rb_node; p = &integrity_iint_tree.rb_node;
while (*p) { while (*p) {
parent = *p; parent = *p;
test_iint = rb_entry(parent, struct ima_iint_cache, rb_node); test_iint = rb_entry(parent, struct integrity_iint_cache,
rb_node);
rc = -EEXIST; rc = -EEXIST;
if (inode < test_iint->inode) if (inode < test_iint->inode)
p = &(*p)->rb_left; p = &(*p)->rb_left;
...@@ -113,14 +114,14 @@ int ima_inode_alloc(struct inode *inode) ...@@ -113,14 +114,14 @@ int ima_inode_alloc(struct inode *inode)
inode->i_flags |= S_IMA; inode->i_flags |= S_IMA;
rb_link_node(new_node, parent, p); rb_link_node(new_node, parent, p);
rb_insert_color(new_node, &ima_iint_tree); rb_insert_color(new_node, &integrity_iint_tree);
spin_unlock(&ima_iint_lock); spin_unlock(&integrity_iint_lock);
mutex_unlock(&inode->i_mutex); /* i_flags */ mutex_unlock(&inode->i_mutex); /* i_flags */
return 0; return 0;
out_err: out_err:
spin_unlock(&ima_iint_lock); spin_unlock(&integrity_iint_lock);
mutex_unlock(&inode->i_mutex); /* i_flags */ mutex_unlock(&inode->i_mutex); /* i_flags */
iint_free(new_iint); iint_free(new_iint);
...@@ -128,42 +129,43 @@ int ima_inode_alloc(struct inode *inode) ...@@ -128,42 +129,43 @@ int ima_inode_alloc(struct inode *inode)
} }
/** /**
* ima_inode_free - called on security_inode_free * integrity_inode_free - called on security_inode_free
* @inode: pointer to the inode * @inode: pointer to the inode
* *
* Free the integrity information(iint) associated with an inode. * Free the integrity information(iint) associated with an inode.
*/ */
void ima_inode_free(struct inode *inode) void integrity_inode_free(struct inode *inode)
{ {
struct ima_iint_cache *iint; struct integrity_iint_cache *iint;
if (!IS_IMA(inode)) if (!IS_IMA(inode))
return; return;
spin_lock(&ima_iint_lock); spin_lock(&integrity_iint_lock);
iint = __ima_iint_find(inode); iint = __integrity_iint_find(inode);
rb_erase(&iint->rb_node, &ima_iint_tree); rb_erase(&iint->rb_node, &integrity_iint_tree);
spin_unlock(&ima_iint_lock); spin_unlock(&integrity_iint_lock);
iint_free(iint); iint_free(iint);
} }
static void init_once(void *foo) static void init_once(void *foo)
{ {
struct ima_iint_cache *iint = foo; struct integrity_iint_cache *iint = foo;
memset(iint, 0, sizeof *iint); memset(iint, 0, sizeof *iint);
iint->version = 0; iint->version = 0;
iint->flags = 0UL; iint->flags = 0UL;
mutex_init(&iint->mutex); mutex_init(&iint->mutex);
iint->evm_status = INTEGRITY_UNKNOWN;
} }
static int __init ima_iintcache_init(void) static int __init integrity_iintcache_init(void)
{ {
iint_cache = iint_cache =
kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0, kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
SLAB_PANIC, init_once); 0, SLAB_PANIC, init_once);
iint_initialized = 1; iint_initialized = 1;
return 0; return 0;
} }
security_initcall(ima_iintcache_init); security_initcall(integrity_iintcache_init);
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
config IMA config IMA
bool "Integrity Measurement Architecture(IMA)" bool "Integrity Measurement Architecture(IMA)"
depends on SECURITY depends on SECURITY
select INTEGRITY
select SECURITYFS select SECURITYFS
select CRYPTO select CRYPTO
select CRYPTO_HMAC select CRYPTO_HMAC
......
...@@ -6,4 +6,4 @@ ...@@ -6,4 +6,4 @@
obj-$(CONFIG_IMA) += ima.o 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-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
...@@ -24,11 +24,13 @@ ...@@ -24,11 +24,13 @@
#include <linux/tpm.h> #include <linux/tpm.h>
#include <linux/audit.h> #include <linux/audit.h>
#include "../integrity.h"
enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII }; enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
/* digest size for IMA, fits SHA1 or MD5 */ /* 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_EVENT_NAME_LEN_MAX 255
#define IMA_HASH_BITS 9 #define IMA_HASH_BITS 9
...@@ -96,34 +98,21 @@ static inline unsigned long ima_hash_key(u8 *digest) ...@@ -96,34 +98,21 @@ static inline unsigned long ima_hash_key(u8 *digest)
return hash_long(*digest, IMA_HASH_BITS); 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 */ /* LIM API function definitions */
int ima_must_measure(struct inode *inode, int mask, int function); int ima_must_measure(struct inode *inode, int mask, int function);
int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file); int ima_collect_measurement(struct integrity_iint_cache *iint,
void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, struct file *file);
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
const unsigned char *filename); const unsigned char *filename);
int ima_store_template(struct ima_template_entry *entry, int violation, int ima_store_template(struct ima_template_entry *entry, int violation,
struct inode *inode); struct inode *inode);
void ima_template_show(struct seq_file *m, void *e, void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
enum ima_show_type show);
/* rbtree tree calls to lookup, insert, delete /* rbtree tree calls to lookup, insert, delete
* integrity data associated with an inode. * integrity data associated with an inode.
*/ */
struct ima_iint_cache *ima_iint_insert(struct inode *inode); struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
struct ima_iint_cache *ima_iint_find(struct inode *inode); struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
/* IMA policy related functions */ /* IMA policy related functions */
enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK }; enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };
......
...@@ -126,7 +126,8 @@ int ima_must_measure(struct inode *inode, int mask, int function) ...@@ -126,7 +126,8 @@ int ima_must_measure(struct inode *inode, int mask, int function)
* *
* Return 0 on success, error code otherwise * 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; int result = -EEXIST;
...@@ -156,8 +157,8 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file) ...@@ -156,8 +157,8 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
* *
* Must be called with iint->mutex held. * Must be called with iint->mutex held.
*/ */
void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, void ima_store_measurement(struct integrity_iint_cache *iint,
const unsigned char *filename) struct file *file, const unsigned char *filename)
{ {
const char *op = "add_template_measure"; const char *op = "add_template_measure";
const char *audit_cause = "ENOMEM"; const char *audit_cause = "ENOMEM";
......
...@@ -82,7 +82,7 @@ static void ima_rdwr_violation_check(struct file *file) ...@@ -82,7 +82,7 @@ static void ima_rdwr_violation_check(struct file *file)
"open_writers"); "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 inode *inode,
struct file *file) struct file *file)
{ {
...@@ -105,12 +105,12 @@ static void ima_check_last_writer(struct ima_iint_cache *iint, ...@@ -105,12 +105,12 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
void ima_file_free(struct file *file) void ima_file_free(struct file *file)
{ {
struct inode *inode = file->f_dentry->d_inode; 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)) if (!iint_initialized || !S_ISREG(inode->i_mode))
return; return;
iint = ima_iint_find(inode); iint = integrity_iint_find(inode);
if (!iint) if (!iint)
return; return;
...@@ -121,7 +121,7 @@ static int process_measurement(struct file *file, const unsigned char *filename, ...@@ -121,7 +121,7 @@ static int process_measurement(struct file *file, const unsigned char *filename,
int mask, int function) int mask, int function)
{ {
struct inode *inode = file->f_dentry->d_inode; struct inode *inode = file->f_dentry->d_inode;
struct ima_iint_cache *iint; struct integrity_iint_cache *iint;
int rc = 0; int rc = 0;
if (!ima_initialized || !S_ISREG(inode->i_mode)) if (!ima_initialized || !S_ISREG(inode->i_mode))
...@@ -131,9 +131,9 @@ static int process_measurement(struct file *file, const unsigned char *filename, ...@@ -131,9 +131,9 @@ static int process_measurement(struct file *file, const unsigned char *filename,
if (rc != 0) if (rc != 0)
return rc; return rc;
retry: retry:
iint = ima_iint_find(inode); iint = integrity_iint_find(inode);
if (!iint) { if (!iint) {
rc = ima_inode_alloc(inode); rc = integrity_inode_alloc(inode);
if (!rc || rc == -EEXIST) if (!rc || rc == -EEXIST)
goto retry; goto retry;
return rc; return rc;
......
/*
* 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);
...@@ -16,7 +16,11 @@ ...@@ -16,7 +16,11 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/integrity.h>
#include <linux/ima.h> #include <linux/ima.h>
#include <linux/evm.h>
#define MAX_LSM_EVM_XATTR 2
/* Boot-time LSM user choice */ /* Boot-time LSM user choice */
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
...@@ -334,11 +338,48 @@ int security_inode_alloc(struct inode *inode) ...@@ -334,11 +338,48 @@ int security_inode_alloc(struct inode *inode)
void security_inode_free(struct inode *inode) void security_inode_free(struct inode *inode)
{ {
ima_inode_free(inode); integrity_inode_free(inode);
security_ops->inode_free_security(inode); security_ops->inode_free_security(inode);
} }
int security_inode_init_security(struct inode *inode, struct inode *dir, int security_inode_init_security(struct inode *inode, struct inode *dir,
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, const struct qstr *qstr, char **name,
void **value, size_t *len) void **value, size_t *len)
{ {
...@@ -347,7 +388,7 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, ...@@ -347,7 +388,7 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
return security_ops->inode_init_security(inode, dir, qstr, name, value, return security_ops->inode_init_security(inode, dir, qstr, name, value,
len); len);
} }
EXPORT_SYMBOL(security_inode_init_security); EXPORT_SYMBOL(security_old_inode_init_security);
#ifdef CONFIG_SECURITY_PATH #ifdef CONFIG_SECURITY_PATH
int security_path_mknod(struct path *dir, struct dentry *dentry, int mode, 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) ...@@ -523,9 +564,14 @@ int security_inode_permission(struct inode *inode, int mask)
int security_inode_setattr(struct dentry *dentry, struct iattr *attr) int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
{ {
int ret;
if (unlikely(IS_PRIVATE(dentry->d_inode))) if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0; 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); EXPORT_SYMBOL_GPL(security_inode_setattr);
...@@ -539,9 +585,14 @@ int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) ...@@ -539,9 +585,14 @@ int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
int security_inode_setxattr(struct dentry *dentry, const char *name, int security_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags) const void *value, size_t size, int flags)
{ {
int ret;
if (unlikely(IS_PRIVATE(dentry->d_inode))) if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0; 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, 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, ...@@ -550,6 +601,7 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
if (unlikely(IS_PRIVATE(dentry->d_inode))) if (unlikely(IS_PRIVATE(dentry->d_inode)))
return; return;
security_ops->inode_post_setxattr(dentry, name, value, size, flags); 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) int security_inode_getxattr(struct dentry *dentry, const char *name)
...@@ -568,9 +620,14 @@ int security_inode_listxattr(struct dentry *dentry) ...@@ -568,9 +620,14 @@ int security_inode_listxattr(struct dentry *dentry)
int security_inode_removexattr(struct dentry *dentry, const char *name) int security_inode_removexattr(struct dentry *dentry, const char *name)
{ {
int ret;
if (unlikely(IS_PRIVATE(dentry->d_inode))) if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0; 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) int security_inode_need_killpriv(struct dentry *dentry)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment