/*
* Implementation of the userspace SID hashtable.
*
* Author : Eamon Walsh, <ewalsh@epoch.ncsc.mil>
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "selinux_internal.h"
#include <selinux/avc.h>
#include "avc_sidtab.h"
#include "avc_internal.h"
static inline unsigned sidtab_hash(security_context_t key)
{
char *p, *keyp;
unsigned int size;
unsigned int val;
val = 0;
keyp = (char *)key;
size = strlen(keyp);
for (p = keyp; (unsigned int)(p - keyp) < size; p++)
val =
(val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p);
return val & (SIDTAB_SIZE - 1);
}
int sidtab_init(struct sidtab *s)
{
int i, rc = 0;
s->htable = (struct sidtab_node **)avc_malloc
(sizeof(struct sidtab_node *) * SIDTAB_SIZE);
if (!s->htable) {
rc = -1;
goto out;
}
for (i = 0; i < SIDTAB_SIZE; i++)
s->htable[i] = NULL;
s->nel = 0;
out:
return rc;
}
int sidtab_insert(struct sidtab *s, const security_context_t ctx)
{
int hvalue, rc = 0;
struct sidtab_node *newnode;
security_context_t newctx;
newnode = (struct sidtab_node *)avc_malloc(sizeof(*newnode));
if (!newnode) {
rc = -1;
goto out;
}
newctx = (security_context_t) strdup(ctx);
if (!newctx) {
rc = -1;
avc_free(newnode);
goto out;
}
hvalue = sidtab_hash(newctx);
newnode->next = s->htable[hvalue];
newnode->sid_s.ctx = newctx;
newnode->sid_s.refcnt = 1; /* unused */
s->htable[hvalue] = newnode;
s->nel++;
out:
return rc;
}
int
sidtab_context_to_sid(struct sidtab *s,
const security_context_t ctx, security_id_t * sid)
{
int hvalue, rc = 0;
struct sidtab_node *cur;
*sid = NULL;
hvalue = sidtab_hash(ctx);
loop:
cur = s->htable[hvalue];
while (cur != NULL && strcmp(cur->sid_s.ctx, ctx))
cur = cur->next;
if (cur == NULL) { /* need to make a new entry */
rc = sidtab_insert(s, ctx);
if (rc)
goto out;
goto loop; /* find the newly inserted node */
}
*sid = &cur->sid_s;
out:
return rc;
}
void sidtab_sid_stats(struct sidtab *h, char *buf, int buflen)
{
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;
}
}
snprintf(buf, buflen,
"%s: %d SID entries and %d/%d buckets used, longest "
"chain length %d\n", avc_prefix, 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];
while (cur != NULL) {
temp = cur;
cur = cur->next;
freecon(temp->sid_s.ctx);
avc_free(temp);
}
s->htable[i] = NULL;
}
avc_free(s->htable);
s->htable = NULL;
}