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 21 int 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 37 int 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; 85 out: 86 return rc; 87 } 88 89 struct context *sidtab_search(struct sidtab *s, u32 sid) 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 (cur == NULL || sid != cur->sid) { 103 /* Remap invalid SIDs to the unlabeled SID. */ 104 sid = SECINITSID_UNLABELED; 105 hvalue = SIDTAB_HASH(sid); 106 cur = s->htable[hvalue]; 107 while (cur != NULL && sid > cur->sid) 108 cur = cur->next; 109 if (!cur || sid != cur->sid) 110 return NULL; 111 } 112 113 return &cur->context; 114 } 115 116 int sidtab_map(struct sidtab *s, 117 int (*apply) (u32 sid, 118 struct context *context, 119 void *args), 120 void *args) 121 { 122 int i, rc = 0; 123 struct sidtab_node *cur; 124 125 if (!s) 126 goto out; 127 128 for (i = 0; i < SIDTAB_SIZE; i++) { 129 cur = s->htable[i]; 130 while (cur != NULL) { 131 rc = apply(cur->sid, &cur->context, args); 132 if (rc) 133 goto out; 134 cur = cur->next; 135 } 136 } 137 out: 138 return rc; 139 } 140 141 void sidtab_map_remove_on_error(struct sidtab *s, 142 int (*apply) (u32 sid, 143 struct context *context, 144 void *args), 145 void *args) 146 { 147 int i, ret; 148 struct sidtab_node *last, *cur, *temp; 149 150 if (!s) 151 return; 152 153 for (i = 0; i < SIDTAB_SIZE; i++) { 154 last = NULL; 155 cur = s->htable[i]; 156 while (cur != NULL) { 157 ret = apply(cur->sid, &cur->context, args); 158 if (ret) { 159 if (last) { 160 last->next = cur->next; 161 } else { 162 s->htable[i] = cur->next; 163 } 164 165 temp = cur; 166 cur = cur->next; 167 context_destroy(&temp->context); 168 kfree(temp); 169 s->nel--; 170 } else { 171 last = cur; 172 cur = cur->next; 173 } 174 } 175 } 176 177 return; 178 } 179 180 static inline u32 sidtab_search_context(struct sidtab *s, 181 struct context *context) 182 { 183 int i; 184 struct sidtab_node *cur; 185 186 for (i = 0; i < SIDTAB_SIZE; i++) { 187 cur = s->htable[i]; 188 while (cur != NULL) { 189 if (context_cmp(&cur->context, context)) 190 return cur->sid; 191 cur = cur->next; 192 } 193 } 194 return 0; 195 } 196 197 int sidtab_context_to_sid(struct sidtab *s, 198 struct context *context, 199 u32 *out_sid) 200 { 201 u32 sid; 202 int ret = 0; 203 unsigned long flags; 204 205 *out_sid = SECSID_NULL; 206 207 sid = sidtab_search_context(s, context); 208 if (!sid) { 209 SIDTAB_LOCK(s, flags); 210 /* Rescan now that we hold the lock. */ 211 sid = sidtab_search_context(s, context); 212 if (sid) 213 goto unlock_out; 214 /* No SID exists for the context. Allocate a new one. */ 215 if (s->next_sid == UINT_MAX || s->shutdown) { 216 ret = -ENOMEM; 217 goto unlock_out; 218 } 219 sid = s->next_sid++; 220 ret = sidtab_insert(s, sid, context); 221 if (ret) 222 s->next_sid--; 223 unlock_out: 224 SIDTAB_UNLOCK(s, flags); 225 } 226 227 if (ret) 228 return ret; 229 230 *out_sid = sid; 231 return 0; 232 } 233 234 void sidtab_hash_eval(struct sidtab *h, char *tag) 235 { 236 int i, chain_len, slots_used, max_chain_len; 237 struct sidtab_node *cur; 238 239 slots_used = 0; 240 max_chain_len = 0; 241 for (i = 0; i < SIDTAB_SIZE; i++) { 242 cur = h->htable[i]; 243 if (cur) { 244 slots_used++; 245 chain_len = 0; 246 while (cur) { 247 chain_len++; 248 cur = cur->next; 249 } 250 251 if (chain_len > max_chain_len) 252 max_chain_len = chain_len; 253 } 254 } 255 256 printk(KERN_DEBUG "%s: %d entries and %d/%d buckets used, longest " 257 "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE, 258 max_chain_len); 259 } 260 261 void sidtab_destroy(struct sidtab *s) 262 { 263 int i; 264 struct sidtab_node *cur, *temp; 265 266 if (!s) 267 return; 268 269 for (i = 0; i < SIDTAB_SIZE; i++) { 270 cur = s->htable[i]; 271 while (cur != NULL) { 272 temp = cur; 273 cur = cur->next; 274 context_destroy(&temp->context); 275 kfree(temp); 276 } 277 s->htable[i] = NULL; 278 } 279 kfree(s->htable); 280 s->htable = NULL; 281 s->nel = 0; 282 s->next_sid = 1; 283 } 284 285 void sidtab_set(struct sidtab *dst, struct sidtab *src) 286 { 287 unsigned long flags; 288 289 SIDTAB_LOCK(src, flags); 290 dst->htable = src->htable; 291 dst->nel = src->nel; 292 dst->next_sid = src->next_sid; 293 dst->shutdown = 0; 294 SIDTAB_UNLOCK(src, flags); 295 } 296 297 void sidtab_shutdown(struct sidtab *s) 298 { 299 unsigned long flags; 300 301 SIDTAB_LOCK(s, flags); 302 s->shutdown = 1; 303 SIDTAB_UNLOCK(s, flags); 304 } 305