Commit a6aacbde authored by James Morris's avatar James Morris

Merge branch 'next' of...

Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity into next
parents b10778a0 6fb5032e
...@@ -1330,6 +1330,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ...@@ -1330,6 +1330,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
Formats: { "ima" | "ima-ng" } Formats: { "ima" | "ima-ng" }
Default: "ima-ng" Default: "ima-ng"
ima_template_fmt=
[IMA] Define a custom template format.
Format: { "field1|...|fieldN" }
ima.ahash_minsize= [IMA] Minimum file size for asynchronous hash usage ima.ahash_minsize= [IMA] Minimum file size for asynchronous hash usage
Format: <min_file_size> Format: <min_file_size>
Set the minimal file size for using asynchronous hash. Set the minimal file size for using asynchronous hash.
......
...@@ -27,25 +27,22 @@ Managing templates with these structures is very simple. To support ...@@ -27,25 +27,22 @@ Managing templates with these structures is very simple. To support
a new data type, developers define the field identifier and implement a new data type, developers define the field identifier and implement
two functions, init() and show(), respectively to generate and display two functions, init() and show(), respectively to generate and display
measurement entries. Defining a new template descriptor requires measurement entries. Defining a new template descriptor requires
specifying the template format, a string of field identifiers separated specifying the template format (a string of field identifiers separated
by the '|' character. While in the current implementation it is possible by the '|' character) through the 'ima_template_fmt' kernel command line
to define new template descriptors only by adding their definition in the parameter. At boot time, IMA initializes the chosen template descriptor
template specific code (ima_template.c), in a future version it will be by translating the format into an array of template fields structures taken
possible to register a new template on a running kernel by supplying to IMA from the set of the supported ones.
the desired format string. In this version, IMA initializes at boot time
all defined template descriptors by translating the format into an array
of template fields structures taken from the set of the supported ones.
After the initialization step, IMA will call ima_alloc_init_template() After the initialization step, IMA will call ima_alloc_init_template()
(new function defined within the patches for the new template management (new function defined within the patches for the new template management
mechanism) to generate a new measurement entry by using the template mechanism) to generate a new measurement entry by using the template
descriptor chosen through the kernel configuration or through the newly descriptor chosen through the kernel configuration or through the newly
introduced 'ima_template=' kernel command line parameter. It is during this introduced 'ima_template' and 'ima_template_fmt' kernel command line parameters.
phase that the advantages of the new architecture are clearly shown: It is during this phase that the advantages of the new architecture are
the latter function will not contain specific code to handle a given template clearly shown: the latter function will not contain specific code to handle
but, instead, it simply calls the init() method of the template fields a given template but, instead, it simply calls the init() method of the template
associated to the chosen template descriptor and store the result (pointer fields associated to the chosen template descriptor and store the result
to allocated data and data length) in the measurement entry structure. (pointer to allocated data and data length) in the measurement entry structure.
The same mechanism is employed to display measurements entries. The same mechanism is employed to display measurements entries.
The functions ima[_ascii]_measurements_show() retrieve, for each entry, The functions ima[_ascii]_measurements_show() retrieve, for each entry,
...@@ -86,4 +83,6 @@ currently the following methods are supported: ...@@ -86,4 +83,6 @@ currently the following methods are supported:
- select a template descriptor among those supported in the kernel - select a template descriptor among those supported in the kernel
configuration ('ima-ng' is the default choice); configuration ('ima-ng' is the default choice);
- specify a template descriptor name from the kernel command line through - specify a template descriptor name from the kernel command line through
the 'ima_template=' parameter. the 'ima_template=' parameter;
- register a new template descriptor with custom format through the kernel
command line parameter 'ima_template_fmt='.
...@@ -412,6 +412,23 @@ ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *p ...@@ -412,6 +412,23 @@ ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *p
EXPORT_SYMBOL(new_sync_read); EXPORT_SYMBOL(new_sync_read);
ssize_t __vfs_read(struct file *file, char __user *buf, size_t count,
loff_t *pos)
{
ssize_t ret;
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, pos);
else if (file->f_op->aio_read)
ret = do_sync_read(file, buf, count, pos);
else if (file->f_op->read_iter)
ret = new_sync_read(file, buf, count, pos);
else
ret = -EINVAL;
return ret;
}
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{ {
ssize_t ret; ssize_t ret;
...@@ -426,12 +443,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) ...@@ -426,12 +443,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
ret = rw_verify_area(READ, file, pos, count); ret = rw_verify_area(READ, file, pos, count);
if (ret >= 0) { if (ret >= 0) {
count = ret; count = ret;
if (file->f_op->read) ret = __vfs_read(file, buf, count, pos);
ret = file->f_op->read(file, buf, count, pos);
else if (file->f_op->aio_read)
ret = do_sync_read(file, buf, count, pos);
else
ret = new_sync_read(file, buf, count, pos);
if (ret > 0) { if (ret > 0) {
fsnotify_access(file); fsnotify_access(file);
add_rchar(current, ret); add_rchar(current, ret);
......
...@@ -1553,6 +1553,7 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, ...@@ -1553,6 +1553,7 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
struct iovec *fast_pointer, struct iovec *fast_pointer,
struct iovec **ret_pointer); struct iovec **ret_pointer);
extern ssize_t __vfs_read(struct file *, char __user *, size_t, loff_t *);
extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *); extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *);
extern ssize_t vfs_write(struct file *, const char __user *, size_t, loff_t *); extern ssize_t vfs_write(struct file *, const char __user *, size_t, loff_t *);
extern ssize_t vfs_readv(struct file *, const struct iovec __user *, extern ssize_t vfs_readv(struct file *, const struct iovec __user *,
......
...@@ -24,6 +24,7 @@ enum integrity_status { ...@@ -24,6 +24,7 @@ enum integrity_status {
#ifdef CONFIG_INTEGRITY #ifdef CONFIG_INTEGRITY
extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode); extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode);
extern void integrity_inode_free(struct inode *inode); extern void integrity_inode_free(struct inode *inode);
extern void __init integrity_load_keys(void);
#else #else
static inline struct integrity_iint_cache * static inline struct integrity_iint_cache *
...@@ -36,5 +37,10 @@ static inline void integrity_inode_free(struct inode *inode) ...@@ -36,5 +37,10 @@ static inline void integrity_inode_free(struct inode *inode)
{ {
return; return;
} }
static inline void integrity_load_keys(void)
{
}
#endif /* CONFIG_INTEGRITY */ #endif /* CONFIG_INTEGRITY */
#endif /* _LINUX_INTEGRITY_H */ #endif /* _LINUX_INTEGRITY_H */
...@@ -78,6 +78,7 @@ ...@@ -78,6 +78,7 @@
#include <linux/context_tracking.h> #include <linux/context_tracking.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/integrity.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/bugs.h> #include <asm/bugs.h>
...@@ -1027,8 +1028,11 @@ static noinline void __init kernel_init_freeable(void) ...@@ -1027,8 +1028,11 @@ static noinline void __init kernel_init_freeable(void)
* Ok, we have completed the initial bootup, and * Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the * we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff.. * initmem segments and start the user-mode stuff..
*
* rootfs is available now, try loading the public keys
* and default modules
*/ */
/* rootfs is available now, try loading default modules */ integrity_load_keys();
load_default_modules(); load_default_modules();
} }
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/rbtree.h> #include <linux/slab.h>
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/key-type.h> #include <linux/key-type.h>
#include <linux/digsig.h> #include <linux/digsig.h>
...@@ -63,7 +63,7 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, ...@@ -63,7 +63,7 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
int integrity_init_keyring(const unsigned int id) int __init integrity_init_keyring(const unsigned int id)
{ {
const struct cred *cred = current_cred(); const struct cred *cred = current_cred();
int err = 0; int err = 0;
...@@ -84,3 +84,37 @@ int integrity_init_keyring(const unsigned int id) ...@@ -84,3 +84,37 @@ int integrity_init_keyring(const unsigned int id)
} }
return err; return err;
} }
int __init integrity_load_x509(const unsigned int id, char *path)
{
key_ref_t key;
char *data;
int rc;
if (!keyring[id])
return -EINVAL;
rc = integrity_read_file(path, &data);
if (rc < 0)
return rc;
key = key_create_or_update(make_key_ref(keyring[id], 1),
"asymmetric",
NULL,
data,
rc,
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ),
KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_TRUSTED);
if (IS_ERR(key)) {
rc = PTR_ERR(key);
pr_err("Problem loading X.509 certificate (%d): %s\n",
rc, path);
} else {
pr_notice("Loaded X.509 cert '%s': %s\n",
key_ref_to_ptr(key)->description, path);
key_ref_put(key);
}
kfree(data);
return 0;
}
...@@ -162,9 +162,14 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, ...@@ -162,9 +162,14 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
(const char *)xattr_data, xattr_len, (const char *)xattr_data, xattr_len,
calc.digest, sizeof(calc.digest)); calc.digest, sizeof(calc.digest));
if (!rc) { if (!rc) {
/* we probably want to replace rsa with hmac here */ /* Replace RSA with HMAC if not mounted readonly and
evm_update_evmxattr(dentry, xattr_name, xattr_value, * not immutable
xattr_value_len); */
if (!IS_RDONLY(dentry->d_inode) &&
!IS_IMMUTABLE(dentry->d_inode))
evm_update_evmxattr(dentry, xattr_name,
xattr_value,
xattr_value_len);
} }
break; break;
default: default:
......
...@@ -19,14 +19,14 @@ ...@@ -19,14 +19,14 @@
#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 <linux/file.h>
#include <linux/uaccess.h>
#include "integrity.h" #include "integrity.h"
static struct rb_root integrity_iint_tree = RB_ROOT; static struct rb_root integrity_iint_tree = RB_ROOT;
static DEFINE_RWLOCK(integrity_iint_lock); static DEFINE_RWLOCK(integrity_iint_lock);
static struct kmem_cache *iint_cache __read_mostly; static struct kmem_cache *iint_cache __read_mostly;
int iint_initialized;
/* /*
* __integrity_iint_find - return the iint associated with an inode * __integrity_iint_find - return the iint associated with an inode
*/ */
...@@ -166,7 +166,89 @@ static int __init integrity_iintcache_init(void) ...@@ -166,7 +166,89 @@ static int __init integrity_iintcache_init(void)
iint_cache = iint_cache =
kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache), kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
0, SLAB_PANIC, init_once); 0, SLAB_PANIC, init_once);
iint_initialized = 1;
return 0; return 0;
} }
security_initcall(integrity_iintcache_init); security_initcall(integrity_iintcache_init);
/*
* integrity_kernel_read - read data from the file
*
* This is a function for reading file content instead of kernel_read().
* It does not perform locking checks to ensure it cannot be blocked.
* It does not perform security checks because it is irrelevant for IMA.
*
*/
int integrity_kernel_read(struct file *file, loff_t offset,
char *addr, unsigned long count)
{
mm_segment_t old_fs;
char __user *buf = (char __user *)addr;
ssize_t ret;
if (!(file->f_mode & FMODE_READ))
return -EBADF;
old_fs = get_fs();
set_fs(get_ds());
ret = __vfs_read(file, buf, count, &offset);
set_fs(old_fs);
return ret;
}
/*
* integrity_read_file - read entire file content into the buffer
*
* This is function opens a file, allocates the buffer of required
* size, read entire file content to the buffer and closes the file
*
* It is used only by init code.
*
*/
int __init integrity_read_file(const char *path, char **data)
{
struct file *file;
loff_t size;
char *buf;
int rc = -EINVAL;
file = filp_open(path, O_RDONLY, 0);
if (IS_ERR(file)) {
rc = PTR_ERR(file);
pr_err("Unable to open file: %s (%d)", path, rc);
return rc;
}
size = i_size_read(file_inode(file));
if (size <= 0)
goto out;
buf = kmalloc(size, GFP_KERNEL);
if (!buf) {
rc = -ENOMEM;
goto out;
}
rc = integrity_kernel_read(file, 0, buf, size);
if (rc < 0)
kfree(buf);
else if (rc != size)
rc = -EIO;
else
*data = buf;
out:
fput(file);
return rc;
}
/*
* integrity_load_keys - load integrity keys hook
*
* Hooks is called from init/main.c:kernel_init_freeable()
* when rootfs is ready
*/
void __init integrity_load_keys(void)
{
ima_load_x509();
}
...@@ -131,3 +131,28 @@ config IMA_TRUSTED_KEYRING ...@@ -131,3 +131,28 @@ config IMA_TRUSTED_KEYRING
help help
This option requires that all keys added to the .ima This option requires that all keys added to the .ima
keyring be signed by a key on the system trusted keyring. keyring be signed by a key on the system trusted keyring.
config IMA_LOAD_X509
bool "Load X509 certificate onto the '.ima' trusted keyring"
depends on IMA_TRUSTED_KEYRING
default n
help
File signature verification is based on the public keys
loaded on the .ima trusted keyring. These public keys are
X509 certificates signed by a trusted key on the
.system keyring. This option enables X509 certificate
loading from the kernel onto the '.ima' trusted keyring.
config IMA_X509_PATH
string "IMA X509 certificate path"
depends on IMA_LOAD_X509
default "/etc/keys/x509_ima.der"
help
This option defines IMA X509 certificate path.
config IMA_APPRAISE_SIGNED_INIT
bool "Require signed user-space initialization"
depends on IMA_LOAD_X509
default n
help
This option requires user-space init to be signed.
...@@ -173,8 +173,7 @@ int ima_get_action(struct inode *inode, int mask, int function) ...@@ -173,8 +173,7 @@ int ima_get_action(struct inode *inode, int mask, int function)
{ {
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE; int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE;
if (!ima_appraise) flags &= ima_policy_flag;
flags &= ~IMA_APPRAISE;
return ima_match_policy(inode, function, mask, flags); return ima_match_policy(inode, function, mask, flags);
} }
...@@ -325,11 +324,11 @@ const char *ima_d_path(struct path *path, char **pathbuf) ...@@ -325,11 +324,11 @@ const char *ima_d_path(struct path *path, char **pathbuf)
{ {
char *pathname = NULL; char *pathname = NULL;
*pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); *pathbuf = __getname();
if (*pathbuf) { if (*pathbuf) {
pathname = d_absolute_path(path, *pathbuf, PATH_MAX); pathname = d_absolute_path(path, *pathbuf, PATH_MAX);
if (IS_ERR(pathname)) { if (IS_ERR(pathname)) {
kfree(*pathbuf); __putname(*pathbuf);
*pathbuf = NULL; *pathbuf = NULL;
pathname = NULL; pathname = NULL;
} }
......
...@@ -67,36 +67,6 @@ MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size"); ...@@ -67,36 +67,6 @@ MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size");
static struct crypto_shash *ima_shash_tfm; static struct crypto_shash *ima_shash_tfm;
static struct crypto_ahash *ima_ahash_tfm; static struct crypto_ahash *ima_ahash_tfm;
/**
* ima_kernel_read - read file content
*
* This is a function for reading file content instead of kernel_read().
* It does not perform locking checks to ensure it cannot be blocked.
* It does not perform security checks because it is irrelevant for IMA.
*
*/
static int ima_kernel_read(struct file *file, loff_t offset,
char *addr, unsigned long count)
{
mm_segment_t old_fs;
char __user *buf = addr;
ssize_t ret = -EINVAL;
if (!(file->f_mode & FMODE_READ))
return -EBADF;
old_fs = get_fs();
set_fs(get_ds());
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, &offset);
else if (file->f_op->aio_read)
ret = do_sync_read(file, buf, count, &offset);
else if (file->f_op->read_iter)
ret = new_sync_read(file, buf, count, &offset);
set_fs(old_fs);
return ret;
}
int __init ima_init_crypto(void) int __init ima_init_crypto(void)
{ {
long rc; long rc;
...@@ -324,7 +294,8 @@ static int ima_calc_file_hash_atfm(struct file *file, ...@@ -324,7 +294,8 @@ static int ima_calc_file_hash_atfm(struct file *file,
} }
/* read buffer */ /* read buffer */
rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]); rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]);
rc = ima_kernel_read(file, offset, rbuf[active], rbuf_len); rc = integrity_kernel_read(file, offset, rbuf[active],
rbuf_len);
if (rc != rbuf_len) if (rc != rbuf_len)
goto out3; goto out3;
...@@ -417,7 +388,7 @@ static int ima_calc_file_hash_tfm(struct file *file, ...@@ -417,7 +388,7 @@ static int ima_calc_file_hash_tfm(struct file *file,
while (offset < i_size) { while (offset < i_size) {
int rbuf_len; int rbuf_len;
rbuf_len = ima_kernel_read(file, offset, rbuf, PAGE_SIZE); rbuf_len = integrity_kernel_read(file, offset, rbuf, PAGE_SIZE);
if (rbuf_len < 0) { if (rbuf_len < 0) {
rc = rbuf_len; rc = rbuf_len;
break; break;
......
...@@ -118,6 +118,7 @@ static int ima_measurements_show(struct seq_file *m, void *v) ...@@ -118,6 +118,7 @@ static int ima_measurements_show(struct seq_file *m, void *v)
/* the list never shrinks, so we don't need a lock here */ /* the list never shrinks, so we don't need a lock here */
struct ima_queue_entry *qe = v; struct ima_queue_entry *qe = v;
struct ima_template_entry *e; struct ima_template_entry *e;
char *template_name;
int namelen; int namelen;
u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX; u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
bool is_ima_template = false; bool is_ima_template = false;
...@@ -128,6 +129,9 @@ static int ima_measurements_show(struct seq_file *m, void *v) ...@@ -128,6 +129,9 @@ static int ima_measurements_show(struct seq_file *m, void *v)
if (e == NULL) if (e == NULL)
return -1; return -1;
template_name = (e->template_desc->name[0] != '\0') ?
e->template_desc->name : e->template_desc->fmt;
/* /*
* 1st: PCRIndex * 1st: PCRIndex
* PCR used is always the same (config option) in * PCR used is always the same (config option) in
...@@ -139,14 +143,14 @@ static int ima_measurements_show(struct seq_file *m, void *v) ...@@ -139,14 +143,14 @@ static int ima_measurements_show(struct seq_file *m, void *v)
ima_putc(m, e->digest, TPM_DIGEST_SIZE); ima_putc(m, e->digest, TPM_DIGEST_SIZE);
/* 3rd: template name size */ /* 3rd: template name size */
namelen = strlen(e->template_desc->name); namelen = strlen(template_name);
ima_putc(m, &namelen, sizeof(namelen)); ima_putc(m, &namelen, sizeof(namelen));
/* 4th: template name */ /* 4th: template name */
ima_putc(m, e->template_desc->name, namelen); ima_putc(m, template_name, namelen);
/* 5th: template length (except for 'ima' template) */ /* 5th: template length (except for 'ima' template) */
if (strcmp(e->template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) if (strcmp(template_name, IMA_TEMPLATE_IMA_NAME) == 0)
is_ima_template = true; is_ima_template = true;
if (!is_ima_template) if (!is_ima_template)
...@@ -200,6 +204,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) ...@@ -200,6 +204,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
/* the list never shrinks, so we don't need a lock here */ /* the list never shrinks, so we don't need a lock here */
struct ima_queue_entry *qe = v; struct ima_queue_entry *qe = v;
struct ima_template_entry *e; struct ima_template_entry *e;
char *template_name;
int i; int i;
/* get entry */ /* get entry */
...@@ -207,6 +212,9 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) ...@@ -207,6 +212,9 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
if (e == NULL) if (e == NULL)
return -1; return -1;
template_name = (e->template_desc->name[0] != '\0') ?
e->template_desc->name : e->template_desc->fmt;
/* 1st: PCR used (config option) */ /* 1st: PCR used (config option) */
seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX); seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
...@@ -214,7 +222,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) ...@@ -214,7 +222,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
ima_print_digest(m, e->digest, TPM_DIGEST_SIZE); ima_print_digest(m, e->digest, TPM_DIGEST_SIZE);
/* 3th: template name */ /* 3th: template name */
seq_printf(m, " %s", e->template_desc->name); seq_printf(m, " %s", template_name);
/* 4th: template specific data */ /* 4th: template specific data */
for (i = 0; i < e->template_desc->num_fields; i++) { for (i = 0; i < e->template_desc->num_fields; i++) {
...@@ -288,7 +296,12 @@ static struct dentry *runtime_measurements_count; ...@@ -288,7 +296,12 @@ static struct dentry *runtime_measurements_count;
static struct dentry *violations; static struct dentry *violations;
static struct dentry *ima_policy; static struct dentry *ima_policy;
static atomic_t policy_opencount = ATOMIC_INIT(1); enum ima_fs_flags {
IMA_FS_BUSY,
};
static unsigned long ima_fs_flags;
/* /*
* ima_open_policy: sequentialize access to the policy file * ima_open_policy: sequentialize access to the policy file
*/ */
...@@ -297,9 +310,9 @@ static int ima_open_policy(struct inode *inode, struct file *filp) ...@@ -297,9 +310,9 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
/* No point in being allowed to open it if you aren't going to write */ /* No point in being allowed to open it if you aren't going to write */
if (!(filp->f_flags & O_WRONLY)) if (!(filp->f_flags & O_WRONLY))
return -EACCES; return -EACCES;
if (atomic_dec_and_test(&policy_opencount)) if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags))
return 0; return -EBUSY;
return -EBUSY; return 0;
} }
/* /*
...@@ -311,10 +324,16 @@ static int ima_open_policy(struct inode *inode, struct file *filp) ...@@ -311,10 +324,16 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
*/ */
static int ima_release_policy(struct inode *inode, struct file *file) static int ima_release_policy(struct inode *inode, struct file *file)
{ {
const char *cause = valid_policy ? "completed" : "failed";
pr_info("IMA: policy update %s\n", cause);
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
"policy_update", cause, !valid_policy, 0);
if (!valid_policy) { if (!valid_policy) {
ima_delete_rules(); ima_delete_rules();
valid_policy = 1; valid_policy = 1;
atomic_set(&policy_opencount, 1); clear_bit(IMA_FS_BUSY, &ima_fs_flags);
return 0; return 0;
} }
ima_update_policy(); ima_update_policy();
......
...@@ -24,6 +24,12 @@ ...@@ -24,6 +24,12 @@
#include <crypto/hash_info.h> #include <crypto/hash_info.h>
#include "ima.h" #include "ima.h"
#ifdef CONFIG_IMA_X509_PATH
#define IMA_X509_PATH CONFIG_IMA_X509_PATH
#else
#define IMA_X509_PATH "/etc/keys/x509_ima.der"
#endif
/* name for boot aggregate entry */ /* name for boot aggregate entry */
static const char *boot_aggregate_name = "boot_aggregate"; static const char *boot_aggregate_name = "boot_aggregate";
int ima_used_chip; int ima_used_chip;
...@@ -91,6 +97,17 @@ static int __init ima_add_boot_aggregate(void) ...@@ -91,6 +97,17 @@ static int __init ima_add_boot_aggregate(void)
return result; return result;
} }
#ifdef CONFIG_IMA_LOAD_X509
void __init ima_load_x509(void)
{
int unset_flags = ima_policy_flag & IMA_APPRAISE;
ima_policy_flag &= ~unset_flags;
integrity_load_x509(INTEGRITY_KEYRING_IMA, IMA_X509_PATH);
ima_policy_flag |= unset_flags;
}
#endif
int __init ima_init(void) int __init ima_init(void)
{ {
u8 pcr_i[TPM_DIGEST_SIZE]; u8 pcr_i[TPM_DIGEST_SIZE];
......
...@@ -143,7 +143,7 @@ void ima_file_free(struct file *file) ...@@ -143,7 +143,7 @@ void ima_file_free(struct file *file)
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint; struct integrity_iint_cache *iint;
if (!iint_initialized || !S_ISREG(inode->i_mode)) if (!ima_policy_flag || !S_ISREG(inode->i_mode))
return; return;
iint = integrity_iint_find(inode); iint = integrity_iint_find(inode);
...@@ -246,7 +246,8 @@ static int process_measurement(struct file *file, int mask, int function, ...@@ -246,7 +246,8 @@ static int process_measurement(struct file *file, int mask, int function,
rc = -EACCES; rc = -EACCES;
kfree(xattr_value); kfree(xattr_value);
out_free: out_free:
kfree(pathbuf); if (pathbuf)
__putname(pathbuf);
out: out:
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
......
...@@ -100,7 +100,13 @@ static struct ima_rule_entry default_appraise_rules[] = { ...@@ -100,7 +100,13 @@ static struct ima_rule_entry default_appraise_rules[] = {
{.action = DONT_APPRAISE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC},
#ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT
{.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER}, {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER},
#else
/* force signature */
{.action = APPRAISE, .fowner = GLOBAL_ROOT_UID,
.flags = IMA_FOWNER | IMA_DIGSIG_REQUIRED},
#endif
}; };
static LIST_HEAD(ima_default_rules); static LIST_HEAD(ima_default_rules);
...@@ -356,19 +362,8 @@ void __init ima_init_policy(void) ...@@ -356,19 +362,8 @@ void __init ima_init_policy(void)
*/ */
void ima_update_policy(void) void ima_update_policy(void)
{ {
static const char op[] = "policy_update"; ima_rules = &ima_policy_rules;
const char *cause = "already-exists"; ima_update_policy_flag();
int result = 1;
int audit_info = 0;
if (ima_rules == &ima_default_rules) {
ima_rules = &ima_policy_rules;
ima_update_policy_flag();
cause = "complete";
result = 0;
}
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
NULL, op, cause, result, audit_info);
} }
enum { enum {
...@@ -686,13 +681,12 @@ ssize_t ima_parse_add_rule(char *rule) ...@@ -686,13 +681,12 @@ ssize_t ima_parse_add_rule(char *rule)
ssize_t result, len; ssize_t result, len;
int audit_info = 0; int audit_info = 0;
/* Prevent installed policy from changing */ p = strsep(&rule, "\n");
if (ima_rules != &ima_default_rules) { len = strlen(p) + 1;
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, p += strspn(p, " \t");
NULL, op, "already-exists",
-EACCES, audit_info); if (*p == '#' || *p == '\0')
return -EACCES; return len;
}
entry = kzalloc(sizeof(*entry), GFP_KERNEL); entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) { if (!entry) {
...@@ -703,14 +697,6 @@ ssize_t ima_parse_add_rule(char *rule) ...@@ -703,14 +697,6 @@ ssize_t ima_parse_add_rule(char *rule)
INIT_LIST_HEAD(&entry->list); INIT_LIST_HEAD(&entry->list);
p = strsep(&rule, "\n");
len = strlen(p) + 1;
if (*p == '#') {
kfree(entry);
return len;
}
result = ima_parse_rule(p, entry); result = ima_parse_rule(p, entry);
if (result) { if (result) {
kfree(entry); kfree(entry);
......
...@@ -24,6 +24,7 @@ static struct ima_template_desc defined_templates[] = { ...@@ -24,6 +24,7 @@ static struct ima_template_desc defined_templates[] = {
{.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT}, {.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
{.name = "ima-ng", .fmt = "d-ng|n-ng"}, {.name = "ima-ng", .fmt = "d-ng|n-ng"},
{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"}, {.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
{.name = "", .fmt = ""}, /* placeholder for a custom format */
}; };
static struct ima_template_field supported_fields[] = { static struct ima_template_field supported_fields[] = {
...@@ -41,19 +42,28 @@ static struct ima_template_field supported_fields[] = { ...@@ -41,19 +42,28 @@ static struct ima_template_field supported_fields[] = {
static struct ima_template_desc *ima_template; static struct ima_template_desc *ima_template;
static struct ima_template_desc *lookup_template_desc(const char *name); static struct ima_template_desc *lookup_template_desc(const char *name);
static int template_desc_init_fields(const char *template_fmt,
struct ima_template_field ***fields,
int *num_fields);
static int __init ima_template_setup(char *str) static int __init ima_template_setup(char *str)
{ {
struct ima_template_desc *template_desc; struct ima_template_desc *template_desc;
int template_len = strlen(str); int template_len = strlen(str);
if (ima_template)
return 1;
/* /*
* Verify that a template with the supplied name exists. * Verify that a template with the supplied name exists.
* If not, use CONFIG_IMA_DEFAULT_TEMPLATE. * If not, use CONFIG_IMA_DEFAULT_TEMPLATE.
*/ */
template_desc = lookup_template_desc(str); template_desc = lookup_template_desc(str);
if (!template_desc) if (!template_desc) {
pr_err("template %s not found, using %s\n",
str, CONFIG_IMA_DEFAULT_TEMPLATE);
return 1; return 1;
}
/* /*
* Verify whether the current hash algorithm is supported * Verify whether the current hash algorithm is supported
...@@ -70,6 +80,25 @@ static int __init ima_template_setup(char *str) ...@@ -70,6 +80,25 @@ static int __init ima_template_setup(char *str)
} }
__setup("ima_template=", ima_template_setup); __setup("ima_template=", ima_template_setup);
static int __init ima_template_fmt_setup(char *str)
{
int num_templates = ARRAY_SIZE(defined_templates);
if (ima_template)
return 1;
if (template_desc_init_fields(str, NULL, NULL) < 0) {
pr_err("format string '%s' not valid, using template %s\n",
str, CONFIG_IMA_DEFAULT_TEMPLATE);
return 1;
}
defined_templates[num_templates - 1].fmt = str;
ima_template = defined_templates + num_templates - 1;
return 1;
}
__setup("ima_template_fmt=", ima_template_fmt_setup);
static struct ima_template_desc *lookup_template_desc(const char *name) static struct ima_template_desc *lookup_template_desc(const char *name)
{ {
int i; int i;
...@@ -113,43 +142,46 @@ static int template_desc_init_fields(const char *template_fmt, ...@@ -113,43 +142,46 @@ static int template_desc_init_fields(const char *template_fmt,
struct ima_template_field ***fields, struct ima_template_field ***fields,
int *num_fields) int *num_fields)
{ {
char *c, *template_fmt_copy, *template_fmt_ptr; const char *template_fmt_ptr;
struct ima_template_field *found_fields[IMA_TEMPLATE_NUM_FIELDS_MAX];
int template_num_fields = template_fmt_size(template_fmt); int template_num_fields = template_fmt_size(template_fmt);
int i, result = 0; int i, len;
if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) {
pr_err("format string '%s' contains too many fields\n",
template_fmt);
return -EINVAL; return -EINVAL;
/* copying is needed as strsep() modifies the original buffer */
template_fmt_copy = kstrdup(template_fmt, GFP_KERNEL);
if (template_fmt_copy == NULL)
return -ENOMEM;
*fields = kzalloc(template_num_fields * sizeof(*fields), GFP_KERNEL);
if (*fields == NULL) {
result = -ENOMEM;
goto out;
} }
template_fmt_ptr = template_fmt_copy; for (i = 0, template_fmt_ptr = template_fmt; i < template_num_fields;
for (i = 0; (c = strsep(&template_fmt_ptr, "|")) != NULL && i++, template_fmt_ptr += len + 1) {
i < template_num_fields; i++) { char tmp_field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN + 1];
struct ima_template_field *f = lookup_template_field(c);
len = strchrnul(template_fmt_ptr, '|') - template_fmt_ptr;
if (len == 0 || len > IMA_TEMPLATE_FIELD_ID_MAX_LEN) {
pr_err("Invalid field with length %d\n", len);
return -EINVAL;
}
if (!f) { memcpy(tmp_field_id, template_fmt_ptr, len);
result = -ENOENT; tmp_field_id[len] = '\0';
goto out; found_fields[i] = lookup_template_field(tmp_field_id);
if (!found_fields[i]) {
pr_err("field '%s' not found\n", tmp_field_id);
return -ENOENT;
} }
(*fields)[i] = f;
} }
*num_fields = i;
out: if (fields && num_fields) {
if (result < 0) { *fields = kmalloc_array(i, sizeof(*fields), GFP_KERNEL);
kfree(*fields); if (*fields == NULL)
*fields = NULL; return -ENOMEM;
memcpy(*fields, found_fields, i * sizeof(*fields));
*num_fields = i;
} }
kfree(template_fmt_copy);
return result; return 0;
} }
struct ima_template_desc *ima_template_desc_current(void) struct ima_template_desc *ima_template_desc_current(void)
...@@ -163,8 +195,15 @@ struct ima_template_desc *ima_template_desc_current(void) ...@@ -163,8 +195,15 @@ struct ima_template_desc *ima_template_desc_current(void)
int __init ima_init_template(void) int __init ima_init_template(void)
{ {
struct ima_template_desc *template = ima_template_desc_current(); struct ima_template_desc *template = ima_template_desc_current();
int result;
result = template_desc_init_fields(template->fmt,
&(template->fields),
&(template->num_fields));
if (result < 0)
pr_err("template %s init failed, result: %d\n",
(strlen(template->name) ?
template->name : template->fmt), result);
return template_desc_init_fields(template->fmt, return result;
&(template->fields),
&(template->num_fields));
} }
...@@ -119,6 +119,10 @@ struct integrity_iint_cache { ...@@ -119,6 +119,10 @@ struct integrity_iint_cache {
*/ */
struct integrity_iint_cache *integrity_iint_find(struct inode *inode); struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
int integrity_kernel_read(struct file *file, loff_t offset,
char *addr, unsigned long count);
int __init integrity_read_file(const char *path, char **data);
#define INTEGRITY_KEYRING_EVM 0 #define INTEGRITY_KEYRING_EVM 0
#define INTEGRITY_KEYRING_MODULE 1 #define INTEGRITY_KEYRING_MODULE 1
#define INTEGRITY_KEYRING_IMA 2 #define INTEGRITY_KEYRING_IMA 2
...@@ -129,7 +133,8 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode); ...@@ -129,7 +133,8 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
const char *digest, int digestlen); const char *digest, int digestlen);
int integrity_init_keyring(const unsigned int id); int __init integrity_init_keyring(const unsigned int id);
int __init integrity_load_x509(const unsigned int id, char *path);
#else #else
static inline int integrity_digsig_verify(const unsigned int id, static inline int integrity_digsig_verify(const unsigned int id,
...@@ -143,6 +148,7 @@ static inline int integrity_init_keyring(const unsigned int id) ...@@ -143,6 +148,7 @@ static inline int integrity_init_keyring(const unsigned int id)
{ {
return 0; return 0;
} }
#endif /* CONFIG_INTEGRITY_SIGNATURE */ #endif /* CONFIG_INTEGRITY_SIGNATURE */
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
...@@ -156,6 +162,14 @@ static inline int asymmetric_verify(struct key *keyring, const char *sig, ...@@ -156,6 +162,14 @@ static inline int asymmetric_verify(struct key *keyring, const char *sig,
} }
#endif #endif
#ifdef CONFIG_IMA_LOAD_X509
void __init ima_load_x509(void);
#else
static inline void ima_load_x509(void)
{
}
#endif
#ifdef CONFIG_INTEGRITY_AUDIT #ifdef CONFIG_INTEGRITY_AUDIT
/* declarations */ /* declarations */
void integrity_audit_msg(int audit_msgno, struct inode *inode, void integrity_audit_msg(int audit_msgno, struct inode *inode,
...@@ -169,6 +183,3 @@ static inline void integrity_audit_msg(int audit_msgno, struct inode *inode, ...@@ -169,6 +183,3 @@ static inline void integrity_audit_msg(int audit_msgno, struct inode *inode,
{ {
} }
#endif #endif
/* set during initialization */
extern int iint_initialized;
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