mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 04:31:50 +00:00
Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull integrity updates from James Morris: "From Mimi: - add run time support for specifying additional security xattrs included in the security.evm HMAC/signature - some code clean up and bug fixes" * 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: EVM: unlock on error path in evm_read_xattrs() EVM: prevent array underflow in evm_write_xattrs() EVM: Fix null dereference on xattr when xattr fails to allocate EVM: fix memory leak of temporary buffer 'temp' IMA: use list_splice_tail_init_rcu() instead of its open coded variant ima: use match_string() helper ima: fix updating the ima_appraise flag ima: based on policy verify firmware signatures (pre-allocated buffer) ima: define a new policy condition based on the filesystem name EVM: Allow runtime modification of the set of verified xattrs EVM: turn evm_config_xattrnames into a list integrity: Add an integrity directory in securityfs ima: Remove unused variable ima_initialized ima: Unify logging ima: Reflect correct permissions for policy
This commit is contained in:
commit
00d535a386
@ -57,3 +57,16 @@ Description:
|
||||
dracut (via 97masterkey and 98integrity) and systemd (via
|
||||
core/ima-setup) have support for loading keys at boot
|
||||
time.
|
||||
|
||||
What: security/integrity/evm/evm_xattrs
|
||||
Date: April 2018
|
||||
Contact: Matthew Garrett <mjg59@google.com>
|
||||
Description:
|
||||
Shows the set of extended attributes used to calculate or
|
||||
validate the EVM signature, and allows additional attributes
|
||||
to be added at runtime. Any signatures generated after
|
||||
additional attributes are added (and on files posessing those
|
||||
additional attributes) will only be valid if the same
|
||||
additional attributes are configured on system boot. Writing
|
||||
a single period (.) will lock the xattr list from any further
|
||||
modification.
|
||||
|
@ -21,7 +21,7 @@ Description:
|
||||
audit | hash | dont_hash
|
||||
condition:= base | lsm [option]
|
||||
base: [[func=] [mask=] [fsmagic=] [fsuuid=] [uid=]
|
||||
[euid=] [fowner=]]
|
||||
[euid=] [fowner=] [fsname=]]
|
||||
lsm: [[subj_user=] [subj_role=] [subj_type=]
|
||||
[obj_user=] [obj_role=] [obj_type=]]
|
||||
option: [[appraise_type=]] [permit_directio]
|
||||
|
@ -147,6 +147,7 @@
|
||||
#define AUDIT_INTEGRITY_HASH 1803 /* Integrity HASH type */
|
||||
#define AUDIT_INTEGRITY_PCR 1804 /* PCR invalidation msgs */
|
||||
#define AUDIT_INTEGRITY_RULE 1805 /* policy rule */
|
||||
#define AUDIT_INTEGRITY_EVM_XATTR 1806 /* New EVM-covered xattr */
|
||||
|
||||
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
|
||||
|
||||
|
@ -42,6 +42,17 @@ config EVM_EXTRA_SMACK_XATTRS
|
||||
additional info to the calculation, requires existing EVM
|
||||
labeled file systems to be relabeled.
|
||||
|
||||
config EVM_ADD_XATTRS
|
||||
bool "Add additional EVM extended attributes at runtime"
|
||||
depends on EVM
|
||||
default n
|
||||
help
|
||||
Allow userland to provide additional xattrs for HMAC calculation.
|
||||
|
||||
When this option is enabled, root can add additional xattrs to the
|
||||
list used by EVM by writing them into
|
||||
/sys/kernel/security/integrity/evm/evm_xattrs.
|
||||
|
||||
config EVM_LOAD_X509
|
||||
bool "Load an X509 certificate onto the '.evm' trusted keyring"
|
||||
depends on EVM && INTEGRITY_TRUSTED_KEYRING
|
||||
|
@ -30,6 +30,11 @@
|
||||
#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP_COMPLETE | \
|
||||
EVM_ALLOW_METADATA_WRITES)
|
||||
|
||||
struct xattr_list {
|
||||
struct list_head list;
|
||||
char *name;
|
||||
};
|
||||
|
||||
extern int evm_initialized;
|
||||
|
||||
#define EVM_ATTR_FSUUID 0x0001
|
||||
@ -40,7 +45,7 @@ extern struct crypto_shash *hmac_tfm;
|
||||
extern struct crypto_shash *hash_tfm;
|
||||
|
||||
/* List of EVM protected security xattrs */
|
||||
extern char *evm_config_xattrnames[];
|
||||
extern struct list_head evm_config_xattrnames;
|
||||
|
||||
int evm_init_key(void);
|
||||
int evm_update_evmxattr(struct dentry *dentry,
|
||||
|
@ -192,8 +192,8 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
|
||||
char type, char *digest)
|
||||
{
|
||||
struct inode *inode = d_backing_inode(dentry);
|
||||
struct xattr_list *xattr;
|
||||
struct shash_desc *desc;
|
||||
char **xattrname;
|
||||
size_t xattr_size = 0;
|
||||
char *xattr_value = NULL;
|
||||
int error;
|
||||
@ -209,14 +209,14 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
|
||||
return PTR_ERR(desc);
|
||||
|
||||
error = -ENODATA;
|
||||
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
|
||||
list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) {
|
||||
bool is_ima = false;
|
||||
|
||||
if (strcmp(*xattrname, XATTR_NAME_IMA) == 0)
|
||||
if (strcmp(xattr->name, XATTR_NAME_IMA) == 0)
|
||||
is_ima = true;
|
||||
|
||||
if ((req_xattr_name && req_xattr_value)
|
||||
&& !strcmp(*xattrname, req_xattr_name)) {
|
||||
&& !strcmp(xattr->name, req_xattr_name)) {
|
||||
error = 0;
|
||||
crypto_shash_update(desc, (const u8 *)req_xattr_value,
|
||||
req_xattr_value_len);
|
||||
@ -224,7 +224,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
|
||||
ima_present = true;
|
||||
continue;
|
||||
}
|
||||
size = vfs_getxattr_alloc(dentry, *xattrname,
|
||||
size = vfs_getxattr_alloc(dentry, xattr->name,
|
||||
&xattr_value, xattr_size, GFP_NOFS);
|
||||
if (size == -ENOMEM) {
|
||||
error = -ENOMEM;
|
||||
|
@ -35,28 +35,29 @@ static const char * const integrity_status_msg[] = {
|
||||
};
|
||||
int evm_hmac_attrs;
|
||||
|
||||
char *evm_config_xattrnames[] = {
|
||||
static struct xattr_list evm_config_default_xattrnames[] = {
|
||||
#ifdef CONFIG_SECURITY_SELINUX
|
||||
XATTR_NAME_SELINUX,
|
||||
{.name = XATTR_NAME_SELINUX},
|
||||
#endif
|
||||
#ifdef CONFIG_SECURITY_SMACK
|
||||
XATTR_NAME_SMACK,
|
||||
{.name = XATTR_NAME_SMACK},
|
||||
#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
|
||||
XATTR_NAME_SMACKEXEC,
|
||||
XATTR_NAME_SMACKTRANSMUTE,
|
||||
XATTR_NAME_SMACKMMAP,
|
||||
{.name = XATTR_NAME_SMACKEXEC},
|
||||
{.name = XATTR_NAME_SMACKTRANSMUTE},
|
||||
{.name = XATTR_NAME_SMACKMMAP},
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_SECURITY_APPARMOR
|
||||
XATTR_NAME_APPARMOR,
|
||||
{.name = XATTR_NAME_APPARMOR},
|
||||
#endif
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
XATTR_NAME_IMA,
|
||||
{.name = XATTR_NAME_IMA},
|
||||
#endif
|
||||
XATTR_NAME_CAPS,
|
||||
NULL
|
||||
{.name = XATTR_NAME_CAPS},
|
||||
};
|
||||
|
||||
LIST_HEAD(evm_config_xattrnames);
|
||||
|
||||
static int evm_fixmode;
|
||||
static int __init evm_set_fixmode(char *str)
|
||||
{
|
||||
@ -68,6 +69,17 @@ __setup("evm=", evm_set_fixmode);
|
||||
|
||||
static void __init evm_init_config(void)
|
||||
{
|
||||
int i, xattrs;
|
||||
|
||||
xattrs = ARRAY_SIZE(evm_config_default_xattrnames);
|
||||
|
||||
pr_info("Initialising EVM extended attributes:\n");
|
||||
for (i = 0; i < xattrs; i++) {
|
||||
pr_info("%s\n", evm_config_default_xattrnames[i].name);
|
||||
list_add_tail(&evm_config_default_xattrnames[i].list,
|
||||
&evm_config_xattrnames);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EVM_ATTR_FSUUID
|
||||
evm_hmac_attrs |= EVM_ATTR_FSUUID;
|
||||
#endif
|
||||
@ -82,15 +94,15 @@ static bool evm_key_loaded(void)
|
||||
static int evm_find_protected_xattrs(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = d_backing_inode(dentry);
|
||||
char **xattr;
|
||||
struct xattr_list *xattr;
|
||||
int error;
|
||||
int count = 0;
|
||||
|
||||
if (!(inode->i_opflags & IOP_XATTR))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) {
|
||||
error = __vfs_getxattr(dentry, inode, *xattr, NULL, 0);
|
||||
list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) {
|
||||
error = __vfs_getxattr(dentry, inode, xattr->name, NULL, 0);
|
||||
if (error < 0) {
|
||||
if (error == -ENODATA)
|
||||
continue;
|
||||
@ -211,24 +223,25 @@ out:
|
||||
|
||||
static int evm_protected_xattr(const char *req_xattr_name)
|
||||
{
|
||||
char **xattrname;
|
||||
int namelen;
|
||||
int found = 0;
|
||||
struct xattr_list *xattr;
|
||||
|
||||
namelen = strlen(req_xattr_name);
|
||||
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
|
||||
if ((strlen(*xattrname) == namelen)
|
||||
&& (strncmp(req_xattr_name, *xattrname, namelen) == 0)) {
|
||||
list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) {
|
||||
if ((strlen(xattr->name) == namelen)
|
||||
&& (strncmp(req_xattr_name, xattr->name, namelen) == 0)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
if (strncmp(req_xattr_name,
|
||||
*xattrname + XATTR_SECURITY_PREFIX_LEN,
|
||||
xattr->name + XATTR_SECURITY_PREFIX_LEN,
|
||||
strlen(req_xattr_name)) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
@ -544,35 +557,35 @@ void __init evm_load_x509(void)
|
||||
static int __init init_evm(void)
|
||||
{
|
||||
int error;
|
||||
struct list_head *pos, *q;
|
||||
struct xattr_list *xattr;
|
||||
|
||||
evm_init_config();
|
||||
|
||||
error = integrity_init_keyring(INTEGRITY_KEYRING_EVM);
|
||||
if (error)
|
||||
return error;
|
||||
goto error;
|
||||
|
||||
error = evm_init_secfs();
|
||||
if (error < 0) {
|
||||
pr_info("Error registering secfs\n");
|
||||
return error;
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
if (error != 0) {
|
||||
if (!list_empty(&evm_config_xattrnames)) {
|
||||
list_for_each_safe(pos, q, &evm_config_xattrnames) {
|
||||
xattr = list_entry(pos, struct xattr_list,
|
||||
list);
|
||||
list_del(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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++)
|
||||
pr_info("%s\n", *xattrname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pure_initcall(evm_display_config);
|
||||
late_initcall(init_evm);
|
||||
|
||||
MODULE_DESCRIPTION("Extended Verification Module");
|
||||
|
@ -15,11 +15,21 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/audit.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include "evm.h"
|
||||
|
||||
static struct dentry *evm_dir;
|
||||
static struct dentry *evm_init_tpm;
|
||||
static struct dentry *evm_symlink;
|
||||
|
||||
#ifdef CONFIG_EVM_ADD_XATTRS
|
||||
static struct dentry *evm_xattrs;
|
||||
static DEFINE_MUTEX(xattr_list_mutex);
|
||||
static int evm_xattrs_locked;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* evm_read_key - read() for <securityfs>/evm
|
||||
@ -107,13 +117,203 @@ static const struct file_operations evm_key_ops = {
|
||||
.write = evm_write_key,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_EVM_ADD_XATTRS
|
||||
/**
|
||||
* evm_read_xattrs - read() for <securityfs>/evm_xattrs
|
||||
*
|
||||
* @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_xattrs(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char *temp;
|
||||
int offset = 0;
|
||||
ssize_t rc, size = 0;
|
||||
struct xattr_list *xattr;
|
||||
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
|
||||
rc = mutex_lock_interruptible(&xattr_list_mutex);
|
||||
if (rc)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
list_for_each_entry(xattr, &evm_config_xattrnames, list)
|
||||
size += strlen(xattr->name) + 1;
|
||||
|
||||
temp = kmalloc(size + 1, GFP_KERNEL);
|
||||
if (!temp) {
|
||||
mutex_unlock(&xattr_list_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
list_for_each_entry(xattr, &evm_config_xattrnames, list) {
|
||||
sprintf(temp + offset, "%s\n", xattr->name);
|
||||
offset += strlen(xattr->name) + 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&xattr_list_mutex);
|
||||
rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
|
||||
|
||||
kfree(temp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* evm_write_xattrs - write() for <securityfs>/evm_xattrs
|
||||
* @file: file pointer, not actually used
|
||||
* @buf: where to get the data from
|
||||
* @count: bytes sent
|
||||
* @ppos: where to start
|
||||
*
|
||||
* Returns number of bytes written or error code, as appropriate
|
||||
*/
|
||||
static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int len, err;
|
||||
struct xattr_list *xattr, *tmp;
|
||||
struct audit_buffer *ab;
|
||||
struct iattr newattrs;
|
||||
struct inode *inode;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN) || evm_xattrs_locked)
|
||||
return -EPERM;
|
||||
|
||||
if (*ppos != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (count > XATTR_NAME_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_EVM_XATTR);
|
||||
if (IS_ERR(ab))
|
||||
return PTR_ERR(ab);
|
||||
|
||||
xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL);
|
||||
if (!xattr) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
xattr->name = memdup_user_nul(buf, count);
|
||||
if (IS_ERR(xattr->name)) {
|
||||
err = PTR_ERR(xattr->name);
|
||||
xattr->name = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Remove any trailing newline */
|
||||
len = strlen(xattr->name);
|
||||
if (len && xattr->name[len-1] == '\n')
|
||||
xattr->name[len-1] = '\0';
|
||||
|
||||
if (strcmp(xattr->name, ".") == 0) {
|
||||
evm_xattrs_locked = 1;
|
||||
newattrs.ia_mode = S_IFREG | 0440;
|
||||
newattrs.ia_valid = ATTR_MODE;
|
||||
inode = evm_xattrs->d_inode;
|
||||
inode_lock(inode);
|
||||
err = simple_setattr(evm_xattrs, &newattrs);
|
||||
inode_unlock(inode);
|
||||
audit_log_format(ab, "locked");
|
||||
if (!err)
|
||||
err = count;
|
||||
goto out;
|
||||
}
|
||||
|
||||
audit_log_format(ab, "xattr=");
|
||||
audit_log_untrustedstring(ab, xattr->name);
|
||||
|
||||
if (strncmp(xattr->name, XATTR_SECURITY_PREFIX,
|
||||
XATTR_SECURITY_PREFIX_LEN) != 0) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Guard against races in evm_read_xattrs */
|
||||
mutex_lock(&xattr_list_mutex);
|
||||
list_for_each_entry(tmp, &evm_config_xattrnames, list) {
|
||||
if (strcmp(xattr->name, tmp->name) == 0) {
|
||||
err = -EEXIST;
|
||||
mutex_unlock(&xattr_list_mutex);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
list_add_tail_rcu(&xattr->list, &evm_config_xattrnames);
|
||||
mutex_unlock(&xattr_list_mutex);
|
||||
|
||||
audit_log_format(ab, " res=0");
|
||||
audit_log_end(ab);
|
||||
return count;
|
||||
out:
|
||||
audit_log_format(ab, " res=%d", err);
|
||||
audit_log_end(ab);
|
||||
if (xattr) {
|
||||
kfree(xattr->name);
|
||||
kfree(xattr);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct file_operations evm_xattr_ops = {
|
||||
.read = evm_read_xattrs,
|
||||
.write = evm_write_xattrs,
|
||||
};
|
||||
|
||||
static int evm_init_xattrs(void)
|
||||
{
|
||||
evm_xattrs = securityfs_create_file("evm_xattrs", 0660, evm_dir, NULL,
|
||||
&evm_xattr_ops);
|
||||
if (!evm_xattrs || IS_ERR(evm_xattrs))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int evm_init_xattrs(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
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))
|
||||
evm_dir = securityfs_create_dir("evm", integrity_dir);
|
||||
if (!evm_dir || IS_ERR(evm_dir))
|
||||
return -EFAULT;
|
||||
|
||||
evm_init_tpm = securityfs_create_file("evm", 0660,
|
||||
evm_dir, NULL, &evm_key_ops);
|
||||
if (!evm_init_tpm || IS_ERR(evm_init_tpm)) {
|
||||
error = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
evm_symlink = securityfs_create_symlink("evm", NULL,
|
||||
"integrity/evm/evm", NULL);
|
||||
if (!evm_symlink || IS_ERR(evm_symlink)) {
|
||||
error = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (evm_init_xattrs() != 0) {
|
||||
error = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
securityfs_remove(evm_symlink);
|
||||
securityfs_remove(evm_init_tpm);
|
||||
securityfs_remove(evm_dir);
|
||||
return error;
|
||||
}
|
||||
|
@ -21,12 +21,15 @@
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/security.h>
|
||||
#include "integrity.h"
|
||||
|
||||
static struct rb_root integrity_iint_tree = RB_ROOT;
|
||||
static DEFINE_RWLOCK(integrity_iint_lock);
|
||||
static struct kmem_cache *iint_cache __read_mostly;
|
||||
|
||||
struct dentry *integrity_dir;
|
||||
|
||||
/*
|
||||
* __integrity_iint_find - return the iint associated with an inode
|
||||
*/
|
||||
@ -211,3 +214,18 @@ void __init integrity_load_keys(void)
|
||||
ima_load_x509();
|
||||
evm_load_x509();
|
||||
}
|
||||
|
||||
static int __init integrity_fs_init(void)
|
||||
{
|
||||
integrity_dir = securityfs_create_dir("integrity", NULL);
|
||||
if (IS_ERR(integrity_dir)) {
|
||||
pr_err("Unable to create integrity sysfs dir: %ld\n",
|
||||
PTR_ERR(integrity_dir));
|
||||
integrity_dir = NULL;
|
||||
return PTR_ERR(integrity_dir);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(integrity_fs_init)
|
||||
|
@ -53,7 +53,6 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
|
||||
extern int ima_policy_flag;
|
||||
|
||||
/* set during initialization */
|
||||
extern int ima_initialized;
|
||||
extern int ima_used_chip;
|
||||
extern int ima_hash_algo;
|
||||
extern int ima_appraise;
|
||||
|
@ -15,6 +15,9 @@
|
||||
* implemenents security file system for reporting
|
||||
* current measurement list and IMA statistics
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
@ -336,7 +339,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
|
||||
if (data[0] == '/') {
|
||||
result = ima_read_policy(data);
|
||||
} else if (ima_appraise & IMA_APPRAISE_POLICY) {
|
||||
pr_err("IMA: signed policy file (specified as an absolute pathname) required\n");
|
||||
pr_err("signed policy file (specified as an absolute pathname) required\n");
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
|
||||
"policy_update", "signed policy required",
|
||||
1, 0);
|
||||
@ -356,6 +359,7 @@ out:
|
||||
}
|
||||
|
||||
static struct dentry *ima_dir;
|
||||
static struct dentry *ima_symlink;
|
||||
static struct dentry *binary_runtime_measurements;
|
||||
static struct dentry *ascii_runtime_measurements;
|
||||
static struct dentry *runtime_measurements_count;
|
||||
@ -417,7 +421,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
|
||||
valid_policy = 0;
|
||||
}
|
||||
|
||||
pr_info("IMA: policy update %s\n", cause);
|
||||
pr_info("policy update %s\n", cause);
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
|
||||
"policy_update", cause, !valid_policy, 0);
|
||||
|
||||
@ -434,6 +438,8 @@ static int ima_release_policy(struct inode *inode, struct file *file)
|
||||
ima_policy = NULL;
|
||||
#elif defined(CONFIG_IMA_WRITE_POLICY)
|
||||
clear_bit(IMA_FS_BUSY, &ima_fs_flags);
|
||||
#elif defined(CONFIG_IMA_READ_POLICY)
|
||||
inode->i_mode &= ~S_IWUSR;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@ -448,10 +454,15 @@ static const struct file_operations ima_measure_policy_ops = {
|
||||
|
||||
int __init ima_fs_init(void)
|
||||
{
|
||||
ima_dir = securityfs_create_dir("ima", NULL);
|
||||
ima_dir = securityfs_create_dir("ima", integrity_dir);
|
||||
if (IS_ERR(ima_dir))
|
||||
return -1;
|
||||
|
||||
ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
|
||||
NULL);
|
||||
if (IS_ERR(ima_symlink))
|
||||
goto out;
|
||||
|
||||
binary_runtime_measurements =
|
||||
securityfs_create_file("binary_runtime_measurements",
|
||||
S_IRUSR | S_IRGRP, ima_dir, NULL,
|
||||
@ -491,6 +502,7 @@ out:
|
||||
securityfs_remove(runtime_measurements_count);
|
||||
securityfs_remove(ascii_runtime_measurements);
|
||||
securityfs_remove(binary_runtime_measurements);
|
||||
securityfs_remove(ima_symlink);
|
||||
securityfs_remove(ima_dir);
|
||||
securityfs_remove(ima_policy);
|
||||
return -1;
|
||||
|
@ -10,6 +10,8 @@
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/kexec.h>
|
||||
|
@ -32,8 +32,6 @@
|
||||
|
||||
#include "ima.h"
|
||||
|
||||
int ima_initialized;
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
int ima_appraise = IMA_APPRAISE_ENFORCE;
|
||||
#else
|
||||
@ -61,14 +59,11 @@ static int __init hash_setup(char *str)
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < HASH_ALGO__LAST; i++) {
|
||||
if (strcmp(str, hash_algo_name[i]) == 0) {
|
||||
ima_hash_algo = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == HASH_ALGO__LAST)
|
||||
i = match_string(hash_algo_name, HASH_ALGO__LAST, str);
|
||||
if (i < 0)
|
||||
return 1;
|
||||
|
||||
ima_hash_algo = i;
|
||||
out:
|
||||
hash_setup_done = 1;
|
||||
return 1;
|
||||
@ -449,6 +444,7 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id)
|
||||
|
||||
static int read_idmap[READING_MAX_ID] = {
|
||||
[READING_FIRMWARE] = FIRMWARE_CHECK,
|
||||
[READING_FIRMWARE_PREALLOC_BUFFER] = FIRMWARE_CHECK,
|
||||
[READING_MODULE] = MODULE_CHECK,
|
||||
[READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK,
|
||||
[READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK,
|
||||
@ -517,10 +513,9 @@ static int __init init_ima(void)
|
||||
error = ima_init();
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
ima_initialized = 1;
|
||||
if (!error)
|
||||
ima_update_policy_flag();
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#define IMA_INMASK 0x0040
|
||||
#define IMA_EUID 0x0080
|
||||
#define IMA_PCR 0x0100
|
||||
#define IMA_FSNAME 0x0200
|
||||
|
||||
#define UNKNOWN 0
|
||||
#define MEASURE 0x0001 /* same as IMA_MEASURE */
|
||||
@ -74,6 +75,7 @@ struct ima_rule_entry {
|
||||
void *args_p; /* audit value */
|
||||
int type; /* audit type */
|
||||
} lsm[MAX_LSM_RULES];
|
||||
char *fsname;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -273,6 +275,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
|
||||
if ((rule->flags & IMA_FSMAGIC)
|
||||
&& rule->fsmagic != inode->i_sb->s_magic)
|
||||
return false;
|
||||
if ((rule->flags & IMA_FSNAME)
|
||||
&& strcmp(rule->fsname, inode->i_sb->s_type->name))
|
||||
return false;
|
||||
if ((rule->flags & IMA_FSUUID) &&
|
||||
!uuid_equal(&rule->fsuuid, &inode->i_sb->s_uuid))
|
||||
return false;
|
||||
@ -435,6 +440,17 @@ void ima_update_policy_flag(void)
|
||||
ima_policy_flag &= ~IMA_APPRAISE;
|
||||
}
|
||||
|
||||
static int ima_appraise_flag(enum ima_hooks func)
|
||||
{
|
||||
if (func == MODULE_CHECK)
|
||||
return IMA_APPRAISE_MODULES;
|
||||
else if (func == FIRMWARE_CHECK)
|
||||
return IMA_APPRAISE_FIRMWARE;
|
||||
else if (func == POLICY_CHECK)
|
||||
return IMA_APPRAISE_POLICY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_init_policy - initialize the default measure rules.
|
||||
*
|
||||
@ -473,9 +489,11 @@ void __init ima_init_policy(void)
|
||||
* Insert the appraise rules requiring file signatures, prior to
|
||||
* any other appraise rules.
|
||||
*/
|
||||
for (i = 0; i < secure_boot_entries; i++)
|
||||
list_add_tail(&secure_boot_rules[i].list,
|
||||
&ima_default_rules);
|
||||
for (i = 0; i < secure_boot_entries; i++) {
|
||||
list_add_tail(&secure_boot_rules[i].list, &ima_default_rules);
|
||||
temp_ima_appraise |=
|
||||
ima_appraise_flag(secure_boot_rules[i].func);
|
||||
}
|
||||
|
||||
for (i = 0; i < appraise_entries; i++) {
|
||||
list_add_tail(&default_appraise_rules[i].list,
|
||||
@ -509,22 +527,9 @@ int ima_check_policy(void)
|
||||
*/
|
||||
void ima_update_policy(void)
|
||||
{
|
||||
struct list_head *first, *last, *policy;
|
||||
struct list_head *policy = &ima_policy_rules;
|
||||
|
||||
/* append current policy with the new rules */
|
||||
first = (&ima_temp_rules)->next;
|
||||
last = (&ima_temp_rules)->prev;
|
||||
policy = &ima_policy_rules;
|
||||
|
||||
synchronize_rcu();
|
||||
|
||||
last->next = policy;
|
||||
rcu_assign_pointer(list_next_rcu(policy->prev), first);
|
||||
first->prev = policy->prev;
|
||||
policy->prev = last;
|
||||
|
||||
/* prepare for the next policy rules addition */
|
||||
INIT_LIST_HEAD(&ima_temp_rules);
|
||||
list_splice_tail_init_rcu(&ima_temp_rules, policy, synchronize_rcu);
|
||||
|
||||
if (ima_rules != policy) {
|
||||
ima_policy_flag = 0;
|
||||
@ -540,7 +545,7 @@ enum {
|
||||
Opt_audit, Opt_hash, Opt_dont_hash,
|
||||
Opt_obj_user, Opt_obj_role, Opt_obj_type,
|
||||
Opt_subj_user, Opt_subj_role, Opt_subj_type,
|
||||
Opt_func, Opt_mask, Opt_fsmagic,
|
||||
Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname,
|
||||
Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq,
|
||||
Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt,
|
||||
Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
|
||||
@ -565,6 +570,7 @@ static match_table_t policy_tokens = {
|
||||
{Opt_func, "func=%s"},
|
||||
{Opt_mask, "mask=%s"},
|
||||
{Opt_fsmagic, "fsmagic=%s"},
|
||||
{Opt_fsname, "fsname=%s"},
|
||||
{Opt_fsuuid, "fsuuid=%s"},
|
||||
{Opt_uid_eq, "uid=%s"},
|
||||
{Opt_euid_eq, "euid=%s"},
|
||||
@ -776,6 +782,17 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
if (!result)
|
||||
entry->flags |= IMA_FSMAGIC;
|
||||
break;
|
||||
case Opt_fsname:
|
||||
ima_log_string(ab, "fsname", args[0].from);
|
||||
|
||||
entry->fsname = kstrdup(args[0].from, GFP_KERNEL);
|
||||
if (!entry->fsname) {
|
||||
result = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
result = 0;
|
||||
entry->flags |= IMA_FSNAME;
|
||||
break;
|
||||
case Opt_fsuuid:
|
||||
ima_log_string(ab, "fsuuid", args[0].from);
|
||||
|
||||
@ -917,12 +934,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
}
|
||||
if (!result && (entry->action == UNKNOWN))
|
||||
result = -EINVAL;
|
||||
else if (entry->func == MODULE_CHECK)
|
||||
temp_ima_appraise |= IMA_APPRAISE_MODULES;
|
||||
else if (entry->func == FIRMWARE_CHECK)
|
||||
temp_ima_appraise |= IMA_APPRAISE_FIRMWARE;
|
||||
else if (entry->func == POLICY_CHECK)
|
||||
temp_ima_appraise |= IMA_APPRAISE_POLICY;
|
||||
else if (entry->action == APPRAISE)
|
||||
temp_ima_appraise |= ima_appraise_flag(entry->func);
|
||||
|
||||
audit_log_format(ab, "res=%d", !result);
|
||||
audit_log_end(ab);
|
||||
return result;
|
||||
@ -1104,6 +1118,12 @@ int ima_policy_show(struct seq_file *m, void *v)
|
||||
seq_puts(m, " ");
|
||||
}
|
||||
|
||||
if (entry->flags & IMA_FSNAME) {
|
||||
snprintf(tbuf, sizeof(tbuf), "%s", entry->fsname);
|
||||
seq_printf(m, pt(Opt_fsname), tbuf);
|
||||
seq_puts(m, " ");
|
||||
}
|
||||
|
||||
if (entry->flags & IMA_PCR) {
|
||||
snprintf(tbuf, sizeof(tbuf), "%d", entry->pcr);
|
||||
seq_printf(m, pt(Opt_pcr), tbuf);
|
||||
|
@ -13,6 +13,8 @@
|
||||
* Library of supported template fields.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include "ima_template_lib.h"
|
||||
|
||||
static bool ima_template_hash_algo_allowed(u8 algo)
|
||||
|
@ -143,6 +143,8 @@ int integrity_kernel_read(struct file *file, loff_t offset,
|
||||
#define INTEGRITY_KEYRING_MODULE 2
|
||||
#define INTEGRITY_KEYRING_MAX 3
|
||||
|
||||
extern struct dentry *integrity_dir;
|
||||
|
||||
#ifdef CONFIG_INTEGRITY_SIGNATURE
|
||||
|
||||
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
|
Loading…
Reference in New Issue
Block a user