sidtab.c revision 12b29f34558b9b45a2c6eabd4f3c6be939a3980f
1/* 2 * Implementation of the SID table type. 3 * 4 * Author : Stephen Smalley, <sds@epoch.ncsc.mil> 5 */ 6#include <linux/kernel.h> 7#include <linux/slab.h> 8#include <linux/spinlock.h> 9#include <linux/errno.h> 10#include "flask.h" 11#include "security.h" 12#include "sidtab.h" 13 14#define SIDTAB_HASH(sid) \ 15(sid & SIDTAB_HASH_MASK) 16 17#define INIT_SIDTAB_LOCK(s) spin_lock_init(&s->lock) 18#define SIDTAB_LOCK(s, x) spin_lock_irqsave(&s->lock, x) 19#define SIDTAB_UNLOCK(s, x) spin_unlock_irqrestore(&s->lock, x) 20 21int sidtab_init(struct sidtab *s) 22{ 23 int i; 24 25 s->htable = kmalloc(sizeof(*(s->htable)) * SIDTAB_SIZE, GFP_ATOMIC); 26 if (!s->htable) 27 return -ENOMEM; 28 for (i = 0; i < SIDTAB_SIZE; i++) 29 s->htable[i] = NULL; 30 s->nel = 0; 31 s->next_sid = 1; 32 s->shutdown = 0; 33 INIT_SIDTAB_LOCK(s); 34 return 0; 35} 36 37int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) 38{ 39 int hvalue, rc = 0; 40 struct sidtab_node *prev, *cur, *newnode; 41 42 if (!s) { 43 rc = -ENOMEM; 44 goto out; 45 } 46 47 hvalue = SIDTAB_HASH(sid); 48 prev = NULL; 49 cur = s->htable[hvalue]; 50 while (cur != NULL && sid > cur->sid) { 51 prev = cur; 52 cur = cur->next; 53 } 54 55 if (cur && sid == cur->sid) { 56 rc = -EEXIST; 57 goto out; 58 } 59 60 newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC); 61 if (newnode == NULL) { 62 rc = -ENOMEM; 63 goto out; 64 } 65 newnode->sid = sid; 66 if (context_cpy(&newnode->context, context)) { 67 kfree(newnode); 68 rc = -ENOMEM; 69 goto out; 70 } 71 72 if (prev) { 73 newnode->next = prev->next; 74 wmb(); 75 prev->next = newnode; 76 } else { 77 newnode->next = s->htable[hvalue]; 78 wmb(); 79 s->htable[hvalue] = newnode; 80 } 81 82 s->nel++; 83 if (sid >= s->next_sid) 84 s->next_sid = sid + 1; 85out: 86 return rc; 87} 88 89static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) 90{ 91 int hvalue; 92 struct sidtab_node *cur; 93 94 if (!s) 95 return NULL; 96 97 hvalue = SIDTAB_HASH(sid); 98 cur = s->htable[hvalue]; 99 while (cur != NULL && sid > cur->sid) 100 cur = cur->next; 101 102 if (force && cur && sid == cur->sid && cur->context.len) 103 return &cur->context; 104 105 if (cur == NULL || sid != cur->sid || cur->context.len) { 106 /* Remap invalid SIDs to the unlabeled SID. */ 107 sid = SECINITSID_UNLABELED; 108 hvalue = SIDTAB_HASH(sid); 109 cur = s->htable[hvalue]; 110 while (cur != NULL && sid > cur->sid) 111 cur = cur->next; 112 if (!cur || sid != cur->sid) 113 return NULL; 114 } 115 116 return &cur->context; 117} 118 119struct context *sidtab_search(struct sidtab *s, u32 sid) 120{ 121 return sidtab_search_core(s, sid, 0); 122} 123 124struct context *sidtab_search_force(struct sidtab *s, u32 sid) 125{ 126 return sidtab_search_core(s, sid, 1); 127} 128 129int sidtab_map(struct sidtab *s, 130 int (*apply) (u32 sid, 131 struct context *context, 132 void *args), 133 void *args) 134{ 135 int i, rc = 0; 136 struct sidtab_node *cur; 137 138 if (!s) 139 goto out; 140 141 for (i = 0; i < SIDTAB_SIZE; i++) { 142 cur = s->htable[i]; 143 while (cur != NULL) { 144 rc = apply(cur->sid, &cur->context, args); 145 if (rc) 146 goto out; 147 cur = cur->next; 148 } 149 } 150out: 151 return rc; 152} 153 154static inline u32 sidtab_search_context(struct sidtab *s, 155 struct context *context) 156{ 157 int i; 158 struct sidtab_node *cur; 159 160 for (i = 0; i < SIDTAB_SIZE; i++) { 161 cur = s->htable[i]; 162 while (cur != NULL) { 163 if (context_cmp(&cur->context, context)) 164 return cur->sid; 165 cur = cur->next; 166 } 167 } 168 return 0; 169} 170 171int sidtab_context_to_sid(struct sidtab *s, 172 struct context *context, 173 u32 *out_sid) 174{ 175 u32 sid; 176 int ret = 0; 177 unsigned long flags; 178 179 *out_sid = SECSID_NULL; 180 181 sid = sidtab_search_context(s, context); 182 if (!sid) { 183 SIDTAB_LOCK(s, flags); 184 /* Rescan now that we hold the lock. */ 185 sid = sidtab_search_context(s, context); 186 if (sid) 187 goto unlock_out; 188 /* No SID exists for the context. Allocate a new one. */ 189 if (s->next_sid == UINT_MAX || s->shutdown) { 190 ret = -ENOMEM; 191 goto unlock_out; 192 } 193 sid = s->next_sid++; 194 if (context->len) 195 printk(KERN_INFO 196 "SELinux: Context %s is not valid (left unmapped).\n", 197 context->str); 198 ret = sidtab_insert(s, sid, context); 199 if (ret) 200 s->next_sid--; 201unlock_out: 202 SIDTAB_UNLOCK(s, flags); 203 } 204 205 if (ret) 206 return ret; 207 208 *out_sid = sid; 209 return 0; 210} 211 212void sidtab_hash_eval(struct sidtab *h, char *tag) 213{ 214 int i, chain_len, slots_used, max_chain_len; 215 struct sidtab_node *cur; 216 217 slots_used = 0; 218 max_chain_len = 0; 219 for (i = 0; i < SIDTAB_SIZE; i++) { 220 cur = h->htable[i]; 221 if (cur) { 222 slots_used++; 223 chain_len = 0; 224 while (cur) { 225 chain_len++; 226 cur = cur->next; 227 } 228 229 if (chain_len > max_chain_len) 230 max_chain_len = chain_len; 231 } 232 } 233 234 printk(KERN_DEBUG "%s: %d entries and %d/%d buckets used, longest " 235 "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE, 236 max_chain_len); 237} 238 239void sidtab_destroy(struct sidtab *s) 240{ 241 int i; 242 struct sidtab_node *cur, *temp; 243 244 if (!s) 245 return; 246 247 for (i = 0; i < SIDTAB_SIZE; i++) { 248 cur = s->htable[i]; 249 while (cur != NULL) { 250 temp = cur; 251 cur = cur->next; 252 context_destroy(&temp->context); 253 kfree(temp); 254 } 255 s->htable[i] = NULL; 256 } 257 kfree(s->htable); 258 s->htable = NULL; 259 s->nel = 0; 260 s->next_sid = 1; 261} 262 263void sidtab_set(struct sidtab *dst, struct sidtab *src) 264{ 265 unsigned long flags; 266 267 SIDTAB_LOCK(src, flags); 268 dst->htable = src->htable; 269 dst->nel = src->nel; 270 dst->next_sid = src->next_sid; 271 dst->shutdown = 0; 272 SIDTAB_UNLOCK(src, flags); 273} 274 275void sidtab_shutdown(struct sidtab *s) 276{ 277 unsigned long flags; 278 279 SIDTAB_LOCK(s, flags); 280 s->shutdown = 1; 281 SIDTAB_UNLOCK(s, flags); 282} 283