1 /*
2  * Implementation of the userspace SID hashtable.
3  *
4  * Author : Eamon Walsh, <ewalsh@epoch.ncsc.mil>
5  */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <stdint.h>
10 #include <string.h>
11 #include "selinux_internal.h"
12 #include <selinux/avc.h>
13 #include "avc_sidtab.h"
14 #include "avc_internal.h"
15 
sidtab_hash(const char * key)16 static inline unsigned sidtab_hash(const char * key)
17 {
18 	char *p, *keyp;
19 	unsigned int size;
20 	unsigned int val;
21 
22 	val = 0;
23 	keyp = (char *)key;
24 	size = strlen(keyp);
25 	for (p = keyp; (unsigned int)(p - keyp) < size; p++)
26 		val =
27 		    (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p);
28 	return val & (SIDTAB_SIZE - 1);
29 }
30 
sidtab_init(struct sidtab * s)31 int sidtab_init(struct sidtab *s)
32 {
33 	int i, rc = 0;
34 
35 	s->htable = (struct sidtab_node **)avc_malloc
36 	    (sizeof(struct sidtab_node *) * SIDTAB_SIZE);
37 
38 	if (!s->htable) {
39 		rc = -1;
40 		goto out;
41 	}
42 	for (i = 0; i < SIDTAB_SIZE; i++)
43 		s->htable[i] = NULL;
44 	s->nel = 0;
45       out:
46 	return rc;
47 }
48 
sidtab_insert(struct sidtab * s,const char * ctx)49 int sidtab_insert(struct sidtab *s, const char * ctx)
50 {
51 	int hvalue, rc = 0;
52 	struct sidtab_node *newnode;
53 	char * newctx;
54 
55 	newnode = (struct sidtab_node *)avc_malloc(sizeof(*newnode));
56 	if (!newnode) {
57 		rc = -1;
58 		goto out;
59 	}
60 	newctx = (char *) strdup(ctx);
61 	if (!newctx) {
62 		rc = -1;
63 		avc_free(newnode);
64 		goto out;
65 	}
66 
67 	hvalue = sidtab_hash(newctx);
68 	newnode->next = s->htable[hvalue];
69 	newnode->sid_s.ctx = newctx;
70 	newnode->sid_s.refcnt = 1;	/* unused */
71 	s->htable[hvalue] = newnode;
72 	s->nel++;
73       out:
74 	return rc;
75 }
76 
77 int
sidtab_context_to_sid(struct sidtab * s,const char * ctx,security_id_t * sid)78 sidtab_context_to_sid(struct sidtab *s,
79 		      const char * ctx, security_id_t * sid)
80 {
81 	int hvalue, rc = 0;
82 	struct sidtab_node *cur;
83 
84 	*sid = NULL;
85 	hvalue = sidtab_hash(ctx);
86 
87       loop:
88 	cur = s->htable[hvalue];
89 	while (cur != NULL && strcmp(cur->sid_s.ctx, ctx))
90 		cur = cur->next;
91 
92 	if (cur == NULL) {	/* need to make a new entry */
93 		rc = sidtab_insert(s, ctx);
94 		if (rc)
95 			goto out;
96 		goto loop;	/* find the newly inserted node */
97 	}
98 
99 	*sid = &cur->sid_s;
100       out:
101 	return rc;
102 }
103 
sidtab_sid_stats(struct sidtab * h,char * buf,int buflen)104 void sidtab_sid_stats(struct sidtab *h, char *buf, int buflen)
105 {
106 	int i, chain_len, slots_used, max_chain_len;
107 	struct sidtab_node *cur;
108 
109 	slots_used = 0;
110 	max_chain_len = 0;
111 	for (i = 0; i < SIDTAB_SIZE; i++) {
112 		cur = h->htable[i];
113 		if (cur) {
114 			slots_used++;
115 			chain_len = 0;
116 			while (cur) {
117 				chain_len++;
118 				cur = cur->next;
119 			}
120 
121 			if (chain_len > max_chain_len)
122 				max_chain_len = chain_len;
123 		}
124 	}
125 
126 	snprintf(buf, buflen,
127 		 "%s:  %u SID entries and %d/%d buckets used, longest "
128 		 "chain length %d\n", avc_prefix, h->nel, slots_used,
129 		 SIDTAB_SIZE, max_chain_len);
130 }
131 
sidtab_destroy(struct sidtab * s)132 void sidtab_destroy(struct sidtab *s)
133 {
134 	int i;
135 	struct sidtab_node *cur, *temp;
136 
137 	if (!s)
138 		return;
139 
140 	for (i = 0; i < SIDTAB_SIZE; i++) {
141 		cur = s->htable[i];
142 		while (cur != NULL) {
143 			temp = cur;
144 			cur = cur->next;
145 			freecon(temp->sid_s.ctx);
146 			avc_free(temp);
147 		}
148 		s->htable[i] = NULL;
149 	}
150 	avc_free(s->htable);
151 	s->htable = NULL;
152 }
153