2
* Implementation of the SID table type.
4
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
6
#include <linux/kernel.h>
7
#include <linux/slab.h>
8
#include <linux/spinlock.h>
9
#include <linux/errno.h>
14
#define SIDTAB_HASH(sid) \
15
(sid & SIDTAB_HASH_MASK)
17
int sidtab_init(struct sidtab *s)
21
s->htable = kmalloc(sizeof(*(s->htable)) * SIDTAB_SIZE, GFP_ATOMIC);
24
for (i = 0; i < SIDTAB_SIZE; i++)
29
spin_lock_init(&s->lock);
33
int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
36
struct sidtab_node *prev, *cur, *newnode;
43
hvalue = SIDTAB_HASH(sid);
45
cur = s->htable[hvalue];
46
while (cur && sid > cur->sid) {
51
if (cur && sid == cur->sid) {
56
newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC);
57
if (newnode == NULL) {
62
if (context_cpy(&newnode->context, context)) {
69
newnode->next = prev->next;
73
newnode->next = s->htable[hvalue];
75
s->htable[hvalue] = newnode;
79
if (sid >= s->next_sid)
80
s->next_sid = sid + 1;
85
static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
88
struct sidtab_node *cur;
93
hvalue = SIDTAB_HASH(sid);
94
cur = s->htable[hvalue];
95
while (cur && sid > cur->sid)
98
if (force && cur && sid == cur->sid && cur->context.len)
101
if (cur == NULL || sid != cur->sid || cur->context.len) {
102
/* Remap invalid SIDs to the unlabeled SID. */
103
sid = SECINITSID_UNLABELED;
104
hvalue = SIDTAB_HASH(sid);
105
cur = s->htable[hvalue];
106
while (cur && sid > cur->sid)
108
if (!cur || sid != cur->sid)
112
return &cur->context;
115
struct context *sidtab_search(struct sidtab *s, u32 sid)
117
return sidtab_search_core(s, sid, 0);
120
struct context *sidtab_search_force(struct sidtab *s, u32 sid)
122
return sidtab_search_core(s, sid, 1);
125
int sidtab_map(struct sidtab *s,
126
int (*apply) (u32 sid,
127
struct context *context,
132
struct sidtab_node *cur;
137
for (i = 0; i < SIDTAB_SIZE; i++) {
140
rc = apply(cur->sid, &cur->context, args);
150
static void sidtab_update_cache(struct sidtab *s, struct sidtab_node *n, int loc)
152
BUG_ON(loc >= SIDTAB_CACHE_LEN);
155
s->cache[loc] = s->cache[loc - 1];
161
static inline u32 sidtab_search_context(struct sidtab *s,
162
struct context *context)
165
struct sidtab_node *cur;
167
for (i = 0; i < SIDTAB_SIZE; i++) {
170
if (context_cmp(&cur->context, context)) {
171
sidtab_update_cache(s, cur, SIDTAB_CACHE_LEN - 1);
180
static inline u32 sidtab_search_cache(struct sidtab *s, struct context *context)
183
struct sidtab_node *node;
185
for (i = 0; i < SIDTAB_CACHE_LEN; i++) {
189
if (context_cmp(&node->context, context)) {
190
sidtab_update_cache(s, node, i);
197
int sidtab_context_to_sid(struct sidtab *s,
198
struct context *context,
205
*out_sid = SECSID_NULL;
207
sid = sidtab_search_cache(s, context);
209
sid = sidtab_search_context(s, context);
211
spin_lock_irqsave(&s->lock, flags);
212
/* Rescan now that we hold the lock. */
213
sid = sidtab_search_context(s, context);
216
/* No SID exists for the context. Allocate a new one. */
217
if (s->next_sid == UINT_MAX || s->shutdown) {
224
"SELinux: Context %s is not valid (left unmapped).\n",
226
ret = sidtab_insert(s, sid, context);
230
spin_unlock_irqrestore(&s->lock, flags);
240
void sidtab_hash_eval(struct sidtab *h, char *tag)
242
int i, chain_len, slots_used, max_chain_len;
243
struct sidtab_node *cur;
247
for (i = 0; i < SIDTAB_SIZE; i++) {
257
if (chain_len > max_chain_len)
258
max_chain_len = chain_len;
262
printk(KERN_DEBUG "%s: %d entries and %d/%d buckets used, longest "
263
"chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE,
267
void sidtab_destroy(struct sidtab *s)
270
struct sidtab_node *cur, *temp;
275
for (i = 0; i < SIDTAB_SIZE; i++) {
280
context_destroy(&temp->context);
291
void sidtab_set(struct sidtab *dst, struct sidtab *src)
296
spin_lock_irqsave(&src->lock, flags);
297
dst->htable = src->htable;
299
dst->next_sid = src->next_sid;
301
for (i = 0; i < SIDTAB_CACHE_LEN; i++)
302
dst->cache[i] = NULL;
303
spin_unlock_irqrestore(&src->lock, flags);
306
void sidtab_shutdown(struct sidtab *s)
310
spin_lock_irqsave(&s->lock, flags);
312
spin_unlock_irqrestore(&s->lock, flags);