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	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
31int 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
49int 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
77int
78sidtab_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
104void 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:  %d 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
132void 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