| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Implementation of the SID table type. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/spinlock.h>
 | 
					
						
							|  |  |  | #include <linux/errno.h>
 | 
					
						
							|  |  |  | #include "flask.h"
 | 
					
						
							|  |  |  | #include "security.h"
 | 
					
						
							|  |  |  | #include "sidtab.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SIDTAB_HASH(sid) \
 | 
					
						
							|  |  |  | (sid & SIDTAB_HASH_MASK) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int sidtab_init(struct sidtab *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s->htable = kmalloc(sizeof(*(s->htable)) * SIDTAB_SIZE, GFP_ATOMIC); | 
					
						
							|  |  |  | 	if (!s->htable) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	for (i = 0; i < SIDTAB_SIZE; i++) | 
					
						
							|  |  |  | 		s->htable[i] = NULL; | 
					
						
							|  |  |  | 	s->nel = 0; | 
					
						
							|  |  |  | 	s->next_sid = 1; | 
					
						
							|  |  |  | 	s->shutdown = 0; | 
					
						
							| 
									
										
										
										
											2008-06-06 18:50:12 +10:00
										 |  |  | 	spin_lock_init(&s->lock); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int hvalue, rc = 0; | 
					
						
							|  |  |  | 	struct sidtab_node *prev, *cur, *newnode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!s) { | 
					
						
							|  |  |  | 		rc = -ENOMEM; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hvalue = SIDTAB_HASH(sid); | 
					
						
							|  |  |  | 	prev = NULL; | 
					
						
							|  |  |  | 	cur = s->htable[hvalue]; | 
					
						
							| 
									
										
										
										
											2008-08-07 03:18:20 +03:00
										 |  |  | 	while (cur && sid > cur->sid) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		prev = cur; | 
					
						
							|  |  |  | 		cur = cur->next; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (cur && sid == cur->sid) { | 
					
						
							|  |  |  | 		rc = -EEXIST; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC); | 
					
						
							|  |  |  | 	if (newnode == NULL) { | 
					
						
							|  |  |  | 		rc = -ENOMEM; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	newnode->sid = sid; | 
					
						
							|  |  |  | 	if (context_cpy(&newnode->context, context)) { | 
					
						
							|  |  |  | 		kfree(newnode); | 
					
						
							|  |  |  | 		rc = -ENOMEM; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (prev) { | 
					
						
							|  |  |  | 		newnode->next = prev->next; | 
					
						
							|  |  |  | 		wmb(); | 
					
						
							|  |  |  | 		prev->next = newnode; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		newnode->next = s->htable[hvalue]; | 
					
						
							|  |  |  | 		wmb(); | 
					
						
							|  |  |  | 		s->htable[hvalue] = newnode; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s->nel++; | 
					
						
							|  |  |  | 	if (sid >= s->next_sid) | 
					
						
							|  |  |  | 		s->next_sid = sid + 1; | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-07 13:03:20 -04:00
										 |  |  | static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int hvalue; | 
					
						
							|  |  |  | 	struct sidtab_node *cur; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!s) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hvalue = SIDTAB_HASH(sid); | 
					
						
							|  |  |  | 	cur = s->htable[hvalue]; | 
					
						
							| 
									
										
										
										
											2008-08-07 03:18:20 +03:00
										 |  |  | 	while (cur && sid > cur->sid) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		cur = cur->next; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-07 13:03:20 -04:00
										 |  |  | 	if (force && cur && sid == cur->sid && cur->context.len) | 
					
						
							|  |  |  | 		return &cur->context; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (cur == NULL || sid != cur->sid || cur->context.len) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/* Remap invalid SIDs to the unlabeled SID. */ | 
					
						
							|  |  |  | 		sid = SECINITSID_UNLABELED; | 
					
						
							|  |  |  | 		hvalue = SIDTAB_HASH(sid); | 
					
						
							|  |  |  | 		cur = s->htable[hvalue]; | 
					
						
							| 
									
										
										
										
											2008-08-07 03:18:20 +03:00
										 |  |  | 		while (cur && sid > cur->sid) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			cur = cur->next; | 
					
						
							|  |  |  | 		if (!cur || sid != cur->sid) | 
					
						
							|  |  |  | 			return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return &cur->context; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-07 13:03:20 -04:00
										 |  |  | struct context *sidtab_search(struct sidtab *s, u32 sid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return sidtab_search_core(s, sid, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct context *sidtab_search_force(struct sidtab *s, u32 sid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return sidtab_search_core(s, sid, 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | int sidtab_map(struct sidtab *s, | 
					
						
							|  |  |  | 	       int (*apply) (u32 sid, | 
					
						
							|  |  |  | 			     struct context *context, | 
					
						
							|  |  |  | 			     void *args), | 
					
						
							|  |  |  | 	       void *args) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i, rc = 0; | 
					
						
							|  |  |  | 	struct sidtab_node *cur; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!s) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < SIDTAB_SIZE; i++) { | 
					
						
							|  |  |  | 		cur = s->htable[i]; | 
					
						
							| 
									
										
										
										
											2008-08-07 03:18:20 +03:00
										 |  |  | 		while (cur) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			rc = apply(cur->sid, &cur->context, args); | 
					
						
							|  |  |  | 			if (rc) | 
					
						
							|  |  |  | 				goto out; | 
					
						
							|  |  |  | 			cur = cur->next; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-07 16:17:28 -05:00
										 |  |  | static void sidtab_update_cache(struct sidtab *s, struct sidtab_node *n, int loc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BUG_ON(loc >= SIDTAB_CACHE_LEN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (loc > 0) { | 
					
						
							|  |  |  | 		s->cache[loc] = s->cache[loc - 1]; | 
					
						
							|  |  |  | 		loc--; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	s->cache[0] = n; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static inline u32 sidtab_search_context(struct sidtab *s, | 
					
						
							|  |  |  | 						  struct context *context) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	struct sidtab_node *cur; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < SIDTAB_SIZE; i++) { | 
					
						
							|  |  |  | 		cur = s->htable[i]; | 
					
						
							| 
									
										
										
										
											2008-08-07 03:18:20 +03:00
										 |  |  | 		while (cur) { | 
					
						
							| 
									
										
										
										
											2010-12-07 16:17:28 -05:00
										 |  |  | 			if (context_cmp(&cur->context, context)) { | 
					
						
							|  |  |  | 				sidtab_update_cache(s, cur, SIDTAB_CACHE_LEN - 1); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				return cur->sid; | 
					
						
							| 
									
										
										
										
											2010-12-07 16:17:28 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			cur = cur->next; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-07 16:17:28 -05:00
										 |  |  | static inline u32 sidtab_search_cache(struct sidtab *s, struct context *context) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	struct sidtab_node *node; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < SIDTAB_CACHE_LEN; i++) { | 
					
						
							|  |  |  | 		node = s->cache[i]; | 
					
						
							|  |  |  | 		if (unlikely(!node)) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		if (context_cmp(&node->context, context)) { | 
					
						
							|  |  |  | 			sidtab_update_cache(s, node, i); | 
					
						
							|  |  |  | 			return node->sid; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | int sidtab_context_to_sid(struct sidtab *s, | 
					
						
							|  |  |  | 			  struct context *context, | 
					
						
							|  |  |  | 			  u32 *out_sid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 sid; | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*out_sid = SECSID_NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-07 16:17:28 -05:00
										 |  |  | 	sid  = sidtab_search_cache(s, context); | 
					
						
							|  |  |  | 	if (!sid) | 
					
						
							|  |  |  | 		sid = sidtab_search_context(s, context); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (!sid) { | 
					
						
							| 
									
										
										
										
											2008-06-06 18:50:12 +10:00
										 |  |  | 		spin_lock_irqsave(&s->lock, flags); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		/* Rescan now that we hold the lock. */ | 
					
						
							|  |  |  | 		sid = sidtab_search_context(s, context); | 
					
						
							|  |  |  | 		if (sid) | 
					
						
							|  |  |  | 			goto unlock_out; | 
					
						
							|  |  |  | 		/* No SID exists for the context.  Allocate a new one. */ | 
					
						
							|  |  |  | 		if (s->next_sid == UINT_MAX || s->shutdown) { | 
					
						
							|  |  |  | 			ret = -ENOMEM; | 
					
						
							|  |  |  | 			goto unlock_out; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		sid = s->next_sid++; | 
					
						
							| 
									
										
										
										
											2008-05-07 13:03:20 -04:00
										 |  |  | 		if (context->len) | 
					
						
							|  |  |  | 			printk(KERN_INFO | 
					
						
							|  |  |  | 		       "SELinux:  Context %s is not valid (left unmapped).\n", | 
					
						
							|  |  |  | 			       context->str); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		ret = sidtab_insert(s, sid, context); | 
					
						
							|  |  |  | 		if (ret) | 
					
						
							|  |  |  | 			s->next_sid--; | 
					
						
							|  |  |  | unlock_out: | 
					
						
							| 
									
										
										
										
											2008-06-06 18:50:12 +10:00
										 |  |  | 		spin_unlock_irqrestore(&s->lock, flags); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*out_sid = sid; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void sidtab_hash_eval(struct sidtab *h, char *tag) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i, chain_len, slots_used, max_chain_len; | 
					
						
							|  |  |  | 	struct sidtab_node *cur; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	slots_used = 0; | 
					
						
							|  |  |  | 	max_chain_len = 0; | 
					
						
							|  |  |  | 	for (i = 0; i < SIDTAB_SIZE; i++) { | 
					
						
							|  |  |  | 		cur = h->htable[i]; | 
					
						
							|  |  |  | 		if (cur) { | 
					
						
							|  |  |  | 			slots_used++; | 
					
						
							|  |  |  | 			chain_len = 0; | 
					
						
							|  |  |  | 			while (cur) { | 
					
						
							|  |  |  | 				chain_len++; | 
					
						
							|  |  |  | 				cur = cur->next; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (chain_len > max_chain_len) | 
					
						
							|  |  |  | 				max_chain_len = chain_len; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-22 18:11:31 -05:00
										 |  |  | 	printk(KERN_DEBUG "%s:  %d entries and %d/%d buckets used, longest " | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	       "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE, | 
					
						
							|  |  |  | 	       max_chain_len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void sidtab_destroy(struct sidtab *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	struct sidtab_node *cur, *temp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!s) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < SIDTAB_SIZE; i++) { | 
					
						
							|  |  |  | 		cur = s->htable[i]; | 
					
						
							| 
									
										
										
										
											2008-08-07 03:18:20 +03:00
										 |  |  | 		while (cur) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			temp = cur; | 
					
						
							|  |  |  | 			cur = cur->next; | 
					
						
							|  |  |  | 			context_destroy(&temp->context); | 
					
						
							|  |  |  | 			kfree(temp); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		s->htable[i] = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	kfree(s->htable); | 
					
						
							|  |  |  | 	s->htable = NULL; | 
					
						
							|  |  |  | 	s->nel = 0; | 
					
						
							|  |  |  | 	s->next_sid = 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void sidtab_set(struct sidtab *dst, struct sidtab *src) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							| 
									
										
										
										
											2010-12-07 16:17:28 -05:00
										 |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-06 18:50:12 +10:00
										 |  |  | 	spin_lock_irqsave(&src->lock, flags); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	dst->htable = src->htable; | 
					
						
							|  |  |  | 	dst->nel = src->nel; | 
					
						
							|  |  |  | 	dst->next_sid = src->next_sid; | 
					
						
							|  |  |  | 	dst->shutdown = 0; | 
					
						
							| 
									
										
										
										
											2010-12-07 16:17:28 -05:00
										 |  |  | 	for (i = 0; i < SIDTAB_CACHE_LEN; i++) | 
					
						
							|  |  |  | 		dst->cache[i] = NULL; | 
					
						
							| 
									
										
										
										
											2008-06-06 18:50:12 +10:00
										 |  |  | 	spin_unlock_irqrestore(&src->lock, flags); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void sidtab_shutdown(struct sidtab *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-06 18:50:12 +10:00
										 |  |  | 	spin_lock_irqsave(&s->lock, flags); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	s->shutdown = 1; | 
					
						
							| 
									
										
										
										
											2008-06-06 18:50:12 +10:00
										 |  |  | 	spin_unlock_irqrestore(&s->lock, flags); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } |