summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/capability.c24
-rw-r--r--security/device_cgroup.c19
-rw-r--r--security/integrity/Kconfig12
-rw-r--r--security/integrity/Makefile1
-rw-r--r--security/integrity/digsig.c11
-rw-r--r--security/integrity/digsig_asymmetric.c115
-rw-r--r--security/integrity/evm/Kconfig13
-rw-r--r--security/integrity/evm/evm.h2
-rw-r--r--security/integrity/evm/evm_crypto.c7
-rw-r--r--security/integrity/evm/evm_main.c10
-rw-r--r--security/integrity/evm/evm_secfs.c6
-rw-r--r--security/integrity/iint.c10
-rw-r--r--security/integrity/ima/ima.h21
-rw-r--r--security/integrity/ima/ima_api.c27
-rw-r--r--security/integrity/ima/ima_appraise.c92
-rw-r--r--security/integrity/ima/ima_crypto.c81
-rw-r--r--security/integrity/ima/ima_init.c3
-rw-r--r--security/integrity/ima/ima_main.c139
-rw-r--r--security/integrity/ima/ima_policy.c139
-rw-r--r--security/integrity/integrity.h62
-rw-r--r--security/keys/process_keys.c2
-rw-r--r--security/security.c28
-rw-r--r--security/selinux/hooks.c50
-rw-r--r--security/selinux/include/classmap.h2
-rw-r--r--security/selinux/include/objsec.h4
25 files changed, 658 insertions, 222 deletions
diff --git a/security/capability.c b/security/capability.c
index 0fe5a026aef8..579775088967 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -709,16 +709,31 @@ static void cap_req_classify_flow(const struct request_sock *req,
{
}
+static int cap_tun_dev_alloc_security(void **security)
+{
+ return 0;
+}
+
+static void cap_tun_dev_free_security(void *security)
+{
+}
+
static int cap_tun_dev_create(void)
{
return 0;
}
-static void cap_tun_dev_post_create(struct sock *sk)
+static int cap_tun_dev_attach_queue(void *security)
+{
+ return 0;
+}
+
+static int cap_tun_dev_attach(struct sock *sk, void *security)
{
+ return 0;
}
-static int cap_tun_dev_attach(struct sock *sk)
+static int cap_tun_dev_open(void *security)
{
return 0;
}
@@ -1050,8 +1065,11 @@ void __init security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, secmark_refcount_inc);
set_to_cap_if_null(ops, secmark_refcount_dec);
set_to_cap_if_null(ops, req_classify_flow);
+ set_to_cap_if_null(ops, tun_dev_alloc_security);
+ set_to_cap_if_null(ops, tun_dev_free_security);
set_to_cap_if_null(ops, tun_dev_create);
- set_to_cap_if_null(ops, tun_dev_post_create);
+ set_to_cap_if_null(ops, tun_dev_open);
+ set_to_cap_if_null(ops, tun_dev_attach_queue);
set_to_cap_if_null(ops, tun_dev_attach);
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 19ecc8de9e6b..1c69e38e3a2c 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -159,6 +159,16 @@ static void dev_exception_rm(struct dev_cgroup *dev_cgroup,
}
}
+static void __dev_exception_clean(struct dev_cgroup *dev_cgroup)
+{
+ struct dev_exception_item *ex, *tmp;
+
+ list_for_each_entry_safe(ex, tmp, &dev_cgroup->exceptions, list) {
+ list_del_rcu(&ex->list);
+ kfree_rcu(ex, rcu);
+ }
+}
+
/**
* dev_exception_clean - frees all entries of the exception list
* @dev_cgroup: dev_cgroup with the exception list to be cleaned
@@ -167,14 +177,9 @@ static void dev_exception_rm(struct dev_cgroup *dev_cgroup,
*/
static void dev_exception_clean(struct dev_cgroup *dev_cgroup)
{
- struct dev_exception_item *ex, *tmp;
-
lockdep_assert_held(&devcgroup_mutex);
- list_for_each_entry_safe(ex, tmp, &dev_cgroup->exceptions, list) {
- list_del_rcu(&ex->list);
- kfree_rcu(ex, rcu);
- }
+ __dev_exception_clean(dev_cgroup);
}
/*
@@ -215,7 +220,7 @@ static void devcgroup_css_free(struct cgroup *cgroup)
struct dev_cgroup *dev_cgroup;
dev_cgroup = cgroup_to_devcgroup(cgroup);
- dev_exception_clean(dev_cgroup);
+ __dev_exception_clean(dev_cgroup);
kfree(dev_cgroup);
}
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
index 5bd1cc1b4a54..4bb3a775a996 100644
--- a/security/integrity/Kconfig
+++ b/security/integrity/Kconfig
@@ -17,5 +17,17 @@ config INTEGRITY_SIGNATURE
This is useful for evm and module keyrings, when keys are
usually only added from initramfs.
+config INTEGRITY_ASYMMETRIC_KEYS
+ boolean "Enable asymmetric keys support"
+ depends on INTEGRITY_SIGNATURE
+ default n
+ select ASYMMETRIC_KEY_TYPE
+ select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select PUBLIC_KEY_ALGO_RSA
+ select X509_CERTIFICATE_PARSER
+ help
+ This option enables digital signature verification using
+ asymmetric keys.
+
source security/integrity/ima/Kconfig
source security/integrity/evm/Kconfig
diff --git a/security/integrity/Makefile b/security/integrity/Makefile
index d43799cc14f6..ebb6409b3fcb 100644
--- a/security/integrity/Makefile
+++ b/security/integrity/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_INTEGRITY) += integrity.o
obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
+obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
integrity-y := iint.o
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 2dc167d7cde9..0b759e17a131 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -44,5 +44,14 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
}
}
- return digsig_verify(keyring[id], sig, siglen, digest, digestlen);
+ switch (sig[0]) {
+ case 1:
+ return digsig_verify(keyring[id], sig, siglen,
+ digest, digestlen);
+ case 2:
+ return asymmetric_verify(keyring[id], sig, siglen,
+ digest, digestlen);
+ }
+
+ return -EOPNOTSUPP;
}
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
new file mode 100644
index 000000000000..b4754667659d
--- /dev/null
+++ b/security/integrity/digsig_asymmetric.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Author:
+ * Dmitry Kasatkin <dmitry.kasatkin@intel.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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/err.h>
+#include <linux/key-type.h>
+#include <crypto/public_key.h>
+#include <keys/asymmetric-type.h>
+
+#include "integrity.h"
+
+/*
+ * signature format v2 - for using with asymmetric keys
+ */
+struct signature_v2_hdr {
+ uint8_t version; /* signature format version */
+ uint8_t hash_algo; /* Digest algorithm [enum pkey_hash_algo] */
+ uint32_t keyid; /* IMA key identifier - not X509/PGP specific*/
+ uint16_t sig_size; /* signature size */
+ uint8_t sig[0]; /* signature payload */
+} __packed;
+
+/*
+ * Request an asymmetric key.
+ */
+static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
+{
+ struct key *key;
+ char name[12];
+
+ sprintf(name, "id:%x", keyid);
+
+ pr_debug("key search: \"%s\"\n", name);
+
+ if (keyring) {
+ /* search in specific keyring */
+ key_ref_t kref;
+ kref = keyring_search(make_key_ref(keyring, 1),
+ &key_type_asymmetric, name);
+ if (IS_ERR(kref))
+ key = ERR_CAST(kref);
+ else
+ key = key_ref_to_ptr(kref);
+ } else {
+ key = request_key(&key_type_asymmetric, name, NULL);
+ }
+
+ if (IS_ERR(key)) {
+ pr_warn("Request for unknown key '%s' err %ld\n",
+ name, PTR_ERR(key));
+ switch (PTR_ERR(key)) {
+ /* Hide some search errors */
+ case -EACCES:
+ case -ENOTDIR:
+ case -EAGAIN:
+ return ERR_PTR(-ENOKEY);
+ default:
+ return key;
+ }
+ }
+
+ pr_debug("%s() = 0 [%x]\n", __func__, key_serial(key));
+
+ return key;
+}
+
+int asymmetric_verify(struct key *keyring, const char *sig,
+ int siglen, const char *data, int datalen)
+{
+ struct public_key_signature pks;
+ struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
+ struct key *key;
+ int ret = -ENOMEM;
+
+ if (siglen <= sizeof(*hdr))
+ return -EBADMSG;
+
+ siglen -= sizeof(*hdr);
+
+ if (siglen != __be16_to_cpu(hdr->sig_size))
+ return -EBADMSG;
+
+ if (hdr->hash_algo >= PKEY_HASH__LAST)
+ return -ENOPKG;
+
+ key = request_asymmetric_key(keyring, __be32_to_cpu(hdr->keyid));
+ if (IS_ERR(key))
+ return PTR_ERR(key);
+
+ memset(&pks, 0, sizeof(pks));
+
+ pks.pkey_hash_algo = hdr->hash_algo;
+ pks.digest = (u8 *)data;
+ pks.digest_size = datalen;
+ pks.nr_mpi = 1;
+ pks.rsa.s = mpi_read_raw_data(hdr->sig, siglen);
+
+ if (pks.rsa.s)
+ ret = verify_signature(key, &pks);
+
+ mpi_free(pks.rsa.s);
+ key_put(key);
+ pr_debug("%s() = %d\n", __func__, ret);
+ return ret;
+}
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
index afbb59dd262d..fea9749c3756 100644
--- a/security/integrity/evm/Kconfig
+++ b/security/integrity/evm/Kconfig
@@ -11,3 +11,16 @@ config EVM
integrity attacks.
If you are unsure how to answer this question, answer N.
+
+config EVM_HMAC_VERSION
+ int "EVM HMAC version"
+ depends on EVM
+ default 2
+ help
+ This options adds EVM HMAC version support.
+ 1 - original version
+ 2 - add per filesystem unique identifier (UUID) (default)
+
+ WARNING: changing the HMAC calculation method or adding
+ additional info to the calculation, requires existing EVM
+ labeled file systems to be relabeled.
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index c885247ebcf7..30bd1ec0232e 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -24,6 +24,7 @@
extern int evm_initialized;
extern char *evm_hmac;
extern char *evm_hash;
+extern int evm_hmac_version;
extern struct crypto_shash *hmac_tfm;
extern struct crypto_shash *hash_tfm;
@@ -45,6 +46,5 @@ extern int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
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);
#endif
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index dfb26918699c..3bab89eb21d6 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -110,6 +110,9 @@ 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)
+ crypto_shash_update(desc, inode->i_sb->s_uuid,
+ sizeof(inode->i_sb->s_uuid));
crypto_shash_final(desc, digest);
}
@@ -205,9 +208,9 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM,
&xattr_data,
sizeof(xattr_data), 0);
- }
- else if (rc == -ENODATA)
+ } else if (rc == -ENODATA && inode->i_op->removexattr) {
rc = inode->i_op->removexattr(dentry, XATTR_NAME_EVM);
+ }
return rc;
}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index eb5484504f50..cdbde1762189 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -26,6 +26,7 @@ int evm_initialized;
char *evm_hmac = "hmac(sha1)";
char *evm_hash = "sha1";
+int evm_hmac_version = CONFIG_EVM_HMAC_VERSION;
char *evm_config_xattrnames[] = {
#ifdef CONFIG_SECURITY_SELINUX
@@ -427,15 +428,6 @@ err:
return error;
}
-static void __exit cleanup_evm(void)
-{
- evm_cleanup_secfs();
- if (hmac_tfm)
- crypto_free_shash(hmac_tfm);
- if (hash_tfm)
- crypto_free_shash(hash_tfm);
-}
-
/*
* evm_display_config - list the EVM protected security extended attributes
*/
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
index ac7629950578..30f670ad6ac3 100644
--- a/security/integrity/evm/evm_secfs.c
+++ b/security/integrity/evm/evm_secfs.c
@@ -100,9 +100,3 @@ int __init evm_init_secfs(void)
error = -EFAULT;
return error;
}
-
-void __exit evm_cleanup_secfs(void)
-{
- if (evm_init_tpm)
- securityfs_remove(evm_init_tpm);
-}
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index d82a5a13d855..74522dbd10a6 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -72,7 +72,10 @@ static void iint_free(struct integrity_iint_cache *iint)
{
iint->version = 0;
iint->flags = 0UL;
- iint->ima_status = INTEGRITY_UNKNOWN;
+ iint->ima_file_status = INTEGRITY_UNKNOWN;
+ iint->ima_mmap_status = INTEGRITY_UNKNOWN;
+ iint->ima_bprm_status = INTEGRITY_UNKNOWN;
+ iint->ima_module_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
kmem_cache_free(iint_cache, iint);
}
@@ -149,7 +152,10 @@ static void init_once(void *foo)
memset(iint, 0, sizeof *iint);
iint->version = 0;
iint->flags = 0UL;
- iint->ima_status = INTEGRITY_UNKNOWN;
+ iint->ima_file_status = INTEGRITY_UNKNOWN;
+ iint->ima_mmap_status = INTEGRITY_UNKNOWN;
+ iint->ima_bprm_status = INTEGRITY_UNKNOWN;
+ iint->ima_module_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
}
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 079a85dc37b2..a41c9c18e5e0 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -84,11 +84,12 @@ void ima_fs_cleanup(void);
int ima_inode_alloc(struct inode *inode);
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
const char *op, struct inode *inode);
-int ima_calc_hash(struct file *file, char *digest);
-int ima_calc_template_hash(int template_len, void *template, char *digest);
+int ima_calc_file_hash(struct file *file, char *digest);
+int ima_calc_buffer_hash(const void *data, int len, char *digest);
int ima_calc_boot_aggregate(char *digest);
void ima_add_violation(struct inode *inode, const unsigned char *filename,
const char *op, const char *cause);
+int ima_init_crypto(void);
/*
* used to protect h_table and sha_table
@@ -119,6 +120,7 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
int ima_store_template(struct ima_template_entry *entry, int violation,
struct inode *inode);
void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
+const char *ima_d_path(struct path *path, char **pathbuf);
/* rbtree tree calls to lookup, insert, delete
* integrity data associated with an inode.
@@ -127,7 +129,7 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
/* IMA policy related functions */
-enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, MODULE_CHECK, POST_SETATTR };
+enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, POST_SETATTR };
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
int flags);
@@ -142,13 +144,16 @@ void ima_delete_rules(void);
#define IMA_APPRAISE_MODULES 0x04
#ifdef CONFIG_IMA_APPRAISE
-int ima_appraise_measurement(struct integrity_iint_cache *iint,
+int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename);
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
+enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
+ int func);
#else
-static inline int ima_appraise_measurement(struct integrity_iint_cache *iint,
+static inline int ima_appraise_measurement(int func,
+ struct integrity_iint_cache *iint,
struct file *file,
const unsigned char *filename)
{
@@ -165,6 +170,12 @@ static inline void ima_update_xattr(struct integrity_iint_cache *iint,
struct file *file)
{
}
+
+static inline enum integrity_status ima_get_cache_status(struct integrity_iint_cache
+ *iint, int func)
+{
+ return INTEGRITY_UNKNOWN;
+}
#endif
/* LSM based policy rules require audit */
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 27cb9eb42cc8..1c03e8f1e0e1 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -50,8 +50,8 @@ int ima_store_template(struct ima_template_entry *entry,
entry->template_len = sizeof(entry->template);
if (!violation) {
- result = ima_calc_template_hash(entry->template_len,
- &entry->template,
+ result = ima_calc_buffer_hash(&entry->template,
+ entry->template_len,
entry->digest);
if (result < 0) {
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
@@ -100,12 +100,12 @@ err_out:
* ima_get_action - appraise & measure decision based on policy.
* @inode: pointer to inode to measure
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
- * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP, MODULE_CHECK)
+ * @function: calling function (FILE_CHECK, BPRM_CHECK, MMAP_CHECK, MODULE_CHECK)
*
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
* subj,obj, and type: are LSM specific.
- * func: FILE_CHECK | BPRM_CHECK | FILE_MMAP | MODULE_CHECK
+ * func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK
* mask: contains the permission mask
* fsmagic: hex value
*
@@ -148,7 +148,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
u64 i_version = file_inode(file)->i_version;
iint->ima_xattr.type = IMA_XATTR_DIGEST;
- result = ima_calc_hash(file, iint->ima_xattr.digest);
+ result = ima_calc_file_hash(file, iint->ima_xattr.digest);
if (!result) {
iint->version = i_version;
iint->flags |= IMA_COLLECTED;
@@ -237,3 +237,20 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
iint->flags |= IMA_AUDITED;
}
+
+const char *ima_d_path(struct path *path, char **pathbuf)
+{
+ char *pathname = NULL;
+
+ /* We will allow 11 spaces for ' (deleted)' to be appended */
+ *pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
+ if (*pathbuf) {
+ pathname = d_path(path, *pathbuf, PATH_MAX + 11);
+ if (IS_ERR(pathname)) {
+ kfree(*pathbuf);
+ *pathbuf = NULL;
+ pathname = NULL;
+ }
+ }
+ return pathname;
+}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index bdc8ba1d1d27..2d4becab8918 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -42,12 +42,69 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
return ima_match_policy(inode, func, mask, IMA_APPRAISE);
}
-static void ima_fix_xattr(struct dentry *dentry,
+static int ima_fix_xattr(struct dentry *dentry,
struct integrity_iint_cache *iint)
{
iint->ima_xattr.type = IMA_XATTR_DIGEST;
- __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, (u8 *)&iint->ima_xattr,
- sizeof iint->ima_xattr, 0);
+ return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
+ (u8 *)&iint->ima_xattr,
+ sizeof(iint->ima_xattr), 0);
+}
+
+/* Return specific func appraised cached result */
+enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
+ int func)
+{
+ switch(func) {
+ case MMAP_CHECK:
+ return iint->ima_mmap_status;
+ case BPRM_CHECK:
+ return iint->ima_bprm_status;
+ case MODULE_CHECK:
+ return iint->ima_module_status;
+ case FILE_CHECK:
+ default:
+ return iint->ima_file_status;
+ }
+}
+
+static void ima_set_cache_status(struct integrity_iint_cache *iint,
+ int func, enum integrity_status status)
+{
+ switch(func) {
+ case MMAP_CHECK:
+ iint->ima_mmap_status = status;
+ break;
+ case BPRM_CHECK:
+ iint->ima_bprm_status = status;
+ break;
+ case MODULE_CHECK:
+ iint->ima_module_status = status;
+ break;
+ case FILE_CHECK:
+ default:
+ iint->ima_file_status = status;
+ break;
+ }
+}
+
+static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
+{
+ switch(func) {
+ case MMAP_CHECK:
+ iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);
+ break;
+ case BPRM_CHECK:
+ iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED);
+ break;
+ case MODULE_CHECK:
+ iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED);
+ break;
+ case FILE_CHECK:
+ default:
+ iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
+ break;
+ }
}
/*
@@ -58,7 +115,7 @@ static void ima_fix_xattr(struct dentry *dentry,
*
* Return 0 on success, error code otherwise
*/
-int ima_appraise_measurement(struct integrity_iint_cache *iint,
+int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename)
{
struct dentry *dentry = file->f_dentry;
@@ -74,9 +131,6 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint,
if (!inode->i_op->getxattr)
return INTEGRITY_UNKNOWN;
- if (iint->flags & IMA_APPRAISED)
- return iint->ima_status;
-
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value,
0, GFP_NOFS);
if (rc <= 0) {
@@ -98,19 +152,18 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint,
cause = "invalid-HMAC";
goto out;
}
-
switch (xattr_value->type) {
case IMA_XATTR_DIGEST:
+ if (iint->flags & IMA_DIGSIG_REQUIRED) {
+ cause = "IMA signature required";
+ status = INTEGRITY_FAIL;
+ break;
+ }
rc = memcmp(xattr_value->digest, iint->ima_xattr.digest,
IMA_DIGEST_SIZE);
if (rc) {
cause = "invalid-hash";
status = INTEGRITY_FAIL;
- print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE,
- xattr_value, sizeof(*xattr_value));
- print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE,
- (u8 *)&iint->ima_xattr,
- sizeof iint->ima_xattr);
break;
}
status = INTEGRITY_PASS;
@@ -141,15 +194,15 @@ out:
if ((ima_appraise & IMA_APPRAISE_FIX) &&
(!xattr_value ||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
- ima_fix_xattr(dentry, iint);
- status = INTEGRITY_PASS;
+ if (!ima_fix_xattr(dentry, iint))
+ status = INTEGRITY_PASS;
}
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
op, cause, rc, 0);
} else {
- iint->flags |= IMA_APPRAISED;
+ ima_cache_flags(iint, func);
}
- iint->ima_status = status;
+ ima_set_cache_status(iint, func, status);
kfree(xattr_value);
return status;
}
@@ -195,10 +248,11 @@ void ima_inode_post_setattr(struct dentry *dentry)
must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
iint = integrity_iint_find(inode);
if (iint) {
+ iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
+ IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
+ IMA_ACTION_FLAGS);
if (must_appraise)
iint->flags |= IMA_APPRAISE;
- else
- iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED);
}
if (!must_appraise)
rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 81dcaa26401e..a02e0791cf15 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -19,38 +19,41 @@
#include <linux/scatterlist.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <crypto/hash.h>
#include "ima.h"
-static int init_desc(struct hash_desc *desc)
+static struct crypto_shash *ima_shash_tfm;
+
+int ima_init_crypto(void)
{
- int rc;
+ long rc;
- desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(desc->tfm)) {
- pr_info("IMA: failed to load %s transform: %ld\n",
- ima_hash, PTR_ERR(desc->tfm));
- rc = PTR_ERR(desc->tfm);
+ ima_shash_tfm = crypto_alloc_shash(ima_hash, 0, 0);
+ if (IS_ERR(ima_shash_tfm)) {
+ rc = PTR_ERR(ima_shash_tfm);
+ pr_err("Can not allocate %s (reason: %ld)\n", ima_hash, rc);
return rc;
}
- desc->flags = 0;
- rc = crypto_hash_init(desc);
- if (rc)
- crypto_free_hash(desc->tfm);
- return rc;
+ return 0;
}
/*
* Calculate the MD5/SHA1 file digest
*/
-int ima_calc_hash(struct file *file, char *digest)
+int ima_calc_file_hash(struct file *file, char *digest)
{
- struct hash_desc desc;
- struct scatterlist sg[1];
loff_t i_size, offset = 0;
char *rbuf;
int rc, read = 0;
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(ima_shash_tfm)];
+ } desc;
- rc = init_desc(&desc);
+ desc.shash.tfm = ima_shash_tfm;
+ desc.shash.flags = 0;
+
+ rc = crypto_shash_init(&desc.shash);
if (rc != 0)
return rc;
@@ -75,41 +78,34 @@ int ima_calc_hash(struct file *file, char *digest)
if (rbuf_len == 0)
break;
offset += rbuf_len;
- sg_init_one(sg, rbuf, rbuf_len);
- rc = crypto_hash_update(&desc, sg, rbuf_len);
+ rc = crypto_shash_update(&desc.shash, rbuf, rbuf_len);
if (rc)
break;
}
kfree(rbuf);
if (!rc)
- rc = crypto_hash_final(&desc, digest);
+ rc = crypto_shash_final(&desc.shash, digest);
if (read)
file->f_mode &= ~FMODE_READ;
out:
- crypto_free_hash(desc.tfm);
return rc;
}
/*
- * Calculate the hash of a given template
+ * Calculate the hash of a given buffer
*/
-int ima_calc_template_hash(int template_len, void *template, char *digest)
+int ima_calc_buffer_hash(const void *data, int len, char *digest)
{
- struct hash_desc desc;
- struct scatterlist sg[1];
- int rc;
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(ima_shash_tfm)];
+ } desc;
- rc = init_desc(&desc);
- if (rc != 0)
- return rc;
+ desc.shash.tfm = ima_shash_tfm;
+ desc.shash.flags = 0;
- sg_init_one(sg, template, template_len);
- rc = crypto_hash_update(&desc, sg, template_len);
- if (!rc)
- rc = crypto_hash_final(&desc, digest);
- crypto_free_hash(desc.tfm);
- return rc;
+ return crypto_shash_digest(&desc.shash, data, len, digest);
}
static void __init ima_pcrread(int idx, u8 *pcr)
@@ -126,12 +122,17 @@ static void __init ima_pcrread(int idx, u8 *pcr)
*/
int __init ima_calc_boot_aggregate(char *digest)
{
- struct hash_desc desc;
- struct scatterlist sg;
u8 pcr_i[IMA_DIGEST_SIZE];
int rc, i;
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(ima_shash_tfm)];
+ } desc;
+
+ desc.shash.tfm = ima_shash_tfm;
+ desc.shash.flags = 0;
- rc = init_desc(&desc);
+ rc = crypto_shash_init(&desc.shash);
if (rc != 0)
return rc;
@@ -139,11 +140,9 @@ int __init ima_calc_boot_aggregate(char *digest)
for (i = TPM_PCR0; i < TPM_PCR8; i++) {
ima_pcrread(i, pcr_i);
/* now accumulate with current aggregate */
- sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE);
- rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
+ rc = crypto_shash_update(&desc.shash, pcr_i, IMA_DIGEST_SIZE);
}
if (!rc)
- crypto_hash_final(&desc, digest);
- crypto_free_hash(desc.tfm);
+ crypto_shash_final(&desc.shash, digest);
return rc;
}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index b5dfd534f13d..162ea723db3d 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -85,6 +85,9 @@ int __init ima_init(void)
if (!ima_used_chip)
pr_info("IMA: No TPM chip found, activating TPM-bypass!\n");
+ rc = ima_init_crypto();
+ if (rc)
+ return rc;
ima_add_boot_aggregate(); /* boot aggregate must be first entry */
ima_init_policy();
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index e7a147f7d371..3b3b7e6bf8da 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -61,7 +61,8 @@ static void ima_rdwr_violation_check(struct file *file)
fmode_t mode = file->f_mode;
int must_measure;
bool send_tomtou = false, send_writers = false;
- unsigned char *pathname = NULL, *pathbuf = NULL;
+ char *pathbuf = NULL;
+ const char *pathname;
if (!S_ISREG(inode->i_mode) || !ima_initialized)
return;
@@ -86,22 +87,15 @@ out:
if (!send_tomtou && !send_writers)
return;
- /* We will allow 11 spaces for ' (deleted)' to be appended */
- pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
- if (pathbuf) {
- pathname = d_path(&file->f_path, pathbuf, PATH_MAX + 11);
- if (IS_ERR(pathname))
- pathname = NULL;
- else if (strlen(pathname) > IMA_EVENT_NAME_LEN_MAX)
- pathname = NULL;
- }
+ pathname = ima_d_path(&file->f_path, &pathbuf);
+ if (!pathname || strlen(pathname) > IMA_EVENT_NAME_LEN_MAX)
+ pathname = dentry->d_name.name;
+
if (send_tomtou)
- ima_add_violation(inode,
- !pathname ? dentry->d_name.name : pathname,
+ ima_add_violation(inode, pathname,
"invalid_pcr", "ToMToU");
if (send_writers)
- ima_add_violation(inode,
- !pathname ? dentry->d_name.name : pathname,
+ ima_add_violation(inode, pathname,
"invalid_pcr", "open_writers");
kfree(pathbuf);
}
@@ -145,25 +139,31 @@ void ima_file_free(struct file *file)
ima_check_last_writer(iint, inode, file);
}
-static int process_measurement(struct file *file, const unsigned char *filename,
+static int process_measurement(struct file *file, const char *filename,
int mask, int function)
{
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint;
- unsigned char *pathname = NULL, *pathbuf = NULL;
- int rc = -ENOMEM, action, must_appraise;
+ char *pathbuf = NULL;
+ const char *pathname = NULL;
+ int rc = -ENOMEM, action, must_appraise, _func;
if (!ima_initialized || !S_ISREG(inode->i_mode))
return 0;
- /* Determine if in appraise/audit/measurement policy,
- * returns IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT bitmask. */
+ /* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
+ * bitmask based on the appraise/audit/measurement policy.
+ * Included is the appraise submask.
+ */
action = ima_get_action(inode, mask, function);
if (!action)
return 0;
must_appraise = action & IMA_APPRAISE;
+ /* Is the appraise rule hook specific? */
+ _func = (action & IMA_FILE_APPRAISE) ? FILE_CHECK : function;
+
mutex_lock(&inode->i_mutex);
iint = integrity_inode_get(inode);
@@ -171,44 +171,45 @@ static int process_measurement(struct file *file, const unsigned char *filename,
goto out;
/* Determine if already appraised/measured based on bitmask
- * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED,
- * IMA_AUDIT, IMA_AUDITED) */
+ * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
+ * IMA_AUDIT, IMA_AUDITED)
+ */
iint->flags |= action;
+ action &= IMA_DO_MASK;
action &= ~((iint->flags & IMA_DONE_MASK) >> 1);
/* Nothing to do, just return existing appraised status */
if (!action) {
- if (iint->flags & IMA_APPRAISED)
- rc = iint->ima_status;
- goto out;
+ if (must_appraise)
+ rc = ima_get_cache_status(iint, _func);
+ goto out_digsig;
}
rc = ima_collect_measurement(iint, file);
if (rc != 0)
- goto out;
+ goto out_digsig;
+
+ if (function != BPRM_CHECK)
+ pathname = ima_d_path(&file->f_path, &pathbuf);
+
+ if (!pathname)
+ pathname = filename;
- if (function != BPRM_CHECK) {
- /* We will allow 11 spaces for ' (deleted)' to be appended */
- pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
- if (pathbuf) {
- pathname =
- d_path(&file->f_path, pathbuf, PATH_MAX + 11);
- if (IS_ERR(pathname))
- pathname = NULL;
- }
- }
if (action & IMA_MEASURE)
- ima_store_measurement(iint, file,
- !pathname ? filename : pathname);
- if (action & IMA_APPRAISE)
- rc = ima_appraise_measurement(iint, file,
- !pathname ? filename : pathname);
+ ima_store_measurement(iint, file, pathname);
+ if (action & IMA_APPRAISE_SUBMASK)
+ rc = ima_appraise_measurement(_func, iint, file, pathname);
if (action & IMA_AUDIT)
- ima_audit_measurement(iint, !pathname ? filename : pathname);
+ ima_audit_measurement(iint, pathname);
kfree(pathbuf);
+out_digsig:
+ if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG))
+ rc = -EACCES;
out:
mutex_unlock(&inode->i_mutex);
- return (rc && must_appraise) ? -EACCES : 0;
+ if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
+ return -EACCES;
+ return 0;
}
/**
@@ -219,19 +220,15 @@ out:
* Measure files being mmapped executable based on the ima_must_measure()
* policy decision.
*
- * Return 0 on success, an error code on failure.
- * (Based on the results of appraise_measurement().)
+ * On success return 0. On integrity appraisal error, assuming the file
+ * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
int ima_file_mmap(struct file *file, unsigned long prot)
{
- int rc = 0;
-
- if (!file)
- return 0;
- if (prot & PROT_EXEC)
- rc = process_measurement(file, file->f_dentry->d_name.name,
- MAY_EXEC, FILE_MMAP);
- return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
+ if (file && (prot & PROT_EXEC))
+ return process_measurement(file, file->f_dentry->d_name.name,
+ MAY_EXEC, MMAP_CHECK);
+ return 0;
}
/**
@@ -244,18 +241,15 @@ int ima_file_mmap(struct file *file, unsigned long prot)
* So we can be certain that what we verify and measure here is actually
* what is being executed.
*
- * Return 0 on success, an error code on failure.
- * (Based on the results of appraise_measurement().)
+ * On success return 0. On integrity appraisal error, assuming the file
+ * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
int ima_bprm_check(struct linux_binprm *bprm)
{
- int rc;
-
- rc = process_measurement(bprm->file,
+ return process_measurement(bprm->file,
(strcmp(bprm->filename, bprm->interp) == 0) ?
bprm->filename : bprm->interp,
MAY_EXEC, BPRM_CHECK);
- return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
}
/**
@@ -265,18 +259,15 @@ int ima_bprm_check(struct linux_binprm *bprm)
*
* Measure files based on the ima_must_measure() policy decision.
*
- * Always return 0 and audit dentry_open failures.
- * (Return code will be based upon measurement appraisal.)
+ * On success return 0. On integrity appraisal error, assuming the file
+ * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
int ima_file_check(struct file *file, int mask)
{
- int rc;
-
ima_rdwr_violation_check(file);
- rc = process_measurement(file, file->f_dentry->d_name.name,
+ return process_measurement(file, file->f_dentry->d_name.name,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
FILE_CHECK);
- return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
}
EXPORT_SYMBOL_GPL(ima_file_check);
@@ -286,23 +277,21 @@ EXPORT_SYMBOL_GPL(ima_file_check);
*
* Measure/appraise kernel modules based on policy.
*
- * Always return 0 and audit dentry_open failures.
- * Return code is based upon measurement appraisal.
+ * On success return 0. On integrity appraisal error, assuming the file
+ * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
int ima_module_check(struct file *file)
{
- int rc = 0;
-
if (!file) {
- if (ima_appraise & IMA_APPRAISE_MODULES) {
#ifndef CONFIG_MODULE_SIG_FORCE
- rc = -EACCES; /* INTEGRITY_UNKNOWN */
+ if ((ima_appraise & IMA_APPRAISE_MODULES) &&
+ (ima_appraise & IMA_APPRAISE_ENFORCE))
+ return -EACCES; /* INTEGRITY_UNKNOWN */
#endif
- }
- } else
- rc = process_measurement(file, file->f_dentry->d_name.name,
- MAY_EXEC, MODULE_CHECK);
- return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
+ return 0; /* We rely on module signature checking */
+ }
+ return process_measurement(file, file->f_dentry->d_name.name,
+ MAY_EXEC, MODULE_CHECK);
}
static int __init init_ima(void)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 479fca940bb5..399433ad614e 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -16,6 +16,7 @@
#include <linux/magic.h>
#include <linux/parser.h>
#include <linux/slab.h>
+#include <linux/genhd.h>
#include "ima.h"
@@ -25,6 +26,7 @@
#define IMA_FSMAGIC 0x0004
#define IMA_UID 0x0008
#define IMA_FOWNER 0x0010
+#define IMA_FSUUID 0x0020
#define UNKNOWN 0
#define MEASURE 0x0001 /* same as IMA_MEASURE */
@@ -45,10 +47,12 @@ struct ima_rule_entry {
enum ima_hooks func;
int mask;
unsigned long fsmagic;
+ u8 fsuuid[16];
kuid_t uid;
kuid_t fowner;
struct {
void *rule; /* LSM file metadata specific */
+ void *args_p; /* audit value */
int type; /* audit type */
} lsm[MAX_LSM_RULES];
};
@@ -74,7 +78,7 @@ static struct ima_rule_entry default_rules[] = {
{.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
{.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC},
- {.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
+ {.action = MEASURE,.func = MMAP_CHECK,.mask = MAY_EXEC,
.flags = IMA_FUNC | IMA_MASK},
{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
.flags = IMA_FUNC | IMA_MASK},
@@ -119,6 +123,35 @@ static int __init default_appraise_policy_setup(char *str)
}
__setup("ima_appraise_tcb", default_appraise_policy_setup);
+/*
+ * Although the IMA policy does not change, the LSM policy can be
+ * reloaded, leaving the IMA LSM based rules referring to the old,
+ * stale LSM policy.
+ *
+ * Update the IMA LSM based rules to reflect the reloaded LSM policy.
+ * We assume the rules still exist; and BUG_ON() if they don't.
+ */
+static void ima_lsm_update_rules(void)
+{
+ struct ima_rule_entry *entry, *tmp;
+ int result;
+ int i;
+
+ mutex_lock(&ima_rules_mutex);
+ list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
+ for (i = 0; i < MAX_LSM_RULES; i++) {
+ if (!entry->lsm[i].rule)
+ continue;
+ result = security_filter_rule_init(entry->lsm[i].type,
+ Audit_equal,
+ entry->lsm[i].args_p,
+ &entry->lsm[i].rule);
+ BUG_ON(!entry->lsm[i].rule);
+ }
+ }
+ mutex_unlock(&ima_rules_mutex);
+}
+
/**
* ima_match_rules - determine whether an inode matches the measure rule.
* @rule: a pointer to a rule
@@ -142,6 +175,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
if ((rule->flags & IMA_FSMAGIC)
&& rule->fsmagic != inode->i_sb->s_magic)
return false;
+ if ((rule->flags & IMA_FSUUID) &&
+ memcmp(rule->fsuuid, inode->i_sb->s_uuid, sizeof(rule->fsuuid)))
+ return false;
if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid))
return false;
if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid))
@@ -149,10 +185,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
for (i = 0; i < MAX_LSM_RULES; i++) {
int rc = 0;
u32 osid, sid;
+ int retried = 0;
if (!rule->lsm[i].rule)
continue;
-
+retry:
switch (i) {
case LSM_OBJ_USER:
case LSM_OBJ_ROLE:
@@ -176,12 +213,39 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
default:
break;
}
+ if ((rc < 0) && (!retried)) {
+ retried = 1;
+ ima_lsm_update_rules();
+ goto retry;
+ }
if (!rc)
return false;
}
return true;
}
+/*
+ * In addition to knowing that we need to appraise the file in general,
+ * we need to differentiate between calling hooks, for hook specific rules.
+ */
+static int get_subaction(struct ima_rule_entry *rule, int func)
+{
+ if (!(rule->flags & IMA_FUNC))
+ return IMA_FILE_APPRAISE;
+
+ switch(func) {
+ case MMAP_CHECK:
+ return IMA_MMAP_APPRAISE;
+ case BPRM_CHECK:
+ return IMA_BPRM_APPRAISE;
+ case MODULE_CHECK:
+ return IMA_MODULE_APPRAISE;
+ case FILE_CHECK:
+ default:
+ return IMA_FILE_APPRAISE;
+ }
+}
+
/**
* ima_match_policy - decision based on LSM and other conditions
* @inode: pointer to an inode for which the policy decision is being made
@@ -209,7 +273,12 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
if (!ima_match_rules(entry, inode, func, mask))
continue;
+ action |= entry->flags & IMA_ACTION_FLAGS;
+
action |= entry->action & IMA_DO_MASK;
+ if (entry->action & IMA_APPRAISE)
+ action |= get_subaction(entry, func);
+
if (entry->action & IMA_DO_MASK)
actmask &= ~(entry->action | entry->action << 1);
else
@@ -282,7 +351,8 @@ enum {
Opt_audit,
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_uid, Opt_fowner
+ Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner,
+ Opt_appraise_type, Opt_fsuuid
};
static match_table_t policy_tokens = {
@@ -300,25 +370,35 @@ static match_table_t policy_tokens = {
{Opt_func, "func=%s"},
{Opt_mask, "mask=%s"},
{Opt_fsmagic, "fsmagic=%s"},
+ {Opt_fsuuid, "fsuuid=%s"},
{Opt_uid, "uid=%s"},
{Opt_fowner, "fowner=%s"},
+ {Opt_appraise_type, "appraise_type=%s"},
{Opt_err, NULL}
};
static int ima_lsm_rule_init(struct ima_rule_entry *entry,
- char *args, int lsm_rule, int audit_type)
+ substring_t *args, int lsm_rule, int audit_type)
{
int result;
if (entry->lsm[lsm_rule].rule)
return -EINVAL;
+ entry->lsm[lsm_rule].args_p = match_strdup(args);
+ if (!entry->lsm[lsm_rule].args_p)
+ return -ENOMEM;
+
entry->lsm[lsm_rule].type = audit_type;
result = security_filter_rule_init(entry->lsm[lsm_rule].type,
- Audit_equal, args,
+ Audit_equal,
+ entry->lsm[lsm_rule].args_p,
&entry->lsm[lsm_rule].rule);
- if (!entry->lsm[lsm_rule].rule)
+ if (!entry->lsm[lsm_rule].rule) {
+ kfree(entry->lsm[lsm_rule].args_p);
return -EINVAL;
+ }
+
return result;
}
@@ -404,8 +484,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->func = FILE_CHECK;
else if (strcmp(args[0].from, "MODULE_CHECK") == 0)
entry->func = MODULE_CHECK;
- else if (strcmp(args[0].from, "FILE_MMAP") == 0)
- entry->func = FILE_MMAP;
+ else if ((strcmp(args[0].from, "FILE_MMAP") == 0)
+ || (strcmp(args[0].from, "MMAP_CHECK") == 0))
+ entry->func = MMAP_CHECK;
else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
entry->func = BPRM_CHECK;
else
@@ -445,6 +526,20 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
if (!result)
entry->flags |= IMA_FSMAGIC;
break;
+ case Opt_fsuuid:
+ ima_log_string(ab, "fsuuid", args[0].from);
+
+ if (memchr_inv(entry->fsuuid, 0x00,
+ sizeof(entry->fsuuid))) {
+ result = -EINVAL;
+ break;
+ }
+
+ result = blk_part_pack_uuid(args[0].from,
+ entry->fsuuid);
+ if (!result)
+ entry->flags |= IMA_FSUUID;
+ break;
case Opt_uid:
ima_log_string(ab, "uid", args[0].from);
@@ -481,40 +576,52 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
break;
case Opt_obj_user:
ima_log_string(ab, "obj_user", args[0].from);
- result = ima_lsm_rule_init(entry, args[0].from,
+ result = ima_lsm_rule_init(entry, args,
LSM_OBJ_USER,
AUDIT_OBJ_USER);
break;
case Opt_obj_role:
ima_log_string(ab, "obj_role", args[0].from);
- result = ima_lsm_rule_init(entry, args[0].from,
+ result = ima_lsm_rule_init(entry, args,
LSM_OBJ_ROLE,
AUDIT_OBJ_ROLE);
break;
case Opt_obj_type:
ima_log_string(ab, "obj_type", args[0].from);
- result = ima_lsm_rule_init(entry, args[0].from,
+ result = ima_lsm_rule_init(entry, args,
LSM_OBJ_TYPE,
AUDIT_OBJ_TYPE);
break;
case Opt_subj_user:
ima_log_string(ab, "subj_user", args[0].from);
- result = ima_lsm_rule_init(entry, args[0].from,
+ result = ima_lsm_rule_init(entry, args,
LSM_SUBJ_USER,
AUDIT_SUBJ_USER);
break;
case Opt_subj_role:
ima_log_string(ab, "subj_role", args[0].from);
- result = ima_lsm_rule_init(entry, args[0].from,
+ result = ima_lsm_rule_init(entry, args,
LSM_SUBJ_ROLE,
AUDIT_SUBJ_ROLE);
break;
case Opt_subj_type:
ima_log_string(ab, "subj_type", args[0].from);
- result = ima_lsm_rule_init(entry, args[0].from,
+ result = ima_lsm_rule_init(entry, args,
LSM_SUBJ_TYPE,
AUDIT_SUBJ_TYPE);
break;
+ case Opt_appraise_type:
+ if (entry->action != APPRAISE) {
+ result = -EINVAL;
+ break;
+ }
+
+ ima_log_string(ab, "appraise_type", args[0].from);
+ if ((strcmp(args[0].from, "imasig")) == 0)
+ entry->flags |= IMA_DIGSIG_REQUIRED;
+ else
+ result = -EINVAL;
+ break;
case Opt_err:
ima_log_string(ab, "UNKNOWN", p);
result = -EINVAL;
@@ -590,9 +697,13 @@ ssize_t ima_parse_add_rule(char *rule)
void ima_delete_rules(void)
{
struct ima_rule_entry *entry, *tmp;
+ int i;
mutex_lock(&ima_rules_mutex);
list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
+ for (i = 0; i < MAX_LSM_RULES; i++)
+ kfree(entry->lsm[i].args_p);
+
list_del(&entry->list);
kfree(entry);
}
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index e9db763a875e..84c37c4db914 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -14,23 +14,41 @@
#include <linux/types.h>
#include <linux/integrity.h>
#include <crypto/sha.h>
+#include <linux/key.h>
/* iint action cache flags */
-#define IMA_MEASURE 0x0001
-#define IMA_MEASURED 0x0002
-#define IMA_APPRAISE 0x0004
-#define IMA_APPRAISED 0x0008
-/*#define IMA_COLLECT 0x0010 do not use this flag */
-#define IMA_COLLECTED 0x0020
-#define IMA_AUDIT 0x0040
-#define IMA_AUDITED 0x0080
+#define IMA_MEASURE 0x00000001
+#define IMA_MEASURED 0x00000002
+#define IMA_APPRAISE 0x00000004
+#define IMA_APPRAISED 0x00000008
+/*#define IMA_COLLECT 0x00000010 do not use this flag */
+#define IMA_COLLECTED 0x00000020
+#define IMA_AUDIT 0x00000040
+#define IMA_AUDITED 0x00000080
/* iint cache flags */
-#define IMA_DIGSIG 0x0100
+#define IMA_ACTION_FLAGS 0xff000000
+#define IMA_DIGSIG 0x01000000
+#define IMA_DIGSIG_REQUIRED 0x02000000
-#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT)
-#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED \
- | IMA_COLLECTED)
+#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
+ IMA_APPRAISE_SUBMASK)
+#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
+ IMA_COLLECTED | IMA_APPRAISED_SUBMASK)
+
+/* iint subaction appraise cache flags */
+#define IMA_FILE_APPRAISE 0x00000100
+#define IMA_FILE_APPRAISED 0x00000200
+#define IMA_MMAP_APPRAISE 0x00000400
+#define IMA_MMAP_APPRAISED 0x00000800
+#define IMA_BPRM_APPRAISE 0x00001000
+#define IMA_BPRM_APPRAISED 0x00002000
+#define IMA_MODULE_APPRAISE 0x00004000
+#define IMA_MODULE_APPRAISED 0x00008000
+#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
+ IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE)
+#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
+ IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED)
enum evm_ima_xattr_type {
IMA_XATTR_DIGEST = 0x01,
@@ -48,10 +66,13 @@ 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 short flags;
+ unsigned long flags;
struct evm_ima_xattr_data ima_xattr;
- enum integrity_status ima_status;
- enum integrity_status evm_status;
+ enum integrity_status ima_file_status:4;
+ enum integrity_status ima_mmap_status:4;
+ enum integrity_status ima_bprm_status:4;
+ enum integrity_status ima_module_status:4;
+ enum integrity_status evm_status:4;
};
/* rbtree tree calls to lookup, insert, delete
@@ -81,5 +102,16 @@ static inline int integrity_digsig_verify(const unsigned int id,
#endif /* CONFIG_INTEGRITY_SIGNATURE */
+#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
+int asymmetric_verify(struct key *keyring, const char *sig,
+ int siglen, const char *data, int datalen);
+#else
+static inline int asymmetric_verify(struct key *keyring, const char *sig,
+ int siglen, const char *data, int datalen)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
/* set during initialization */
extern int iint_initialized;
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 20e4bf57aec8..58dfe0890947 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -367,6 +367,8 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
switch (PTR_ERR(key_ref)) {
case -EAGAIN: /* no key */
+ if (ret)
+ break;
case -ENOKEY: /* negative key */
ret = key_ref;
break;
diff --git a/security/security.c b/security/security.c
index daa97f4ac9d1..7b88c6aeaed4 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1254,24 +1254,42 @@ void security_secmark_refcount_dec(void)
}
EXPORT_SYMBOL(security_secmark_refcount_dec);
+int security_tun_dev_alloc_security(void **security)
+{
+ return security_ops->tun_dev_alloc_security(security);
+}
+EXPORT_SYMBOL(security_tun_dev_alloc_security);
+
+void security_tun_dev_free_security(void *security)
+{
+ security_ops->tun_dev_free_security(security);
+}
+EXPORT_SYMBOL(security_tun_dev_free_security);
+
int security_tun_dev_create(void)
{
return security_ops->tun_dev_create();
}
EXPORT_SYMBOL(security_tun_dev_create);
-void security_tun_dev_post_create(struct sock *sk)
+int security_tun_dev_attach_queue(void *security)
{
- return security_ops->tun_dev_post_create(sk);
+ return security_ops->tun_dev_attach_queue(security);
}
-EXPORT_SYMBOL(security_tun_dev_post_create);
+EXPORT_SYMBOL(security_tun_dev_attach_queue);
-int security_tun_dev_attach(struct sock *sk)
+int security_tun_dev_attach(struct sock *sk, void *security)
{
- return security_ops->tun_dev_attach(sk);
+ return security_ops->tun_dev_attach(sk, security);
}
EXPORT_SYMBOL(security_tun_dev_attach);
+int security_tun_dev_open(void *security)
+{
+ return security_ops->tun_dev_open(security);
+}
+EXPORT_SYMBOL(security_tun_dev_open);
+
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 2963c689f9c0..84b591711eec 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4399,6 +4399,24 @@ static void selinux_req_classify_flow(const struct request_sock *req,
fl->flowi_secid = req->secid;
}
+static int selinux_tun_dev_alloc_security(void **security)
+{
+ struct tun_security_struct *tunsec;
+
+ tunsec = kzalloc(sizeof(*tunsec), GFP_KERNEL);
+ if (!tunsec)
+ return -ENOMEM;
+ tunsec->sid = current_sid();
+
+ *security = tunsec;
+ return 0;
+}
+
+static void selinux_tun_dev_free_security(void *security)
+{
+ kfree(security);
+}
+
static int selinux_tun_dev_create(void)
{
u32 sid = current_sid();
@@ -4414,8 +4432,17 @@ static int selinux_tun_dev_create(void)
NULL);
}
-static void selinux_tun_dev_post_create(struct sock *sk)
+static int selinux_tun_dev_attach_queue(void *security)
{
+ struct tun_security_struct *tunsec = security;
+
+ return avc_has_perm(current_sid(), tunsec->sid, SECCLASS_TUN_SOCKET,
+ TUN_SOCKET__ATTACH_QUEUE, NULL);
+}
+
+static int selinux_tun_dev_attach(struct sock *sk, void *security)
+{
+ struct tun_security_struct *tunsec = security;
struct sk_security_struct *sksec = sk->sk_security;
/* we don't currently perform any NetLabel based labeling here and it
@@ -4425,20 +4452,19 @@ static void selinux_tun_dev_post_create(struct sock *sk)
* cause confusion to the TUN user that had no idea network labeling
* protocols were being used */
- /* see the comments in selinux_tun_dev_create() about why we don't use
- * the sockcreate SID here */
-
- sksec->sid = current_sid();
+ sksec->sid = tunsec->sid;
sksec->sclass = SECCLASS_TUN_SOCKET;
+
+ return 0;
}
-static int selinux_tun_dev_attach(struct sock *sk)
+static int selinux_tun_dev_open(void *security)
{
- struct sk_security_struct *sksec = sk->sk_security;
+ struct tun_security_struct *tunsec = security;
u32 sid = current_sid();
int err;
- err = avc_has_perm(sid, sksec->sid, SECCLASS_TUN_SOCKET,
+ err = avc_has_perm(sid, tunsec->sid, SECCLASS_TUN_SOCKET,
TUN_SOCKET__RELABELFROM, NULL);
if (err)
return err;
@@ -4446,8 +4472,7 @@ static int selinux_tun_dev_attach(struct sock *sk)
TUN_SOCKET__RELABELTO, NULL);
if (err)
return err;
-
- sksec->sid = sid;
+ tunsec->sid = sid;
return 0;
}
@@ -5642,9 +5667,12 @@ static struct security_operations selinux_ops = {
.secmark_refcount_inc = selinux_secmark_refcount_inc,
.secmark_refcount_dec = selinux_secmark_refcount_dec,
.req_classify_flow = selinux_req_classify_flow,
+ .tun_dev_alloc_security = selinux_tun_dev_alloc_security,
+ .tun_dev_free_security = selinux_tun_dev_free_security,
.tun_dev_create = selinux_tun_dev_create,
- .tun_dev_post_create = selinux_tun_dev_post_create,
+ .tun_dev_attach_queue = selinux_tun_dev_attach_queue,
.tun_dev_attach = selinux_tun_dev_attach,
+ .tun_dev_open = selinux_tun_dev_open,
#ifdef CONFIG_SECURITY_NETWORK_XFRM
.xfrm_policy_alloc_security = selinux_xfrm_policy_alloc,
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index df2de54a958d..14d04e63b1f0 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -150,6 +150,6 @@ struct security_class_mapping secclass_map[] = {
NULL } },
{ "kernel_service", { "use_as_override", "create_files_as", NULL } },
{ "tun_socket",
- { COMMON_SOCK_PERMS, NULL } },
+ { COMMON_SOCK_PERMS, "attach_queue", NULL } },
{ NULL }
};
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 26c7eee1c309..aa47bcabb5f6 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -110,6 +110,10 @@ struct sk_security_struct {
u16 sclass; /* sock security class */
};
+struct tun_security_struct {
+ u32 sid; /* SID for the tun device sockets */
+};
+
struct key_security_struct {
u32 sid; /* SID of key */
};