mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 17:57:38 -04:00
Merge to v3.13-rc7 for prerequisite changes in the Xen code for TPM
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
|
||||
|
||||
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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +21,19 @@
|
||||
#include <crypto/hash_info.h>
|
||||
#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
|
||||
*/
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 @@ static void key_garbage_collector(struct work_struct *work)
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 @@ static bool search_nested_keyrings(struct key *keyring,
|
||||
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]");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 @@ static int selinux_set_mnt_opts(struct super_block *sb,
|
||||
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 @@ static int selinux_sb_remount(struct super_block *sb, void *data)
|
||||
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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid)
|
||||
{
|
||||
*sid = SECSID_NULL;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid)
|
||||
{
|
||||
return selinux_xfrm_decode_session(skb, sid, 0);
|
||||
}
|
||||
|
||||
#endif /* _SELINUX_XFRM_H_ */
|
||||
|
||||
@@ -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 },
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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]) {
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
struct dst_entry *dst = skb_dst(skb);
|
||||
struct xfrm_state *x;
|
||||
|
||||
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;
|
||||
struct sec_path *sp = skb->sp;
|
||||
|
||||
if (skb == NULL)
|
||||
goto out;
|
||||
|
||||
sp = skb->sp;
|
||||
if (sp) {
|
||||
int i;
|
||||
|
||||
@@ -247,6 +254,30 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user