Commit aa569fa0 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'serge-next-2' of git://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux-security

Pull more security layer updates from Serge Hallyn:
 "A few more commits had previously failed to make it through
  security-next into linux-next but this week made it into linux-next.
  At least commit "ima: introduce ima_kernel_read()" was deemed critical
  by Mimi to make this merge window.

  This is a temporary tree just for this request.  Mimi has pointed me
  to some previous threads about keeping maintainer trees at the
  previous release, which I'll certainly do for anything long-term,
  after talking with James"

* 'serge-next-2' of git://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux-security:
  ima: introduce ima_kernel_read()
  evm: prohibit userspace writing 'security.evm' HMAC value
  ima: check inode integrity cache in violation check
  ima: prevent unnecessary policy checking
  evm: provide option to protect additional SMACK xattrs
  evm: replace HMAC version with attribute mask
  ima: prevent new digsig xattr from being replaced
parents 6d87c225 0430e49b
...@@ -12,15 +12,41 @@ config EVM ...@@ -12,15 +12,41 @@ config EVM
If you are unsure how to answer this question, answer N. If you are unsure how to answer this question, answer N.
config EVM_HMAC_VERSION if EVM
int "EVM HMAC version"
menu "EVM options"
config EVM_ATTR_FSUUID
bool "FSUUID (version 2)"
default y
depends on EVM depends on EVM
default 2
help help
This options adds EVM HMAC version support. Include filesystem UUID for HMAC calculation.
1 - original version
2 - add per filesystem unique identifier (UUID) (default) Default value is 'selected', which is former version 2.
if 'not selected', it is former version 1
WARNING: changing the HMAC calculation method or adding WARNING: changing the HMAC calculation method or adding
additional info to the calculation, requires existing EVM additional info to the calculation, requires existing EVM
labeled file systems to be relabeled. labeled file systems to be relabeled.
config EVM_EXTRA_SMACK_XATTRS
bool "Additional SMACK xattrs"
depends on EVM && SECURITY_SMACK
default n
help
Include additional SMACK xattrs for HMAC calculation.
In addition to the original security xattrs (eg. security.selinux,
security.SMACK64, security.capability, and security.ima) included
in the HMAC calculation, enabling this option includes newly defined
Smack xattrs: security.SMACK64EXEC, security.SMACK64TRANSMUTE and
security.SMACK64MMAP.
WARNING: changing the HMAC calculation method or adding
additional info to the calculation, requires existing EVM
labeled file systems to be relabeled.
endmenu
endif
...@@ -24,7 +24,10 @@ ...@@ -24,7 +24,10 @@
extern int evm_initialized; extern int evm_initialized;
extern char *evm_hmac; extern char *evm_hmac;
extern char *evm_hash; extern char *evm_hash;
extern int evm_hmac_version;
#define EVM_ATTR_FSUUID 0x0001
extern int evm_hmac_attrs;
extern struct crypto_shash *hmac_tfm; extern struct crypto_shash *hmac_tfm;
extern struct crypto_shash *hash_tfm; extern struct crypto_shash *hash_tfm;
......
...@@ -112,7 +112,7 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, ...@@ -112,7 +112,7 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid); hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
hmac_misc.mode = inode->i_mode; hmac_misc.mode = inode->i_mode;
crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc)); crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
if (evm_hmac_version > 1) if (evm_hmac_attrs & EVM_ATTR_FSUUID)
crypto_shash_update(desc, inode->i_sb->s_uuid, crypto_shash_update(desc, inode->i_sb->s_uuid,
sizeof(inode->i_sb->s_uuid)); sizeof(inode->i_sb->s_uuid));
crypto_shash_final(desc, digest); crypto_shash_final(desc, digest);
......
...@@ -32,7 +32,7 @@ static char *integrity_status_msg[] = { ...@@ -32,7 +32,7 @@ static char *integrity_status_msg[] = {
}; };
char *evm_hmac = "hmac(sha1)"; char *evm_hmac = "hmac(sha1)";
char *evm_hash = "sha1"; char *evm_hash = "sha1";
int evm_hmac_version = CONFIG_EVM_HMAC_VERSION; int evm_hmac_attrs;
char *evm_config_xattrnames[] = { char *evm_config_xattrnames[] = {
#ifdef CONFIG_SECURITY_SELINUX #ifdef CONFIG_SECURITY_SELINUX
...@@ -40,6 +40,11 @@ char *evm_config_xattrnames[] = { ...@@ -40,6 +40,11 @@ char *evm_config_xattrnames[] = {
#endif #endif
#ifdef CONFIG_SECURITY_SMACK #ifdef CONFIG_SECURITY_SMACK
XATTR_NAME_SMACK, XATTR_NAME_SMACK,
#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
XATTR_NAME_SMACKEXEC,
XATTR_NAME_SMACKTRANSMUTE,
XATTR_NAME_SMACKMMAP,
#endif
#endif #endif
#ifdef CONFIG_IMA_APPRAISE #ifdef CONFIG_IMA_APPRAISE
XATTR_NAME_IMA, XATTR_NAME_IMA,
...@@ -57,6 +62,14 @@ static int __init evm_set_fixmode(char *str) ...@@ -57,6 +62,14 @@ static int __init evm_set_fixmode(char *str)
} }
__setup("evm=", evm_set_fixmode); __setup("evm=", evm_set_fixmode);
static void __init evm_init_config(void)
{
#ifdef CONFIG_EVM_ATTR_FSUUID
evm_hmac_attrs |= EVM_ATTR_FSUUID;
#endif
pr_info("HMAC attrs: 0x%x\n", evm_hmac_attrs);
}
static int evm_find_protected_xattrs(struct dentry *dentry) static int evm_find_protected_xattrs(struct dentry *dentry)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
...@@ -287,12 +300,20 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, ...@@ -287,12 +300,20 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
* @xattr_value: pointer to the new extended attribute value * @xattr_value: pointer to the new extended attribute value
* @xattr_value_len: pointer to the new extended attribute value length * @xattr_value_len: pointer to the new extended attribute value length
* *
* Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that * Before allowing the 'security.evm' protected xattr to be updated,
* the current value is valid. * verify the existing value is valid. As only the kernel should have
* access to the EVM encrypted key needed to calculate the HMAC, prevent
* userspace from writing HMAC value. Writing 'security.evm' requires
* requires CAP_SYS_ADMIN privileges.
*/ */
int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name, int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len) const void *xattr_value, size_t xattr_value_len)
{ {
const struct evm_ima_xattr_data *xattr_data = xattr_value;
if ((strcmp(xattr_name, XATTR_NAME_EVM) == 0)
&& (xattr_data->type == EVM_XATTR_HMAC))
return -EPERM;
return evm_protect_xattr(dentry, xattr_name, xattr_value, return evm_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len); xattr_value_len);
} }
...@@ -432,6 +453,8 @@ static int __init init_evm(void) ...@@ -432,6 +453,8 @@ static int __init init_evm(void)
{ {
int error; int error;
evm_init_config();
error = evm_init_secfs(); error = evm_init_secfs();
if (error < 0) { if (error < 0) {
pr_info("Error registering secfs\n"); pr_info("Error registering secfs\n");
......
...@@ -341,7 +341,7 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name, ...@@ -341,7 +341,7 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
return 0; return 0;
} }
static void ima_reset_appraise_flags(struct inode *inode) static void ima_reset_appraise_flags(struct inode *inode, int digsig)
{ {
struct integrity_iint_cache *iint; struct integrity_iint_cache *iint;
...@@ -353,18 +353,22 @@ static void ima_reset_appraise_flags(struct inode *inode) ...@@ -353,18 +353,22 @@ static void ima_reset_appraise_flags(struct inode *inode)
return; return;
iint->flags &= ~IMA_DONE_MASK; iint->flags &= ~IMA_DONE_MASK;
if (digsig)
iint->flags |= IMA_DIGSIG;
return; return;
} }
int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len) const void *xattr_value, size_t xattr_value_len)
{ {
const struct evm_ima_xattr_data *xvalue = xattr_value;
int result; int result;
result = ima_protect_xattr(dentry, xattr_name, xattr_value, result = ima_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len); xattr_value_len);
if (result == 1) { if (result == 1) {
ima_reset_appraise_flags(dentry->d_inode); ima_reset_appraise_flags(dentry->d_inode,
(xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0);
result = 0; result = 0;
} }
return result; return result;
...@@ -376,7 +380,7 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name) ...@@ -376,7 +380,7 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
result = ima_protect_xattr(dentry, xattr_name, NULL, 0); result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
if (result == 1) { if (result == 1) {
ima_reset_appraise_flags(dentry->d_inode); ima_reset_appraise_flags(dentry->d_inode, 0);
result = 0; result = 0;
} }
return result; return result;
......
...@@ -27,6 +27,36 @@ ...@@ -27,6 +27,36 @@
static struct crypto_shash *ima_shash_tfm; static struct crypto_shash *ima_shash_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;
if (!(file->f_mode & FMODE_READ))
return -EBADF;
if (!file->f_op->read && !file->f_op->aio_read)
return -EINVAL;
old_fs = get_fs();
set_fs(get_ds());
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, &offset);
else
ret = do_sync_read(file, buf, count, &offset);
set_fs(old_fs);
return ret;
}
int ima_init_crypto(void) int ima_init_crypto(void)
{ {
long rc; long rc;
...@@ -104,7 +134,7 @@ static int ima_calc_file_hash_tfm(struct file *file, ...@@ -104,7 +134,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 = kernel_read(file, offset, rbuf, PAGE_SIZE); rbuf_len = ima_kernel_read(file, offset, rbuf, PAGE_SIZE);
if (rbuf_len < 0) { if (rbuf_len < 0) {
rc = rbuf_len; rc = rbuf_len;
break; break;
......
...@@ -81,7 +81,6 @@ static void ima_rdwr_violation_check(struct file *file) ...@@ -81,7 +81,6 @@ static void ima_rdwr_violation_check(struct file *file)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
fmode_t mode = file->f_mode; fmode_t mode = file->f_mode;
int must_measure;
bool send_tomtou = false, send_writers = false; bool send_tomtou = false, send_writers = false;
char *pathbuf = NULL; char *pathbuf = NULL;
const char *pathname; const char *pathname;
...@@ -92,18 +91,19 @@ static void ima_rdwr_violation_check(struct file *file) ...@@ -92,18 +91,19 @@ static void ima_rdwr_violation_check(struct file *file)
mutex_lock(&inode->i_mutex); /* file metadata: permissions, xattr */ mutex_lock(&inode->i_mutex); /* file metadata: permissions, xattr */
if (mode & FMODE_WRITE) { if (mode & FMODE_WRITE) {
if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) {
send_tomtou = true; struct integrity_iint_cache *iint;
goto out; iint = integrity_iint_find(inode);
/* IMA_MEASURE is set from reader side */
if (iint && (iint->flags & IMA_MEASURE))
send_tomtou = true;
}
} else {
if ((atomic_read(&inode->i_writecount) > 0) &&
ima_must_measure(inode, MAY_READ, FILE_CHECK))
send_writers = true;
} }
must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK);
if (!must_measure)
goto out;
if (atomic_read(&inode->i_writecount) > 0)
send_writers = true;
out:
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
if (!send_tomtou && !send_writers) if (!send_tomtou && !send_writers)
......
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