summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-06-13 07:39:39 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-13 07:39:39 -0700
commitaa569fa0ea32d1c24fb801a07d2d9174cbbdde23 (patch)
tree54c211a790684e0a93d6139634251152c703bbc2
parent6d87c225f5d82d29243dc124f1ffcbb0e14ec358 (diff)
parent0430e49b6e7c6b5e076be8fefdee089958c9adad (diff)
downloadlwn-aa569fa0ea32d1c24fb801a07d2d9174cbbdde23.tar.gz
lwn-aa569fa0ea32d1c24fb801a07d2d9174cbbdde23.zip
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
-rw-r--r--security/integrity/evm/Kconfig42
-rw-r--r--security/integrity/evm/evm.h5
-rw-r--r--security/integrity/evm/evm_crypto.c2
-rw-r--r--security/integrity/evm/evm_main.c29
-rw-r--r--security/integrity/ima/ima_appraise.c10
-rw-r--r--security/integrity/ima/ima_crypto.c32
-rw-r--r--security/integrity/ima/ima_main.c22
7 files changed, 114 insertions, 28 deletions
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
index d35b4915b00d..d606f3d12d6b 100644
--- a/security/integrity/evm/Kconfig
+++ b/security/integrity/evm/Kconfig
@@ -12,15 +12,41 @@ config EVM
If you are unsure how to answer this question, answer N.
-config EVM_HMAC_VERSION
- int "EVM HMAC version"
+if EVM
+
+menu "EVM options"
+
+config EVM_ATTR_FSUUID
+ bool "FSUUID (version 2)"
+ default y
depends on EVM
- default 2
help
- This options adds EVM HMAC version support.
- 1 - original version
- 2 - add per filesystem unique identifier (UUID) (default)
+ Include filesystem UUID for HMAC calculation.
+
+ 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
- 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
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index 37c88ddb3cfe..88bfe77efa1c 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -24,7 +24,10 @@
extern int evm_initialized;
extern char *evm_hmac;
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 *hash_tfm;
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index 6b540f1822e0..5e9687f02e1b 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -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.mode = inode->i_mode;
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,
sizeof(inode->i_sb->s_uuid));
crypto_shash_final(desc, digest);
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 6e0bd933b6a9..3bcb80df4d01 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -32,7 +32,7 @@ static char *integrity_status_msg[] = {
};
char *evm_hmac = "hmac(sha1)";
char *evm_hash = "sha1";
-int evm_hmac_version = CONFIG_EVM_HMAC_VERSION;
+int evm_hmac_attrs;
char *evm_config_xattrnames[] = {
#ifdef CONFIG_SECURITY_SELINUX
@@ -40,6 +40,11 @@ char *evm_config_xattrnames[] = {
#endif
#ifdef CONFIG_SECURITY_SMACK
XATTR_NAME_SMACK,
+#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
+ XATTR_NAME_SMACKEXEC,
+ XATTR_NAME_SMACKTRANSMUTE,
+ XATTR_NAME_SMACKMMAP,
+#endif
#endif
#ifdef CONFIG_IMA_APPRAISE
XATTR_NAME_IMA,
@@ -57,6 +62,14 @@ static int __init evm_set_fixmode(char *str)
}
__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)
{
struct inode *inode = dentry->d_inode;
@@ -287,12 +300,20 @@ out:
* @xattr_value: pointer to the new extended attribute value
* @xattr_value_len: pointer to the new extended attribute value length
*
- * Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that
- * the current value is valid.
+ * Before allowing the 'security.evm' protected xattr to be updated,
+ * 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,
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,
xattr_value_len);
}
@@ -432,6 +453,8 @@ static int __init init_evm(void)
{
int error;
+ evm_init_config();
+
error = evm_init_secfs();
if (error < 0) {
pr_info("Error registering secfs\n");
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 291bf0f3a46d..d3113d4aaa3c 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -341,7 +341,7 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
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;
@@ -353,18 +353,22 @@ static void ima_reset_appraise_flags(struct inode *inode)
return;
iint->flags &= ~IMA_DONE_MASK;
+ if (digsig)
+ iint->flags |= IMA_DIGSIG;
return;
}
int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
+ const struct evm_ima_xattr_data *xvalue = xattr_value;
int result;
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len);
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;
}
return result;
@@ -376,7 +380,7 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
if (result == 1) {
- ima_reset_appraise_flags(dentry->d_inode);
+ ima_reset_appraise_flags(dentry->d_inode, 0);
result = 0;
}
return result;
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 1bde8e627766..ccd0ac8fa9a0 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -27,6 +27,36 @@
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)
{
long rc;
@@ -104,7 +134,7 @@ static int ima_calc_file_hash_tfm(struct file *file,
while (offset < i_size) {
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) {
rc = rbuf_len;
break;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index dcc98cf542d8..09baa335ebc7 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -81,7 +81,6 @@ static void ima_rdwr_violation_check(struct file *file)
{
struct inode *inode = file_inode(file);
fmode_t mode = file->f_mode;
- int must_measure;
bool send_tomtou = false, send_writers = false;
char *pathbuf = NULL;
const char *pathname;
@@ -92,18 +91,19 @@ static void ima_rdwr_violation_check(struct file *file)
mutex_lock(&inode->i_mutex); /* file metadata: permissions, xattr */
if (mode & FMODE_WRITE) {
- if (atomic_read(&inode->i_readcount) && IS_IMA(inode))
- send_tomtou = true;
- goto out;
+ if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) {
+ struct integrity_iint_cache *iint;
+ 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);
if (!send_tomtou && !send_writers)