summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/apparmor/apparmorfs.c4
-rw-r--r--security/apparmor/crypto.c34
-rw-r--r--security/apparmor/include/policy.h4
-rw-r--r--security/apparmor/policy.c4
-rw-r--r--security/device_cgroup.c11
-rw-r--r--security/integrity/digsig.c30
-rw-r--r--security/integrity/ima/Kconfig8
-rw-r--r--security/integrity/ima/ima.h7
-rw-r--r--security/integrity/ima/ima_api.c22
-rw-r--r--security/integrity/ima/ima_appraise.c11
-rw-r--r--security/integrity/ima/ima_crypto.c17
-rw-r--r--security/integrity/ima/ima_fs.c14
-rw-r--r--security/integrity/ima/ima_init.c3
-rw-r--r--security/integrity/ima/ima_template.c21
-rw-r--r--security/integrity/ima/ima_template_lib.c6
-rw-r--r--security/integrity/integrity.h7
-rw-r--r--security/keys/big_key.c3
-rw-r--r--security/keys/gc.c42
-rw-r--r--security/keys/key.c8
-rw-r--r--security/keys/keyring.c62
-rw-r--r--security/keys/persistent.c6
-rw-r--r--security/lsm_audit.c10
-rw-r--r--security/selinux/avc.c9
-rw-r--r--security/selinux/hooks.c212
-rw-r--r--security/selinux/include/avc.h18
-rw-r--r--security/selinux/include/xfrm.h8
-rw-r--r--security/selinux/nlmsgtab.c2
-rw-r--r--security/selinux/ss/services.c42
-rw-r--r--security/selinux/xfrm.c62
29 files changed, 361 insertions, 326 deletions
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 95c2b2689a03..7db9954f1af2 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -580,15 +580,13 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root,
/* check if the next ns is a sibling, parent, gp, .. */
parent = ns->parent;
- while (parent) {
+ while (ns != root) {
mutex_unlock(&ns->lock);
next = list_entry_next(ns, base.list);
if (!list_entry_is_head(next, &parent->sub_ns, base.list)) {
mutex_lock(&next->lock);
return next;
}
- if (parent == root)
- return NULL;
ns = parent;
parent = parent->parent;
}
diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c
index d6222ba4e919..532471d0b3a0 100644
--- a/security/apparmor/crypto.c
+++ b/security/apparmor/crypto.c
@@ -15,14 +15,14 @@
* it should be.
*/
-#include <linux/crypto.h>
+#include <crypto/hash.h>
#include "include/apparmor.h"
#include "include/crypto.h"
static unsigned int apparmor_hash_size;
-static struct crypto_hash *apparmor_tfm;
+static struct crypto_shash *apparmor_tfm;
unsigned int aa_hash_size(void)
{
@@ -32,35 +32,33 @@ unsigned int aa_hash_size(void)
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
size_t len)
{
- struct scatterlist sg[2];
- struct hash_desc desc = {
- .tfm = apparmor_tfm,
- .flags = 0
- };
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(apparmor_tfm)];
+ } desc;
int error = -ENOMEM;
u32 le32_version = cpu_to_le32(version);
if (!apparmor_tfm)
return 0;
- sg_init_table(sg, 2);
- sg_set_buf(&sg[0], &le32_version, 4);
- sg_set_buf(&sg[1], (u8 *) start, len);
-
profile->hash = kzalloc(apparmor_hash_size, GFP_KERNEL);
if (!profile->hash)
goto fail;
- error = crypto_hash_init(&desc);
+ desc.shash.tfm = apparmor_tfm;
+ desc.shash.flags = 0;
+
+ error = crypto_shash_init(&desc.shash);
if (error)
goto fail;
- error = crypto_hash_update(&desc, &sg[0], 4);
+ error = crypto_shash_update(&desc.shash, (u8 *) &le32_version, 4);
if (error)
goto fail;
- error = crypto_hash_update(&desc, &sg[1], len);
+ error = crypto_shash_update(&desc.shash, (u8 *) start, len);
if (error)
goto fail;
- error = crypto_hash_final(&desc, profile->hash);
+ error = crypto_shash_final(&desc.shash, profile->hash);
if (error)
goto fail;
@@ -75,19 +73,19 @@ fail:
static int __init init_profile_hash(void)
{
- struct crypto_hash *tfm;
+ struct crypto_shash *tfm;
if (!apparmor_initialized)
return 0;
- tfm = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC);
+ tfm = crypto_alloc_shash("sha1", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm)) {
int error = PTR_ERR(tfm);
AA_ERROR("failed to setup profile sha1 hashing: %d\n", error);
return error;
}
apparmor_tfm = tfm;
- apparmor_hash_size = crypto_hash_digestsize(apparmor_tfm);
+ apparmor_hash_size = crypto_shash_digestsize(apparmor_tfm);
aa_info_message("AppArmor sha1 policy hashing enabled");
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index f2d4b6348cbc..c28b0f20ab53 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -360,7 +360,9 @@ static inline void aa_put_replacedby(struct aa_replacedby *p)
static inline void __aa_update_replacedby(struct aa_profile *orig,
struct aa_profile *new)
{
- struct aa_profile *tmp = rcu_dereference(orig->replacedby->profile);
+ struct aa_profile *tmp;
+ tmp = rcu_dereference_protected(orig->replacedby->profile,
+ mutex_is_locked(&orig->ns->lock));
rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new));
orig->flags |= PFLAG_INVALID;
aa_put_profile(tmp);
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 6172509fa2b7..705c2879d3a9 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -563,7 +563,8 @@ void __init aa_free_root_ns(void)
static void free_replacedby(struct aa_replacedby *r)
{
if (r) {
- aa_put_profile(rcu_dereference(r->profile));
+ /* r->profile will not be updated any more as r is dead */
+ aa_put_profile(rcu_dereference_protected(r->profile, true));
kzfree(r);
}
}
@@ -609,6 +610,7 @@ void aa_free_profile(struct aa_profile *profile)
aa_put_dfa(profile->policy.dfa);
aa_put_replacedby(profile->replacedby);
+ kzfree(profile->hash);
kzfree(profile);
}
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index c123628d3f84..7c2a0a71049e 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -63,16 +63,6 @@ static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
struct cgroup_subsys devices_subsys;
-static int devcgroup_can_attach(struct cgroup_subsys_state *new_css,
- struct cgroup_taskset *set)
-{
- struct task_struct *task = cgroup_taskset_first(set);
-
- if (current != task && !capable(CAP_SYS_ADMIN))
- return -EPERM;
- return 0;
-}
-
/*
* called under devcgroup_mutex
*/
@@ -697,7 +687,6 @@ static struct cftype dev_cgroup_files[] = {
struct cgroup_subsys devices_subsys = {
.name = "devices",
- .can_attach = devcgroup_can_attach,
.css_alloc = devcgroup_css_alloc,
.css_free = devcgroup_css_free,
.css_online = devcgroup_online,
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 77ca965ab684..b4af4ebc5be2 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -13,9 +13,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/err.h>
-#include <linux/sched.h>
#include <linux/rbtree.h>
-#include <linux/cred.h>
#include <linux/key-type.h>
#include <linux/digsig.h>
@@ -23,19 +21,11 @@
static struct key *keyring[INTEGRITY_KEYRING_MAX];
-#ifdef CONFIG_IMA_TRUSTED_KEYRING
-static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {
- ".evm",
- ".module",
- ".ima",
-};
-#else
static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {
"_evm",
"_module",
"_ima",
};
-#endif
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
const char *digest, int digestlen)
@@ -45,7 +35,7 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
if (!keyring[id]) {
keyring[id] =
- request_key(&key_type_keyring, keyring_name[id], NULL);
+ request_key(&key_type_keyring, keyring_name[id], NULL);
if (IS_ERR(keyring[id])) {
int err = PTR_ERR(keyring[id]);
pr_err("no %s keyring: %d\n", keyring_name[id], err);
@@ -66,21 +56,3 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
return -EOPNOTSUPP;
}
-
-int integrity_init_keyring(const unsigned int id)
-{
- const struct cred *cred = current_cred();
- const struct user_struct *user = cred->user;
-
- keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
- KGIDT_INIT(0), cred,
- ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
- KEY_USR_VIEW | KEY_USR_READ),
- KEY_ALLOC_NOT_IN_QUOTA, user->uid_keyring);
- if (!IS_ERR(keyring[id]))
- set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags);
- else
- pr_info("Can't allocate %s keyring (%ld)\n",
- keyring_name[id], PTR_ERR(keyring[id]));
- return 0;
-}
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index dad8d4ca2437..81a27971d884 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -123,11 +123,3 @@ config IMA_APPRAISE
For more information on integrity appraisal refer to:
<http://linux-ima.sourceforge.net>
If unsure, say N.
-
-config IMA_TRUSTED_KEYRING
- bool "Require all keys on the _ima keyring be signed"
- depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING
- default y
- help
- This option requires that all keys added to the _ima
- keyring be signed by a key on the system trusted keyring.
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index bf03c6a16cc8..0356e1d437ca 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -26,7 +26,8 @@
#include "../integrity.h"
-enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
+enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_BINARY_NO_FIELD_LEN,
+ IMA_SHOW_ASCII };
enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
/* digest size for IMA, fits SHA1 or MD5 */
@@ -97,7 +98,8 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
const char *op, struct inode *inode,
const unsigned char *filename);
int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash);
-int ima_calc_field_array_hash(struct ima_field_data *field_data, int num_fields,
+int ima_calc_field_array_hash(struct ima_field_data *field_data,
+ struct ima_template_desc *desc, int num_fields,
struct ima_digest_data *hash);
int __init ima_calc_boot_aggregate(struct ima_digest_data *hash);
void ima_add_violation(struct file *file, const unsigned char *filename,
@@ -146,6 +148,7 @@ int ima_alloc_init_template(struct integrity_iint_cache *iint,
int xattr_len, struct ima_template_entry **entry);
int ima_store_template(struct ima_template_entry *entry, int violation,
struct inode *inode, const unsigned char *filename);
+void ima_free_template_entry(struct ima_template_entry *entry);
const char *ima_d_path(struct path *path, char **pathbuf);
/* rbtree tree calls to lookup, insert, delete
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 0e7540863fc2..c38bbce8c6a6 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -22,6 +22,19 @@
#include "ima.h"
/*
+ * ima_free_template_entry - free an existing template entry
+ */
+void ima_free_template_entry(struct ima_template_entry *entry)
+{
+ int i;
+
+ for (i = 0; i < entry->template_desc->num_fields; i++)
+ kfree(entry->template_data[i].data);
+
+ kfree(entry);
+}
+
+/*
* ima_alloc_init_template - create and initialize a new template entry
*/
int ima_alloc_init_template(struct integrity_iint_cache *iint,
@@ -37,6 +50,7 @@ int ima_alloc_init_template(struct integrity_iint_cache *iint,
if (!*entry)
return -ENOMEM;
+ (*entry)->template_desc = template_desc;
for (i = 0; i < template_desc->num_fields; i++) {
struct ima_template_field *field = template_desc->fields[i];
u32 len;
@@ -51,10 +65,9 @@ int ima_alloc_init_template(struct integrity_iint_cache *iint,
(*entry)->template_data_len += sizeof(len);
(*entry)->template_data_len += len;
}
- (*entry)->template_desc = template_desc;
return 0;
out:
- kfree(*entry);
+ ima_free_template_entry(*entry);
*entry = NULL;
return result;
}
@@ -94,6 +107,7 @@ int ima_store_template(struct ima_template_entry *entry,
/* this function uses default algo */
hash.hdr.algo = HASH_ALGO_SHA1;
result = ima_calc_field_array_hash(&entry->template_data[0],
+ entry->template_desc,
num_fields, &hash.hdr);
if (result < 0) {
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
@@ -133,7 +147,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
}
result = ima_store_template(entry, violation, inode, filename);
if (result < 0)
- kfree(entry);
+ ima_free_template_entry(entry);
err_out:
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
op, cause, result, 0);
@@ -268,7 +282,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
if (!result || result == -EEXIST)
iint->flags |= IMA_MEASURED;
if (result < 0)
- kfree(entry);
+ ima_free_template_entry(entry);
}
void ima_audit_measurement(struct integrity_iint_cache *iint,
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 46353ee517f6..734e9468aca0 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -381,14 +381,3 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
}
return result;
}
-
-#ifdef CONFIG_IMA_TRUSTED_KEYRING
-static int __init init_ima_keyring(void)
-{
- int ret;
-
- ret = integrity_init_keyring(INTEGRITY_KEYRING_IMA);
- return 0;
-}
-late_initcall(init_ima_keyring);
-#endif
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 676e0292dfec..fdf60def52e9 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -140,6 +140,7 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
* Calculate the hash of template data
*/
static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data,
+ struct ima_template_desc *td,
int num_fields,
struct ima_digest_data *hash,
struct crypto_shash *tfm)
@@ -160,9 +161,13 @@ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data,
return rc;
for (i = 0; i < num_fields; i++) {
- rc = crypto_shash_update(&desc.shash,
- (const u8 *) &field_data[i].len,
- sizeof(field_data[i].len));
+ if (strcmp(td->name, IMA_TEMPLATE_IMA_NAME) != 0) {
+ rc = crypto_shash_update(&desc.shash,
+ (const u8 *) &field_data[i].len,
+ sizeof(field_data[i].len));
+ if (rc)
+ break;
+ }
rc = crypto_shash_update(&desc.shash, field_data[i].data,
field_data[i].len);
if (rc)
@@ -175,7 +180,8 @@ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data,
return rc;
}
-int ima_calc_field_array_hash(struct ima_field_data *field_data, int num_fields,
+int ima_calc_field_array_hash(struct ima_field_data *field_data,
+ struct ima_template_desc *desc, int num_fields,
struct ima_digest_data *hash)
{
struct crypto_shash *tfm;
@@ -185,7 +191,8 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data, int num_fields,
if (IS_ERR(tfm))
return PTR_ERR(tfm);
- rc = ima_calc_field_array_hash_tfm(field_data, num_fields, hash, tfm);
+ rc = ima_calc_field_array_hash_tfm(field_data, desc, num_fields,
+ hash, tfm);
ima_free_tfm(tfm);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index d47a7c86a21d..db01125926bd 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -120,6 +120,7 @@ static int ima_measurements_show(struct seq_file *m, void *v)
struct ima_template_entry *e;
int namelen;
u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
+ bool is_ima_template = false;
int i;
/* get entry */
@@ -145,14 +146,21 @@ static int ima_measurements_show(struct seq_file *m, void *v)
ima_putc(m, e->template_desc->name, namelen);
/* 5th: template length (except for 'ima' template) */
- if (strcmp(e->template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0)
+ if (strcmp(e->template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0)
+ is_ima_template = true;
+
+ if (!is_ima_template)
ima_putc(m, &e->template_data_len,
sizeof(e->template_data_len));
/* 6th: template specific data */
for (i = 0; i < e->template_desc->num_fields; i++) {
- e->template_desc->fields[i]->field_show(m, IMA_SHOW_BINARY,
- &e->template_data[i]);
+ enum ima_show_type show = IMA_SHOW_BINARY;
+ struct ima_template_field *field = e->template_desc->fields[i];
+
+ if (is_ima_template && strcmp(field->field_id, "d") == 0)
+ show = IMA_SHOW_BINARY_NO_FIELD_LEN;
+ field->field_show(m, show, &e->template_data[i]);
}
return 0;
}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 15f34bd40abe..37122768554a 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -63,7 +63,6 @@ static void __init ima_add_boot_aggregate(void)
result = ima_calc_boot_aggregate(&hash.hdr);
if (result < 0) {
audit_cause = "hashing_error";
- kfree(entry);
goto err_out;
}
}
@@ -76,7 +75,7 @@ static void __init ima_add_boot_aggregate(void)
result = ima_store_template(entry, violation, NULL,
boot_aggregate_name);
if (result < 0)
- kfree(entry);
+ ima_free_template_entry(entry);
return;
err_out:
integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op,
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 4e5da990630b..635695f6a185 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -90,7 +90,7 @@ static struct ima_template_field *lookup_template_field(const char *field_id)
return NULL;
}
-static int template_fmt_size(char *template_fmt)
+static int template_fmt_size(const char *template_fmt)
{
char c;
int template_fmt_len = strlen(template_fmt);
@@ -106,22 +106,29 @@ static int template_fmt_size(char *template_fmt)
return j + 1;
}
-static int template_desc_init_fields(char *template_fmt,
+static int template_desc_init_fields(const char *template_fmt,
struct ima_template_field ***fields,
int *num_fields)
{
- char *c, *template_fmt_ptr = template_fmt;
+ char *c, *template_fmt_copy, *template_fmt_ptr;
int template_num_fields = template_fmt_size(template_fmt);
int i, result = 0;
if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX)
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; (c = strsep(&template_fmt_ptr, "|")) != NULL &&
i < template_num_fields; i++) {
struct ima_template_field *f = lookup_template_field(c);
@@ -133,10 +140,12 @@ static int template_desc_init_fields(char *template_fmt,
(*fields)[i] = f;
}
*num_fields = i;
- return 0;
out:
- kfree(*fields);
- *fields = NULL;
+ if (result < 0) {
+ kfree(*fields);
+ *fields = NULL;
+ }
+ kfree(template_fmt_copy);
return result;
}
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 6d01c694a32c..1683bbf289a4 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -109,9 +109,12 @@ static void ima_show_template_data_binary(struct seq_file *m,
enum data_formats datafmt,
struct ima_field_data *field_data)
{
- ima_putc(m, &field_data->len, sizeof(u32));
+ if (show != IMA_SHOW_BINARY_NO_FIELD_LEN)
+ ima_putc(m, &field_data->len, sizeof(u32));
+
if (!field_data->len)
return;
+
ima_putc(m, field_data->data, field_data->len);
}
@@ -125,6 +128,7 @@ static void ima_show_template_field_data(struct seq_file *m,
ima_show_template_data_ascii(m, show, datafmt, field_data);
break;
case IMA_SHOW_BINARY:
+ case IMA_SHOW_BINARY_NO_FIELD_LEN:
ima_show_template_data_binary(m, show, datafmt, field_data);
break;
default:
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index b9e7c133734a..2fb5e53e927f 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -137,19 +137,12 @@ static inline int integrity_digsig_verify(const unsigned int id,
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
int asymmetric_verify(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen);
-
-int integrity_init_keyring(const unsigned int id);
#else
static inline int asymmetric_verify(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen)
{
return -EOPNOTSUPP;
}
-
-static int integrity_init_keyring(const unsigned int id)
-{
- return 0;
-}
#endif
#ifdef CONFIG_INTEGRITY_AUDIT
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
index 2cf5e62d67af..8137b27d641d 100644
--- a/security/keys/big_key.c
+++ b/security/keys/big_key.c
@@ -70,7 +70,7 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
*
* TODO: Encrypt the stored data with a temporary key.
*/
- file = shmem_file_setup("", datalen, 0);
+ file = shmem_kernel_file_setup("", datalen, 0);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto err_quota;
@@ -78,6 +78,7 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
written = kernel_write(file, prep->data, prep->datalen, 0);
if (written != datalen) {
+ ret = written;
if (written >= 0)
ret = -ENOMEM;
goto err_fput;
diff --git a/security/keys/gc.c b/security/keys/gc.c
index cce621c33dce..d3222b6d7d59 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -130,46 +130,6 @@ void key_gc_keytype(struct key_type *ktype)
kleave("");
}
-static int key_gc_keyring_func(const void *object, void *iterator_data)
-{
- const struct key *key = object;
- time_t *limit = iterator_data;
- return key_is_dead(key, *limit);
-}
-
-/*
- * Garbage collect pointers from a keyring.
- *
- * Not called with any locks held. The keyring's key struct will not be
- * deallocated under us as only our caller may deallocate it.
- */
-static void key_gc_keyring(struct key *keyring, time_t limit)
-{
- int result;
-
- kenter("%x{%s}", keyring->serial, keyring->description ?: "");
-
- if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
- (1 << KEY_FLAG_REVOKED)))
- goto dont_gc;
-
- /* scan the keyring looking for dead keys */
- rcu_read_lock();
- result = assoc_array_iterate(&keyring->keys,
- key_gc_keyring_func, &limit);
- rcu_read_unlock();
- if (result == true)
- goto do_gc;
-
-dont_gc:
- kleave(" [no gc]");
- return;
-
-do_gc:
- keyring_gc(keyring, limit);
- kleave(" [gc]");
-}
-
/*
* Garbage collect a list of unreferenced, detached keys
*/
@@ -388,7 +348,7 @@ found_unreferenced_key:
*/
found_keyring:
spin_unlock(&key_serial_lock);
- key_gc_keyring(key, limit);
+ keyring_gc(key, limit);
goto maybe_resched;
/* We found a dead key that is still referenced. Reset its type and
diff --git a/security/keys/key.c b/security/keys/key.c
index 55d110f0aced..6e21c11e48bc 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -272,7 +272,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
}
/* allocate and initialise the key and its description */
- key = kmem_cache_alloc(key_jar, GFP_KERNEL);
+ key = kmem_cache_zalloc(key_jar, GFP_KERNEL);
if (!key)
goto no_memory_2;
@@ -293,18 +293,12 @@ struct key *key_alloc(struct key_type *type, const char *desc,
key->uid = uid;
key->gid = gid;
key->perm = perm;
- key->flags = 0;
- key->expiry = 0;
- key->payload.data = NULL;
- key->security = NULL;
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
key->flags |= 1 << KEY_FLAG_IN_QUOTA;
if (flags & KEY_ALLOC_TRUSTED)
key->flags |= 1 << KEY_FLAG_TRUSTED;
- memset(&key->type_data, 0, sizeof(key->type_data));
-
#ifdef KEY_DEBUGGING
key->magic = KEY_DEBUG_MAGIC;
#endif
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index d80311e571c3..d46cbc5e335e 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -160,7 +160,7 @@ static u64 mult_64x32_and_fold(u64 x, u32 y)
static unsigned long hash_key_type_and_desc(const struct keyring_index_key *index_key)
{
const unsigned level_shift = ASSOC_ARRAY_LEVEL_STEP;
- const unsigned long level_mask = ASSOC_ARRAY_LEVEL_STEP_MASK;
+ const unsigned long fan_mask = ASSOC_ARRAY_FAN_MASK;
const char *description = index_key->description;
unsigned long hash, type;
u32 piece;
@@ -194,10 +194,10 @@ static unsigned long hash_key_type_and_desc(const struct keyring_index_key *inde
* ordinary keys by making sure the lowest level segment in the hash is
* zero for keyrings and non-zero otherwise.
*/
- if (index_key->type != &key_type_keyring && (hash & level_mask) == 0)
+ if (index_key->type != &key_type_keyring && (hash & fan_mask) == 0)
return hash | (hash >> (ASSOC_ARRAY_KEY_CHUNK_SIZE - level_shift)) | 1;
- if (index_key->type == &key_type_keyring && (hash & level_mask) != 0)
- return (hash + (hash << level_shift)) & ~level_mask;
+ if (index_key->type == &key_type_keyring && (hash & fan_mask) != 0)
+ return (hash + (hash << level_shift)) & ~fan_mask;
return hash;
}
@@ -279,12 +279,11 @@ static bool keyring_compare_object(const void *object, const void *data)
* Compare the index keys of a pair of objects and determine the bit position
* at which they differ - if they differ.
*/
-static int keyring_diff_objects(const void *_a, const void *_b)
+static int keyring_diff_objects(const void *object, const void *data)
{
- const struct key *key_a = keyring_ptr_to_key(_a);
- const struct key *key_b = keyring_ptr_to_key(_b);
+ const struct key *key_a = keyring_ptr_to_key(object);
const struct keyring_index_key *a = &key_a->index_key;
- const struct keyring_index_key *b = &key_b->index_key;
+ const struct keyring_index_key *b = data;
unsigned long seg_a, seg_b;
int level, i;
@@ -691,8 +690,8 @@ descend_to_node:
smp_read_barrier_depends();
ptr = ACCESS_ONCE(shortcut->next_node);
BUG_ON(!assoc_array_ptr_is_node(ptr));
- node = assoc_array_ptr_to_node(ptr);
}
+ node = assoc_array_ptr_to_node(ptr);
begin_node:
kdebug("begin_node");
@@ -1304,7 +1303,7 @@ static void keyring_revoke(struct key *keyring)
}
}
-static bool gc_iterator(void *object, void *iterator_data)
+static bool keyring_gc_select_iterator(void *object, void *iterator_data)
{
struct key *key = keyring_ptr_to_key(object);
time_t *limit = iterator_data;
@@ -1315,22 +1314,47 @@ static bool gc_iterator(void *object, void *iterator_data)
return true;
}
+static int keyring_gc_check_iterator(const void *object, void *iterator_data)
+{
+ const struct key *key = keyring_ptr_to_key(object);
+ time_t *limit = iterator_data;
+
+ key_check(key);
+ return key_is_dead(key, *limit);
+}
+
/*
- * Collect garbage from the contents of a keyring, replacing the old list with
- * a new one with the pointers all shuffled down.
+ * Garbage collect pointers from a keyring.
*
- * Dead keys are classed as oned that are flagged as being dead or are revoked,
- * expired or negative keys that were revoked or expired before the specified
- * limit.
+ * Not called with any locks held. The keyring's key struct will not be
+ * deallocated under us as only our caller may deallocate it.
*/
void keyring_gc(struct key *keyring, time_t limit)
{
- kenter("{%x,%s}", key_serial(keyring), keyring->description);
+ int result;
+ kenter("%x{%s}", keyring->serial, keyring->description ?: "");
+
+ if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
+ (1 << KEY_FLAG_REVOKED)))
+ goto dont_gc;
+
+ /* scan the keyring looking for dead keys */
+ rcu_read_lock();
+ result = assoc_array_iterate(&keyring->keys,
+ keyring_gc_check_iterator, &limit);
+ rcu_read_unlock();
+ if (result == true)
+ goto do_gc;
+
+dont_gc:
+ kleave(" [no gc]");
+ return;
+
+do_gc:
down_write(&keyring->sem);
assoc_array_gc(&keyring->keys, &keyring_assoc_array_ops,
- gc_iterator, &limit);
+ keyring_gc_select_iterator, &limit);
up_write(&keyring->sem);
-
- kleave("");
+ kleave(" [gc]");
}
diff --git a/security/keys/persistent.c b/security/keys/persistent.c
index 82f4957a7acf..0ad3ee283781 100644
--- a/security/keys/persistent.c
+++ b/security/keys/persistent.c
@@ -144,10 +144,8 @@ long keyctl_get_persistent(uid_t _uid, key_serial_t destid)
/* You can only see your own persistent cache if you're not
* sufficiently privileged.
*/
- if (uid_eq(uid, current_uid()) &&
- uid_eq(uid, current_suid()) &&
- uid_eq(uid, current_euid()) &&
- uid_eq(uid, current_fsuid()) &&
+ if (!uid_eq(uid, current_uid()) &&
+ !uid_eq(uid, current_euid()) &&
!ns_capable(ns, CAP_SETUID))
return -EPERM;
}
diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index 8d8d97dbb389..9a62045e6282 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -302,18 +302,19 @@ static void dump_common_audit_data(struct audit_buffer *ab,
"faddr", "fport");
break;
}
+#if IS_ENABLED(CONFIG_IPV6)
case AF_INET6: {
struct inet_sock *inet = inet_sk(sk);
- struct ipv6_pinfo *inet6 = inet6_sk(sk);
- print_ipv6_addr(ab, &inet6->rcv_saddr,
+ print_ipv6_addr(ab, &sk->sk_v6_rcv_saddr,
inet->inet_sport,
"laddr", "lport");
- print_ipv6_addr(ab, &inet6->daddr,
+ print_ipv6_addr(ab, &sk->sk_v6_daddr,
inet->inet_dport,
"faddr", "fport");
break;
}
+#endif
case AF_UNIX:
u = unix_sk(sk);
if (u->path.dentry) {
@@ -396,7 +397,8 @@ void common_lsm_audit(struct common_audit_data *a,
if (a == NULL)
return;
/* we use GFP_ATOMIC so we won't sleep */
- ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC);
+ ab = audit_log_start(current->audit_context, GFP_ATOMIC | __GFP_NOWARN,
+ AUDIT_AVC);
if (ab == NULL)
return;
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index dad36a6ab45f..fc3e6628a864 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -746,7 +746,6 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
* @tclass: target security class
* @requested: requested permissions, interpreted based on @tclass
* @auditdata: auxiliary audit data
- * @flags: VFS walk flags
*
* Check the AVC to determine whether the @requested permissions are granted
* for the SID pair (@ssid, @tsid), interpreting the permissions
@@ -756,17 +755,15 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
* permissions are granted, -%EACCES if any permissions are denied, or
* another -errno upon other errors.
*/
-int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass,
- u32 requested, struct common_audit_data *auditdata,
- unsigned flags)
+int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
+ u32 requested, struct common_audit_data *auditdata)
{
struct av_decision avd;
int rc, rc2;
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
- rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata,
- flags);
+ rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
if (rc2)
return rc2;
return rc;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 6d0bf5c0c832..6625699f497c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -53,6 +53,7 @@
#include <net/ip.h> /* for local_port_range[] */
#include <net/sock.h>
#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
+#include <net/inet_connection_sock.h>
#include <net/net_namespace.h>
#include <net/netlabel.h>
#include <linux/uaccess.h>
@@ -95,10 +96,6 @@
#include "audit.h"
#include "avc_ss.h"
-#define SB_TYPE_FMT "%s%s%s"
-#define SB_SUBTYPE(sb) (sb->s_subtype && sb->s_subtype[0])
-#define SB_TYPE_ARGS(sb) sb->s_type->name, SB_SUBTYPE(sb) ? "." : "", SB_SUBTYPE(sb) ? sb->s_subtype : ""
-
extern struct security_operations *security_ops;
/* SECMARK reference count */
@@ -413,8 +410,8 @@ static int sb_finish_set_opts(struct super_block *sb)
the first boot of the SELinux kernel before we have
assigned xattr values to the filesystem. */
if (!root_inode->i_op->getxattr) {
- printk(KERN_WARNING "SELinux: (dev %s, type "SB_TYPE_FMT") has no "
- "xattr support\n", sb->s_id, SB_TYPE_ARGS(sb));
+ printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
+ "xattr support\n", sb->s_id, sb->s_type->name);
rc = -EOPNOTSUPP;
goto out;
}
@@ -422,22 +419,22 @@ static int sb_finish_set_opts(struct super_block *sb)
if (rc < 0 && rc != -ENODATA) {
if (rc == -EOPNOTSUPP)
printk(KERN_WARNING "SELinux: (dev %s, type "
- SB_TYPE_FMT") has no security xattr handler\n",
- sb->s_id, SB_TYPE_ARGS(sb));
+ "%s) has no security xattr handler\n",
+ sb->s_id, sb->s_type->name);
else
printk(KERN_WARNING "SELinux: (dev %s, type "
- SB_TYPE_FMT") getxattr errno %d\n", sb->s_id,
- SB_TYPE_ARGS(sb), -rc);
+ "%s) getxattr errno %d\n", sb->s_id,
+ sb->s_type->name, -rc);
goto out;
}
}
if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
- printk(KERN_ERR "SELinux: initialized (dev %s, type "SB_TYPE_FMT"), unknown behavior\n",
- sb->s_id, SB_TYPE_ARGS(sb));
+ printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
+ sb->s_id, sb->s_type->name);
else
- printk(KERN_DEBUG "SELinux: initialized (dev %s, type "SB_TYPE_FMT"), %s\n",
- sb->s_id, SB_TYPE_ARGS(sb),
+ printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n",
+ sb->s_id, sb->s_type->name,
labeling_behaviors[sbsec->behavior-1]);
sbsec->flags |= SE_SBINITIALIZED;
@@ -600,6 +597,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
const struct cred *cred = current_cred();
int rc = 0, i;
struct superblock_security_struct *sbsec = sb->s_security;
+ const char *name = sb->s_type->name;
struct inode *inode = sbsec->sb->s_root->d_inode;
struct inode_security_struct *root_isec = inode->i_security;
u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
@@ -658,8 +656,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
strlen(mount_options[i]), &sid);
if (rc) {
printk(KERN_WARNING "SELinux: security_context_to_sid"
- "(%s) failed for (dev %s, type "SB_TYPE_FMT") errno=%d\n",
- mount_options[i], sb->s_id, SB_TYPE_ARGS(sb), rc);
+ "(%s) failed for (dev %s, type %s) errno=%d\n",
+ mount_options[i], sb->s_id, name, rc);
goto out;
}
switch (flags[i]) {
@@ -806,8 +804,7 @@ out:
out_double_mount:
rc = -EINVAL;
printk(KERN_WARNING "SELinux: mount invalid. Same superblock, different "
- "security settings for (dev %s, type "SB_TYPE_FMT")\n", sb->s_id,
- SB_TYPE_ARGS(sb));
+ "security settings for (dev %s, type %s)\n", sb->s_id, name);
goto out;
}
@@ -1539,7 +1536,7 @@ static int cred_has_capability(const struct cred *cred,
rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
if (audit == SECURITY_CAP_AUDIT) {
- int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0);
+ int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad);
if (rc2)
return rc2;
}
@@ -1562,8 +1559,7 @@ static int task_has_system(struct task_struct *tsk,
static int inode_has_perm(const struct cred *cred,
struct inode *inode,
u32 perms,
- struct common_audit_data *adp,
- unsigned flags)
+ struct common_audit_data *adp)
{
struct inode_security_struct *isec;
u32 sid;
@@ -1576,7 +1572,7 @@ static int inode_has_perm(const struct cred *cred,
sid = cred_sid(cred);
isec = inode->i_security;
- return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags);
+ return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
}
/* Same as inode_has_perm, but pass explicit audit data containing
@@ -1591,7 +1587,7 @@ static inline int dentry_has_perm(const struct cred *cred,
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
- return inode_has_perm(cred, inode, av, &ad, 0);
+ return inode_has_perm(cred, inode, av, &ad);
}
/* Same as inode_has_perm, but pass explicit audit data containing
@@ -1606,7 +1602,7 @@ static inline int path_has_perm(const struct cred *cred,
ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = *path;
- return inode_has_perm(cred, inode, av, &ad, 0);
+ return inode_has_perm(cred, inode, av, &ad);
}
/* Same as path_has_perm, but uses the inode from the file struct. */
@@ -1618,7 +1614,7 @@ static inline int file_path_has_perm(const struct cred *cred,
ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = file->f_path;
- return inode_has_perm(cred, file_inode(file), av, &ad, 0);
+ return inode_has_perm(cred, file_inode(file), av, &ad);
}
/* Check whether a task can use an open file descriptor to
@@ -1654,7 +1650,7 @@ static int file_has_perm(const struct cred *cred,
/* av is zero if only checking access to the descriptor. */
rc = 0;
if (av)
- rc = inode_has_perm(cred, inode, av, &ad, 0);
+ rc = inode_has_perm(cred, inode, av, &ad);
out:
return rc;
@@ -2481,8 +2477,8 @@ static int selinux_sb_remount(struct super_block *sb, void *data)
rc = security_context_to_sid(mount_options[i], len, &sid);
if (rc) {
printk(KERN_WARNING "SELinux: security_context_to_sid"
- "(%s) failed for (dev %s, type "SB_TYPE_FMT") errno=%d\n",
- mount_options[i], sb->s_id, SB_TYPE_ARGS(sb), rc);
+ "(%s) failed for (dev %s, type %s) errno=%d\n",
+ mount_options[i], sb->s_id, sb->s_type->name, rc);
goto out_free_opts;
}
rc = -EINVAL;
@@ -2520,8 +2516,8 @@ out_free_secdata:
return rc;
out_bad_option:
printk(KERN_WARNING "SELinux: unable to change security options "
- "during remount (dev %s, type "SB_TYPE_FMT")\n", sb->s_id,
- SB_TYPE_ARGS(sb));
+ "during remount (dev %s, type=%s)\n", sb->s_id,
+ sb->s_type->name);
goto out_free_opts;
}
@@ -3829,7 +3825,7 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
u32 nlbl_sid;
u32 nlbl_type;
- err = selinux_skb_xfrm_sid(skb, &xfrm_sid);
+ err = selinux_xfrm_skb_sid(skb, &xfrm_sid);
if (unlikely(err))
return -EACCES;
err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid);
@@ -3847,6 +3843,30 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
return 0;
}
+/**
+ * selinux_conn_sid - Determine the child socket label for a connection
+ * @sk_sid: the parent socket's SID
+ * @skb_sid: the packet's SID
+ * @conn_sid: the resulting connection SID
+ *
+ * If @skb_sid is valid then the user:role:type information from @sk_sid is
+ * combined with the MLS information from @skb_sid in order to create
+ * @conn_sid. If @skb_sid is not valid then then @conn_sid is simply a copy
+ * of @sk_sid. Returns zero on success, negative values on failure.
+ *
+ */
+static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid)
+{
+ int err = 0;
+
+ if (skb_sid != SECSID_NULL)
+ err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid);
+ else
+ *conn_sid = sk_sid;
+
+ return err;
+}
+
/* socket security operations */
static int socket_sockcreate_sid(const struct task_security_struct *tsec,
@@ -3970,7 +3990,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
if (snum) {
int low, high;
- inet_get_local_port_range(&low, &high);
+ inet_get_local_port_range(sock_net(sk), &low, &high);
if (snum < max(PROT_SOCK, low) || snum > high) {
err = sel_netport_sid(sk->sk_protocol,
@@ -4314,8 +4334,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
}
err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
PEER__RECV, &ad);
- if (err)
+ if (err) {
selinux_netlbl_err(skb, err, 0);
+ return err;
+ }
}
if (secmark_active) {
@@ -4453,7 +4475,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
struct sk_security_struct *sksec = sk->sk_security;
int err;
u16 family = sk->sk_family;
- u32 newsid;
+ u32 connsid;
u32 peersid;
/* handle mapped IPv4 packets arriving via IPv6 sockets */
@@ -4463,16 +4485,11 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
err = selinux_skb_peerlbl_sid(skb, family, &peersid);
if (err)
return err;
- if (peersid == SECSID_NULL) {
- req->secid = sksec->sid;
- req->peer_secid = SECSID_NULL;
- } else {
- err = security_sid_mls_copy(sksec->sid, peersid, &newsid);
- if (err)
- return err;
- req->secid = newsid;
- req->peer_secid = peersid;
- }
+ err = selinux_conn_sid(sksec->sid, peersid, &connsid);
+ if (err)
+ return err;
+ req->secid = connsid;
+ req->peer_secid = peersid;
return selinux_netlbl_inet_conn_request(req, family);
}
@@ -4709,7 +4726,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
return NF_ACCEPT;
}
-static unsigned int selinux_ipv4_forward(unsigned int hooknum,
+static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
@@ -4719,7 +4736,7 @@ static unsigned int selinux_ipv4_forward(unsigned int hooknum,
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static unsigned int selinux_ipv6_forward(unsigned int hooknum,
+static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
@@ -4732,6 +4749,7 @@ static unsigned int selinux_ipv6_forward(unsigned int hooknum,
static unsigned int selinux_ip_output(struct sk_buff *skb,
u16 family)
{
+ struct sock *sk;
u32 sid;
if (!netlbl_enabled())
@@ -4740,8 +4758,27 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,
/* we do this in the LOCAL_OUT path and not the POST_ROUTING path
* because we want to make sure we apply the necessary labeling
* before IPsec is applied so we can leverage AH protection */
- if (skb->sk) {
- struct sk_security_struct *sksec = skb->sk->sk_security;
+ sk = skb->sk;
+ if (sk) {
+ struct sk_security_struct *sksec;
+
+ if (sk->sk_state == TCP_LISTEN)
+ /* if the socket is the listening state then this
+ * packet is a SYN-ACK packet which means it needs to
+ * be labeled based on the connection/request_sock and
+ * not the parent socket. unfortunately, we can't
+ * lookup the request_sock yet as it isn't queued on
+ * the parent socket until after the SYN-ACK is sent.
+ * the "solution" is to simply pass the packet as-is
+ * as any IP option based labeling should be copied
+ * from the initial connection request (in the IP
+ * layer). it is far from ideal, but until we get a
+ * security label in the packet itself this is the
+ * best we can do. */
+ return NF_ACCEPT;
+
+ /* standard practice, label using the parent socket */
+ sksec = sk->sk_security;
sid = sksec->sid;
} else
sid = SECINITSID_KERNEL;
@@ -4751,7 +4788,7 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,
return NF_ACCEPT;
}
-static unsigned int selinux_ipv4_output(unsigned int hooknum,
+static unsigned int selinux_ipv4_output(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
@@ -4811,27 +4848,36 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
* as fast and as clean as possible. */
if (!selinux_policycap_netpeer)
return selinux_ip_postroute_compat(skb, ifindex, family);
+
+ secmark_active = selinux_secmark_enabled();
+ peerlbl_active = selinux_peerlbl_enabled();
+ if (!secmark_active && !peerlbl_active)
+ return NF_ACCEPT;
+
+ sk = skb->sk;
+
#ifdef CONFIG_XFRM
/* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
* packet transformation so allow the packet to pass without any checks
* since we'll have another chance to perform access control checks
* when the packet is on it's final way out.
* NOTE: there appear to be some IPv6 multicast cases where skb->dst
- * is NULL, in this case go ahead and apply access control. */
- if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL)
+ * is NULL, in this case go ahead and apply access control.
+ * NOTE: if this is a local socket (skb->sk != NULL) that is in the
+ * TCP listening state we cannot wait until the XFRM processing
+ * is done as we will miss out on the SA label if we do;
+ * unfortunately, this means more work, but it is only once per
+ * connection. */
+ if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL &&
+ !(sk != NULL && sk->sk_state == TCP_LISTEN))
return NF_ACCEPT;
#endif
- secmark_active = selinux_secmark_enabled();
- peerlbl_active = selinux_peerlbl_enabled();
- if (!secmark_active && !peerlbl_active)
- return NF_ACCEPT;
- /* if the packet is being forwarded then get the peer label from the
- * packet itself; otherwise check to see if it is from a local
- * application or the kernel, if from an application get the peer label
- * from the sending socket, otherwise use the kernel's sid */
- sk = skb->sk;
if (sk == NULL) {
+ /* Without an associated socket the packet is either coming
+ * from the kernel or it is being forwarded; check the packet
+ * to determine which and if the packet is being forwarded
+ * query the packet directly to determine the security label. */
if (skb->skb_iif) {
secmark_perm = PACKET__FORWARD_OUT;
if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
@@ -4840,7 +4886,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
secmark_perm = PACKET__SEND;
peer_sid = SECINITSID_KERNEL;
}
+ } else if (sk->sk_state == TCP_LISTEN) {
+ /* Locally generated packet but the associated socket is in the
+ * listening state which means this is a SYN-ACK packet. In
+ * this particular case the correct security label is assigned
+ * to the connection/request_sock but unfortunately we can't
+ * query the request_sock as it isn't queued on the parent
+ * socket until after the SYN-ACK packet is sent; the only
+ * viable choice is to regenerate the label like we do in
+ * selinux_inet_conn_request(). See also selinux_ip_output()
+ * for similar problems. */
+ u32 skb_sid;
+ struct sk_security_struct *sksec = sk->sk_security;
+ if (selinux_skb_peerlbl_sid(skb, family, &skb_sid))
+ return NF_DROP;
+ /* At this point, if the returned skb peerlbl is SECSID_NULL
+ * and the packet has been through at least one XFRM
+ * transformation then we must be dealing with the "final"
+ * form of labeled IPsec packet; since we've already applied
+ * all of our access controls on this packet we can safely
+ * pass the packet. */
+ if (skb_sid == SECSID_NULL) {
+ switch (family) {
+ case PF_INET:
+ if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED)
+ return NF_ACCEPT;
+ break;
+ case PF_INET6:
+ if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED)
+ return NF_ACCEPT;
+ default:
+ return NF_DROP_ERR(-ECONNREFUSED);
+ }
+ }
+ if (selinux_conn_sid(sksec->sid, skb_sid, &peer_sid))
+ return NF_DROP;
+ secmark_perm = PACKET__SEND;
} else {
+ /* Locally generated packet, fetch the security label from the
+ * associated socket. */
struct sk_security_struct *sksec = sk->sk_security;
peer_sid = sksec->sid;
secmark_perm = PACKET__SEND;
@@ -4878,7 +4962,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
return NF_ACCEPT;
}
-static unsigned int selinux_ipv4_postroute(unsigned int hooknum,
+static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
@@ -4888,7 +4972,7 @@ static unsigned int selinux_ipv4_postroute(unsigned int hooknum,
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static unsigned int selinux_ipv6_postroute(unsigned int hooknum,
+static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
@@ -5504,11 +5588,11 @@ static int selinux_setprocattr(struct task_struct *p,
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and fail. */
ptsid = 0;
- task_lock(p);
+ rcu_read_lock();
tracer = ptrace_parent(p);
if (tracer)
ptsid = task_sid(tracer);
- task_unlock(p);
+ rcu_read_unlock();
if (tracer) {
error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index 92d0ab561db8..f53ee3c58d0f 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -130,7 +130,7 @@ static inline int avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd,
int result,
- struct common_audit_data *a, unsigned flags)
+ struct common_audit_data *a)
{
u32 audited, denied;
audited = avc_audit_required(requested, avd, result, 0, &denied);
@@ -138,7 +138,7 @@ static inline int avc_audit(u32 ssid, u32 tsid,
return 0;
return slow_avc_audit(ssid, tsid, tclass,
requested, audited, denied,
- a, flags);
+ a, 0);
}
#define AVC_STRICT 1 /* Ignore permissive mode. */
@@ -147,17 +147,9 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
unsigned flags,
struct av_decision *avd);
-int avc_has_perm_flags(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- struct common_audit_data *auditdata,
- unsigned);
-
-static inline int avc_has_perm(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- struct common_audit_data *auditdata)
-{
- return avc_has_perm_flags(ssid, tsid, tclass, requested, auditdata, 0);
-}
+int avc_has_perm(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct common_audit_data *auditdata);
u32 avc_policy_seqno(void);
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
index 0dec76c64cf5..48c3cc94c168 100644
--- a/security/selinux/include/xfrm.h
+++ b/security/selinux/include/xfrm.h
@@ -39,6 +39,7 @@ int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
struct common_audit_data *ad, u8 proto);
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
+int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid);
static inline void selinux_xfrm_notify_policyload(void)
{
@@ -79,11 +80,12 @@ static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid,
static inline void selinux_xfrm_notify_policyload(void)
{
}
-#endif
-static inline int selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid)
+static inline int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid)
{
- return selinux_xfrm_decode_session(skb, sid, 0);
+ *sid = SECSID_NULL;
+ return 0;
}
+#endif
#endif /* _SELINUX_XFRM_H_ */
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 855e464e92ef..332ac8a80cf5 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -116,6 +116,8 @@ static struct nlmsg_perm nlmsg_audit_perms[] =
{ AUDIT_MAKE_EQUIV, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ AUDIT_TTY_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
{ AUDIT_TTY_SET, NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT },
+ { AUDIT_GET_FEATURE, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_SET_FEATURE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
};
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index ee470a0b5c27..d106733ad987 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2334,50 +2334,16 @@ int security_fs_use(struct super_block *sb)
struct ocontext *c;
struct superblock_security_struct *sbsec = sb->s_security;
const char *fstype = sb->s_type->name;
- const char *subtype = (sb->s_subtype && sb->s_subtype[0]) ? sb->s_subtype : NULL;
- struct ocontext *base = NULL;
read_lock(&policy_rwlock);
- for (c = policydb.ocontexts[OCON_FSUSE]; c; c = c->next) {
- char *sub;
- int baselen;
-
- baselen = strlen(fstype);
-
- /* if base does not match, this is not the one */
- if (strncmp(fstype, c->u.name, baselen))
- continue;
-
- /* if there is no subtype, this is the one! */
- if (!subtype)
- break;
-
- /* skip past the base in this entry */
- sub = c->u.name + baselen;
-
- /* entry is only a base. save it. keep looking for subtype */
- if (sub[0] == '\0') {
- base = c;
- continue;
- }
-
- /* entry is not followed by a subtype, so it is not a match */
- if (sub[0] != '.')
- continue;
-
- /* whew, we found a subtype of this fstype */
- sub++; /* move past '.' */
-
- /* exact match of fstype AND subtype */
- if (!strcmp(subtype, sub))
+ c = policydb.ocontexts[OCON_FSUSE];
+ while (c) {
+ if (strcmp(fstype, c->u.name) == 0)
break;
+ c = c->next;
}
- /* in case we had found an fstype match but no subtype match */
- if (!c)
- c = base;
-
if (c) {
sbsec->behavior = c->v.behavior;
if (!c->sid[0]) {
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index a91d205ec0c6..0462cb3ff0a7 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -209,19 +209,26 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
NULL) ? 0 : 1);
}
-/*
- * LSM hook implementation that checks and/or returns the xfrm sid for the
- * incoming packet.
- */
-int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
+static u32 selinux_xfrm_skb_sid_egress(struct sk_buff *skb)
{
- u32 sid_session = SECSID_NULL;
- struct sec_path *sp;
+ struct dst_entry *dst = skb_dst(skb);
+ struct xfrm_state *x;
- if (skb == NULL)
- goto out;
+ if (dst == NULL)
+ return SECSID_NULL;
+ x = dst->xfrm;
+ if (x == NULL || !selinux_authorizable_xfrm(x))
+ return SECSID_NULL;
+
+ return x->security->ctx_sid;
+}
+
+static int selinux_xfrm_skb_sid_ingress(struct sk_buff *skb,
+ u32 *sid, int ckall)
+{
+ u32 sid_session = SECSID_NULL;
+ struct sec_path *sp = skb->sp;
- sp = skb->sp;
if (sp) {
int i;
@@ -248,6 +255,30 @@ out:
}
/*
+ * LSM hook implementation that checks and/or returns the xfrm sid for the
+ * incoming packet.
+ */
+int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
+{
+ if (skb == NULL) {
+ *sid = SECSID_NULL;
+ return 0;
+ }
+ return selinux_xfrm_skb_sid_ingress(skb, sid, ckall);
+}
+
+int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid)
+{
+ int rc;
+
+ rc = selinux_xfrm_skb_sid_ingress(skb, sid, 0);
+ if (rc == 0 && *sid == SECSID_NULL)
+ *sid = selinux_xfrm_skb_sid_egress(skb);
+
+ return rc;
+}
+
+/*
* LSM hook implementation that allocs and transfers uctx spec to xfrm_policy.
*/
int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
@@ -327,19 +358,22 @@ int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x,
return rc;
ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC);
- if (!ctx)
- return -ENOMEM;
+ if (!ctx) {
+ rc = -ENOMEM;
+ goto out;
+ }
ctx->ctx_doi = XFRM_SC_DOI_LSM;
ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
ctx->ctx_sid = secid;
ctx->ctx_len = str_len;
memcpy(ctx->ctx_str, ctx_str, str_len);
- kfree(ctx_str);
x->security = ctx;
atomic_inc(&selinux_xfrm_refcount);
- return 0;
+out:
+ kfree(ctx_str);
+ return rc;
}
/*