Commit f41030a2 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'selinux-pr-20200601' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux

Pull SELinux updates from Paul Moore:
 "The highlights:

   - A number of improvements to various SELinux internal data
     structures to help improve performance. We move the role
     transitions into a hash table. In the content structure we shift
     from hashing the content string (aka SELinux label) to the
     structure itself, when it is valid. This last change not only
     offers a speedup, but it helps us simplify the code some as well.

   - Add a new SELinux policy version which allows for a more space
     efficient way of storing the filename transitions in the binary
     policy. Given the default Fedora SELinux policy with the unconfined
     module enabled, this change drops the policy size from ~7.6MB to
     ~3.3MB. The kernel policy load time dropped as well.

   - Some fixes to the error handling code in the policy parser to
     properly return error codes when things go wrong"

* tag 'selinux-pr-20200601' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  selinux: netlabel: Remove unused inline function
  selinux: do not allocate hashtabs dynamically
  selinux: fix return value on error in policydb_read()
  selinux: simplify range_write()
  selinux: fix error return code in policydb_read()
  selinux: don't produce incorrect filename_trans_count
  selinux: implement new format of filename transitions
  selinux: move context hashing under sidtab
  selinux: hash context structure directly
  selinux: store role transitions in a hash table
  selinux: drop unnecessary smp_load_acquire() call
  selinux: fix warning Comparison to bool
parents 9d99b164 fe5a90b8
......@@ -8,7 +8,7 @@ obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \
netnode.o netport.o status.o \
ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \
ss/policydb.o ss/services.o ss/conditional.o ss/mls.o
ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/context.o
selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
......
......@@ -98,12 +98,6 @@ static inline int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
return 0;
}
static inline int selinux_netlbl_conn_setsid(struct sock *sk,
struct sockaddr *addr)
{
return 0;
}
static inline int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep,
struct sk_buff *skb)
{
......
......@@ -41,10 +41,11 @@
#define POLICYDB_VERSION_XPERMS_IOCTL 30
#define POLICYDB_VERSION_INFINIBAND 31
#define POLICYDB_VERSION_GLBLUB 32
#define POLICYDB_VERSION_COMP_FTRANS 33 /* compressed filename transitions */
/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_GLBLUB
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_COMP_FTRANS
/* Mask for just the mount related flags */
#define SE_MNTMASK 0x0f
......
// SPDX-License-Identifier: GPL-2.0
/*
* Implementations of the security context functions.
*
* Author: Ondrej Mosnacek <omosnacek@gmail.com>
* Copyright (C) 2020 Red Hat, Inc.
*/
#include <linux/jhash.h>
#include "context.h"
#include "mls.h"
u32 context_compute_hash(const struct context *c)
{
u32 hash = 0;
/*
* If a context is invalid, it will always be represented by a
* context struct with only the len & str set (and vice versa)
* under a given policy. Since context structs from different
* policies should never meet, it is safe to hash valid and
* invalid contexts differently. The context_cmp() function
* already operates under the same assumption.
*/
if (c->len)
return full_name_hash(NULL, c->str, c->len);
hash = jhash_3words(c->user, c->role, c->type, hash);
hash = mls_range_hash(&c->range, hash);
return hash;
}
......@@ -31,7 +31,6 @@ struct context {
u32 len; /* length of string in bytes */
struct mls_range range;
char *str; /* string representation if context cannot be mapped. */
u32 hash; /* a hash of the string representation */
};
static inline void mls_context_init(struct context *c)
......@@ -169,13 +168,12 @@ static inline int context_cpy(struct context *dst, struct context *src)
kfree(dst->str);
return rc;
}
dst->hash = src->hash;
return 0;
}
static inline void context_destroy(struct context *c)
{
c->user = c->role = c->type = c->hash = 0;
c->user = c->role = c->type = 0;
kfree(c->str);
c->str = NULL;
c->len = 0;
......@@ -184,8 +182,6 @@ static inline void context_destroy(struct context *c)
static inline int context_cmp(struct context *c1, struct context *c2)
{
if (c1->hash && c2->hash && (c1->hash != c2->hash))
return 0;
if (c1->len && c2->len)
return (c1->len == c2->len && !strcmp(c1->str, c2->str));
if (c1->len || c2->len)
......@@ -196,10 +192,7 @@ static inline int context_cmp(struct context *c1, struct context *c2)
mls_context_cmp(c1, c2));
}
static inline unsigned int context_compute_hash(const char *s)
{
return full_name_hash(NULL, s, strlen(s));
}
u32 context_compute_hash(const struct context *c);
#endif /* _SS_CONTEXT_H_ */
......@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/jhash.h>
#include <net/netlabel.h>
#include "ebitmap.h"
#include "policydb.h"
......@@ -542,6 +543,19 @@ int ebitmap_write(struct ebitmap *e, void *fp)
return 0;
}
u32 ebitmap_hash(const struct ebitmap *e, u32 hash)
{
struct ebitmap_node *node;
/* need to change hash even if ebitmap is empty */
hash = jhash_1word(e->highbit, hash);
for (node = e->node; node; node = node->next) {
hash = jhash_1word(node->startbit, hash);
hash = jhash(node->maps, sizeof(node->maps), hash);
}
return hash;
}
void __init ebitmap_cache_init(void)
{
ebitmap_node_cachep = kmem_cache_create("ebitmap_node",
......
......@@ -131,6 +131,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value);
void ebitmap_destroy(struct ebitmap *e);
int ebitmap_read(struct ebitmap *e, void *fp);
int ebitmap_write(struct ebitmap *e, void *fp);
u32 ebitmap_hash(const struct ebitmap *e, u32 hash);
#ifdef CONFIG_NETLABEL
int ebitmap_netlbl_export(struct ebitmap *ebmap,
......
......@@ -29,34 +29,21 @@ static u32 hashtab_compute_size(u32 nel)
return nel == 0 ? 0 : roundup_pow_of_two(nel);
}
struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key),
int (*keycmp)(struct hashtab *h, const void *key1, const void *key2),
u32 nel_hint)
int hashtab_init(struct hashtab *h,
u32 (*hash_value)(struct hashtab *h, const void *key),
int (*keycmp)(struct hashtab *h, const void *key1,
const void *key2),
u32 nel_hint)
{
struct hashtab *p;
u32 i, size = hashtab_compute_size(nel_hint);
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p)
return p;
p->size = size;
p->nel = 0;
p->hash_value = hash_value;
p->keycmp = keycmp;
if (!size)
return p;
p->htable = kmalloc_array(size, sizeof(*p->htable), GFP_KERNEL);
if (!p->htable) {
kfree(p);
return NULL;
}
for (i = 0; i < size; i++)
p->htable[i] = NULL;
h->size = hashtab_compute_size(nel_hint);
h->nel = 0;
h->hash_value = hash_value;
h->keycmp = keycmp;
if (!h->size)
return 0;
return p;
h->htable = kcalloc(h->size, sizeof(*h->htable), GFP_KERNEL);
return h->htable ? 0 : -ENOMEM;
}
int hashtab_insert(struct hashtab *h, void *key, void *datum)
......@@ -66,7 +53,7 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum)
cond_resched();
if (!h || !h->size || h->nel == HASHTAB_MAX_NODES)
if (!h->size || h->nel == HASHTAB_MAX_NODES)
return -EINVAL;
hvalue = h->hash_value(h, key);
......@@ -102,7 +89,7 @@ void *hashtab_search(struct hashtab *h, const void *key)
u32 hvalue;
struct hashtab_node *cur;
if (!h || !h->size)
if (!h->size)
return NULL;
hvalue = h->hash_value(h, key);
......@@ -121,9 +108,6 @@ void hashtab_destroy(struct hashtab *h)
u32 i;
struct hashtab_node *cur, *temp;
if (!h)
return;
for (i = 0; i < h->size; i++) {
cur = h->htable[i];
while (cur) {
......@@ -136,8 +120,6 @@ void hashtab_destroy(struct hashtab *h)
kfree(h->htable);
h->htable = NULL;
kfree(h);
}
int hashtab_map(struct hashtab *h,
......@@ -148,9 +130,6 @@ int hashtab_map(struct hashtab *h,
int ret;
struct hashtab_node *cur;
if (!h)
return 0;
for (i = 0; i < h->size; i++) {
cur = h->htable[i];
while (cur) {
......
......@@ -35,14 +35,15 @@ struct hashtab_info {
};
/*
* Creates a new hash table with the specified characteristics.
* Initializes a new hash table with the specified characteristics.
*
* Returns NULL if insufficent space is available or
* the new hash table otherwise.
* Returns -ENOMEM if insufficient space is available or 0 otherwise.
*/
struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key),
int (*keycmp)(struct hashtab *h, const void *key1, const void *key2),
u32 nel_hint);
int hashtab_init(struct hashtab *h,
u32 (*hash_value)(struct hashtab *h, const void *key),
int (*keycmp)(struct hashtab *h, const void *key1,
const void *key2),
u32 nel_hint);
/*
* Inserts the specified (key, datum) pair into the specified hash table.
......
......@@ -165,7 +165,7 @@ int mls_level_isvalid(struct policydb *p, struct mls_level *l)
if (!l->sens || l->sens > p->p_levels.nprim)
return 0;
levdatum = hashtab_search(p->p_levels.table,
levdatum = hashtab_search(&p->p_levels.table,
sym_name(p, SYM_LEVELS, l->sens - 1));
if (!levdatum)
return 0;
......@@ -293,7 +293,7 @@ int mls_context_to_sid(struct policydb *pol,
*(next_cat++) = '\0';
/* Parse sensitivity. */
levdatum = hashtab_search(pol->p_levels.table, sensitivity);
levdatum = hashtab_search(&pol->p_levels.table, sensitivity);
if (!levdatum)
return -EINVAL;
context->range.level[l].sens = levdatum->level->sens;
......@@ -312,7 +312,7 @@ int mls_context_to_sid(struct policydb *pol,
*rngptr++ = '\0';
}
catdatum = hashtab_search(pol->p_cats.table, cur_cat);
catdatum = hashtab_search(&pol->p_cats.table, cur_cat);
if (!catdatum)
return -EINVAL;
......@@ -325,7 +325,7 @@ int mls_context_to_sid(struct policydb *pol,
if (rngptr == NULL)
continue;
rngdatum = hashtab_search(pol->p_cats.table, rngptr);
rngdatum = hashtab_search(&pol->p_cats.table, rngptr);
if (!rngdatum)
return -EINVAL;
......@@ -458,7 +458,7 @@ int mls_convert_context(struct policydb *oldp,
return 0;
for (l = 0; l < 2; l++) {
levdatum = hashtab_search(newp->p_levels.table,
levdatum = hashtab_search(&newp->p_levels.table,
sym_name(oldp, SYM_LEVELS,
oldc->range.level[l].sens - 1));
......@@ -470,7 +470,7 @@ int mls_convert_context(struct policydb *oldp,
node, i) {
int rc;
catdatum = hashtab_search(newp->p_cats.table,
catdatum = hashtab_search(&newp->p_cats.table,
sym_name(oldp, SYM_CATS, i));
if (!catdatum)
return -EINVAL;
......@@ -506,7 +506,7 @@ int mls_compute_sid(struct policydb *p,
rtr.source_type = scontext->type;
rtr.target_type = tcontext->type;
rtr.target_class = tclass;
r = hashtab_search(p->range_tr, &rtr);
r = hashtab_search(&p->range_tr, &rtr);
if (r)
return mls_range_set(newcontext, r);
......@@ -536,7 +536,7 @@ int mls_compute_sid(struct policydb *p,
/* Fallthrough */
case AVTAB_CHANGE:
if ((tclass == p->process_class) || (sock == true))
if ((tclass == p->process_class) || sock)
/* Use the process MLS attributes. */
return mls_context_cpy(newcontext, scontext);
else
......
......@@ -22,7 +22,10 @@
#ifndef _SS_MLS_H_
#define _SS_MLS_H_
#include <linux/jhash.h>
#include "context.h"
#include "ebitmap.h"
#include "policydb.h"
int mls_compute_context_len(struct policydb *p, struct context *context);
......@@ -101,5 +104,13 @@ static inline int mls_import_netlbl_cat(struct policydb *p,
}
#endif
static inline u32 mls_range_hash(const struct mls_range *r, u32 hash)
{
hash = jhash_2words(r->level[0].sens, r->level[1].sens, hash);
hash = ebitmap_hash(&r->level[0].cat, hash);
hash = ebitmap_hash(&r->level[1].cat, hash);
return hash;
}
#endif /* _SS_MLS_H */
This diff is collapsed.
......@@ -81,12 +81,14 @@ struct role_datum {
struct ebitmap types; /* set of authorized types for role */
};
struct role_trans {
struct role_trans_key {
u32 role; /* current role */
u32 type; /* program executable type, or new object type */
u32 tclass; /* process class, or new object class */
};
struct role_trans_datum {
u32 new_role; /* new role */
struct role_trans *next;
};
struct filename_trans_key {
......@@ -261,14 +263,15 @@ struct policydb {
struct avtab te_avtab;
/* role transitions */
struct role_trans *role_tr;
struct hashtab role_tr;
/* file transitions with the last path component */
/* quickly exclude lookups when parent ttype has no rules */
struct ebitmap filename_trans_ttypes;
/* actual set of filename_trans rules */
struct hashtab *filename_trans;
u32 filename_trans_count;
struct hashtab filename_trans;
/* only used if policyvers < POLICYDB_VERSION_COMP_FTRANS */
u32 compat_filename_trans_count;
/* bools indexed by (value - 1) */
struct cond_bool_datum **bool_val_to_struct;
......@@ -291,7 +294,7 @@ struct policydb {
struct genfs *genfs;
/* range transitions table (range_trans_key -> mls_range) */
struct hashtab *range_tr;
struct hashtab range_tr;
/* type -> attribute reverse mapping */
struct ebitmap *type_attr_map_array;
......
This diff is collapsed.
......@@ -8,7 +8,6 @@
#define _SS_SERVICES_H_
#include "policydb.h"
#include "context.h"
/* Mapping for a single class */
struct selinux_mapping {
......@@ -37,6 +36,4 @@ void services_compute_xperms_drivers(struct extended_perms *xperms,
void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
struct avtab_node *node);
int context_add_hash(struct policydb *policydb, struct context *context);
#endif /* _SS_SERVICES_H_ */
......@@ -54,14 +54,15 @@ int sidtab_init(struct sidtab *s)
return 0;
}
static u32 context_to_sid(struct sidtab *s, struct context *context)
static u32 context_to_sid(struct sidtab *s, struct context *context, u32 hash)
{
struct sidtab_entry *entry;
u32 sid = 0;
rcu_read_lock();
hash_for_each_possible_rcu(s->context_to_sid, entry, list,
context->hash) {
hash_for_each_possible_rcu(s->context_to_sid, entry, list, hash) {
if (entry->hash != hash)
continue;
if (context_cmp(&entry->context, context)) {
sid = entry->sid;
break;
......@@ -74,6 +75,7 @@ static u32 context_to_sid(struct sidtab *s, struct context *context)
int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
{
struct sidtab_isid_entry *isid;
u32 hash;
int rc;
if (sid == 0 || sid > SECINITSID_NUM)
......@@ -90,15 +92,18 @@ int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
#endif
isid->set = 1;
hash = context_compute_hash(context);
/*
* Multiple initial sids may map to the same context. Check that this
* context is not already represented in the context_to_sid hashtable
* to avoid duplicate entries and long linked lists upon hash
* collision.
*/
if (!context_to_sid(s, context)) {
if (!context_to_sid(s, context, hash)) {
isid->entry.sid = sid;
hash_add(s->context_to_sid, &isid->entry.list, context->hash);
isid->entry.hash = hash;
hash_add(s->context_to_sid, &isid->entry.list, hash);
}
return 0;
......@@ -259,12 +264,12 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context,
u32 *sid)
{
unsigned long flags;
u32 count;
u32 count, hash = context_compute_hash(context);
struct sidtab_convert_params *convert;
struct sidtab_entry *dst, *dst_convert;
int rc;
*sid = context_to_sid(s, context);
*sid = context_to_sid(s, context, hash);
if (*sid)
return 0;
......@@ -272,12 +277,11 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context,
spin_lock_irqsave(&s->lock, flags);
rc = 0;
*sid = context_to_sid(s, context);
*sid = context_to_sid(s, context, hash);
if (*sid)
goto out_unlock;
/* read entries only after reading count */
count = smp_load_acquire(&s->count);
count = s->count;
convert = s->convert;
/* bail out if we already reached max entries */
......@@ -292,6 +296,7 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context,
goto out_unlock;
dst->sid = index_to_sid(count);
dst->hash = hash;
rc = context_cpy(&dst->context, context);
if (rc)
......@@ -316,10 +321,11 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context,
goto out_unlock;
}
dst_convert->sid = index_to_sid(count);
dst_convert->hash = context_compute_hash(&dst_convert->context);
convert->target->count = count + 1;
hash_add_rcu(convert->target->context_to_sid,
&dst_convert->list, dst_convert->context.hash);
&dst_convert->list, dst_convert->hash);
}
if (context->len)
......@@ -330,7 +336,7 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context,
/* write entries before updating count */
smp_store_release(&s->count, count + 1);
hash_add_rcu(s->context_to_sid, &dst->list, dst->context.hash);
hash_add_rcu(s->context_to_sid, &dst->list, dst->hash);
rc = 0;
out_unlock:
......@@ -346,10 +352,9 @@ static void sidtab_convert_hashtable(struct sidtab *s, u32 count)
for (i = 0; i < count; i++) {
entry = sidtab_do_lookup(s, i, 0);
entry->sid = index_to_sid(i);
entry->hash = context_compute_hash(&entry->context);
hash_add_rcu(s->context_to_sid, &entry->list,
entry->context.hash);
hash_add_rcu(s->context_to_sid, &entry->list, entry->hash);
}
}
......
......@@ -19,6 +19,7 @@
struct sidtab_entry {
u32 sid;
u32 hash;
struct context context;
#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
struct sidtab_str_cache __rcu *cache;
......
......@@ -35,10 +35,7 @@ static int symcmp(struct hashtab *h, const void *key1, const void *key2)
int symtab_init(struct symtab *s, unsigned int size)
{
s->table = hashtab_create(symhash, symcmp, size);
if (!s->table)
return -ENOMEM;
s->nprim = 0;
return 0;
return hashtab_init(&s->table, symhash, symcmp, size);
}
......@@ -13,7 +13,7 @@
#include "hashtab.h"
struct symtab {
struct hashtab *table; /* hash table (keyed on a string) */
struct hashtab table; /* hash table (keyed on a string) */
u32 nprim; /* number of primary names in table */
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment