/*
* Implementation of the userspace SID hashtable.
*
* Author : Eamon Walsh, <ewalsh@epoch.ncsc.mil>
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.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, 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 = 0; /* caller should increment */
s->htable[hvalue] = newnode;
s->nel++;
out:
return rc;
}
void
sidtab_remove(struct sidtab *s, security_id_t sid)
{
int hvalue;
struct sidtab_node *cur, *prev;
hvalue = sidtab_hash(sid->ctx);
cur = s->htable[hvalue];
prev = NULL;
while (cur) {
if (sid == &cur->sid_s) {
if (prev)
prev->next = cur->next;
else
s->htable[hvalue] = cur->next;
avc_free(cur);
s->nel--;
return;
} else {
prev = cur;
cur = cur->next;
}
}
}
security_id_t
sidtab_claim_sid(struct sidtab *s)
{
int i;
struct sidtab_node *cur;
for (i=0; i < SIDTAB_SIZE; i++) {
cur = s->htable[i];
while (cur) {
if (!cur->sid_s.refcnt)
return &cur->sid_s;
cur = cur->next;
}
}
return NULL;
}
int
sidtab_context_to_sid(struct sidtab *s,
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;
}