/*
* subject.c - Minimal linked list set of subject attributes
* Copyright (c) 2016 Red Hat Inc., Durham, North Carolina.
* All Rights Reserved.
*
* This software may be freely redistributed and/or modified under the
* terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1335, USA.
*
* Authors:
*   Steve Grubb <sgrubb@redhat.com>
*/

#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "policy.h"
#include "subject.h"
#include "message.h"

//#define DEBUG

int subject_create(s_array *a)
{
	if (a == NULL)
		return 1;

	a->cnt = 0;
	a->info = NULL;
	a->subj = calloc(SUBJ_COUNT, sizeof(subject_attr_t *));
	if (a->subj == NULL)
		return 1;

	return 0;
}

#ifdef DEBUG
static void sanity_check_array(const s_array *a, const char *id)
{
	int i;
	unsigned int num = 0;
	if (a == NULL) {
		msg(LOG_DEBUG, "%s - array is NULL", id);
		abort();
	}
	for (i = 0; i < SUBJ_COUNT; i++)
		if (a->subj[i])
			num++;
	if (num != a->cnt) {
		msg(LOG_DEBUG, "%s - array corruption %u!=%u", id, num, a->cnt);
		abort();
	}
}
#else
#define sanity_check_array(a, b) do {} while(0)
#endif

subject_attr_t *subject_access(const s_array *a, subject_type_t t)
{
	if (a == NULL || a->subj == NULL)
		return NULL;
	sanity_check_array(a, "subject_access");
	// These store the same info, see get_subj_attr in event.c
	if (t == EXE_DIR)
		t = EXE;
	if (t >= SUBJ_START && t <= SUBJ_END)
		return a->subj[t - SUBJ_START];
	else
		return NULL;
}

// Returns 1 on failure and 0 on success
int subject_add(s_array *a, const subject_attr_t *subj)
{
	subject_attr_t* newnode;
	subject_attr_t **slot;
	subject_type_t t;

	if (a == NULL || a->subj == NULL)
		return 1;
	sanity_check_array(a, "subject_add 1");
	if (subj) {
		t = subj->type;
		// These store the same info, see get_subj_attr in event.c
		if (t == EXE_DIR)
			t = EXE;
		if (t >= SUBJ_START && t <= SUBJ_END) {
			newnode = malloc(sizeof(subject_attr_t));
			if (newnode == NULL)
				return 1;
			newnode->type = t;
			if (subj->type >= COMM)
			newnode->str = subj->str;
			else if (subj->type == GID || subj->type == UID)
			/*
			 * Attribute sets are reference-count-less blobs.  The
			 * caller allocates a fresh set for us to adopt so we
			 * simply transfer ownership to the cached node.
			 */
				newnode->set = subj->set;
			else if (subj->type == PID || subj->type == PPID)
				newnode->pid = subj->pid;
			else
				newnode->uval = subj->uval;
		} else
			return 1;
	} else
		return 1;

	slot = &a->subj[t - SUBJ_START];
	if (*slot) {
		subject_attr_t *old = *slot;

		if (old->type == GID || old->type == UID)
			attr_set_destroy(old->set);
		else if (old->type >= COMM)
			free(old->str);
		free(old);
	} else
		a->cnt++;

	*slot = newnode;
	sanity_check_array(a, "subject_add 2");

	return 0;
}

subject_attr_t *subject_find_exe(const s_array *a)
{
	if (a == NULL || a->subj == NULL)
		return NULL;
	sanity_check_array(a, "subject_find_exe");
	if (a->subj[EXE - SUBJ_START])
		return a->subj[EXE - SUBJ_START];

	return NULL;
}

subject_attr_t *subject_find_comm(const s_array *a)
{
	if (a == NULL || a->subj == NULL)
		return NULL;
	sanity_check_array(a, "subject_find_comm");
	if (a->subj[COMM - SUBJ_START])
		return a->subj[COMM - SUBJ_START];

	return NULL;
}

void subject_clear(s_array* a)
{
	int i;
	subject_attr_t *current;

	if (a == NULL)
		return;

	if (a->subj) {
		sanity_check_array(a, "subject_clear");
		for (i = 0; i < SUBJ_COUNT; i++) {
			current = a->subj[i];
			if (current == NULL)
				continue;
			if (current->type == GID || current->type == UID) {
				/*
				 * GID/UID attributes own dynamically allocated
				 * sets; attr_set_destroy() releases contents and
				 * container.
				 */
				attr_set_destroy(current->set);
			} else if (current->type >= COMM)
				free(current->str);
			free(current);
			a->subj[i] = NULL;
		}
		free(a->subj);
		a->subj = NULL;
	}
	clear_proc_info(a->info);
	free(a->info);
	a->info = NULL;
	a->cnt = 0;
}

void subject_reset(s_array *a, subject_type_t t)
{
	if (a == NULL || a->subj == NULL)
		return;

	sanity_check_array(a, "subject_reset1");
	if (t >= SUBJ_START && t <= SUBJ_END) {
		subject_attr_t *current = a->subj[t - SUBJ_START];
		if (current == NULL)
			return;
		if (current->type == GID || current->type == UID) {
			/*
			 * GID/UID attributes own dynamically allocated
			 * sets; attr_set_destroy() releases contents and
			 * container.
			 */
			attr_set_destroy(current->set);
		} else if (current->type >= COMM)
			free(current->str);
		free(current);
		a->subj[t - SUBJ_START] = NULL;
		a->cnt--;
		sanity_check_array(a, "subject_reset2");
	}
}
