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