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