
/*
 * Licensed Materials - Property of IBM
 *
 * trousers - An open source TCG Software Stack
 *
 * (C) Copyright International Business Machines Corp. 2004-2006
 *
 */

/*
 * imaem.c
 *
 * Routines for handling PCR events from the Integrity Measurement
 * Architecture.
 *
 * The external event source format used by IMA:
 *
 *     4 bytes PCR Index (bin)
 *    20 bytes SHA1 template (bin)
 *     4 bytes template name_len
 * 1-255 bytes template name
 *    20 bytes SHA1 IMA(bin)
 *     4 bytes IMA name len
 * 1-255 bytes eventname
 *     1 byte  separator = '\0'
 *
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>

#include "trousers/tss.h"
#include "trousers_types.h"
#include "tcs_tsp.h"
#include "tcs_utils.h"
#include "tcs_int_literals.h"
#include "capabilities.h"
#include "tcsps.h"
#include "tcslog.h"
#include "tcsem.h"

#ifdef EVLOG_SOURCE_IMA

#define EVLOG_FILENAME_MAXSIZE 255

struct ext_log_source ima_source = {
	ima_open,
	ima_get_entries_by_pcr,
	ima_get_entry,
	ima_close
};

int
ima_open(void *source, FILE **handle)
{
 	FILE *fd;

	if ((fd = fopen((char *)source, "r")) == NULL) {
		LogError("Error opening PCR log file %s: %s",
			(char *)source, strerror(errno));
		return -1;
	}

	*handle =  fd;
	return 0;
}

TSS_RESULT
ima_get_entries_by_pcr(FILE *handle, UINT32 pcr_index, UINT32 first,
			UINT32 *count, TSS_PCR_EVENT **events)
{
	int pcr_value;
	char page[IMA_READ_SIZE];
	int error_path = 1, ptr = 0;
	UINT32 copied_events = 0, i;
	struct event_wrapper *list, *cur;
	TSS_RESULT result = TCSERR(TSS_E_INTERNAL_ERROR);
	FILE *fp = (FILE *) handle;
	uint len;
	char name[EVLOG_FILENAME_MAXSIZE];

	if (!fp) {
		LogError("File handle is NULL!\n");
		return 1;
	}

	if (*count == 0)
		return TSS_SUCCESS;

	list = calloc(1, sizeof(struct event_wrapper));
	if (list == NULL) {
		LogError("malloc of %zd bytes failed.", sizeof(struct event_wrapper));
		return TCSERR(TSS_E_OUTOFMEMORY);
	}
	cur = list;

	rewind(fp);

        while (fread(page, 24, 1, fp)) {
		/* copy the initial 4 bytes (PCR index) XXX endianess ignored */
		ptr = 0;
		memcpy(&pcr_value, &page[ptr], sizeof(int));
		cur->event.ulPcrIndex = pcr_value;
		ptr += sizeof(int);

		/* grab this entry */
		cur->event.ulPcrValueLength = 20;
		cur->event.rgbPcrValue = malloc(cur->event.ulPcrValueLength);
		if (cur->event.rgbPcrValue == NULL) {
			LogError("malloc of %d bytes failed.", 20);
			result = TCSERR(TSS_E_OUTOFMEMORY);
			goto free_list;
		}

		/* copy the template SHA1 XXX endianess ignored */
		memcpy(cur->event.rgbPcrValue, &page[ptr],
		       cur->event.ulPcrValueLength);

/* Get the template name size, template name */
{
		char digest[20];

		if (fread(&len, 1, sizeof(len), fp) != (sizeof(len))) {
			LogError("Failed to read event log file");
			result = TCSERR(TSS_E_INTERNAL_ERROR);
			goto free_list;
		}
		if (len > EVLOG_FILENAME_MAXSIZE) {
			LogError("Event log file name too big! Max size is %d", EVLOG_FILENAME_MAXSIZE);
			result = TCSERR(TSS_E_INTERNAL_ERROR);
			goto free_list;
		}
		memset(name, 0, EVLOG_FILENAME_MAXSIZE);
		if (fread(name, 1, len, fp) != len) {
			LogError("Failed to read event log file");
			result = TCSERR(TSS_E_INTERNAL_ERROR);
			goto free_list;
		}
		if (fread(digest, 1, sizeof digest, fp) != (sizeof(digest))) {
			LogError("Failed to read event log file");
			result = TCSERR(TSS_E_INTERNAL_ERROR);
			goto free_list;
		}
}
		/* Get the template data namelen and data */
		if (fread(&cur->event.ulEventLength, 1, sizeof(int), fp) != sizeof(int)) {
			LogError("Failed to read event log file");
			result = TCSERR(TSS_E_INTERNAL_ERROR);
			goto free_list;
		}
		cur->event.rgbEvent = malloc(cur->event.ulEventLength + 1);
		if (cur->event.rgbEvent == NULL) {
			free(cur->event.rgbPcrValue);
			LogError("malloc of %u bytes failed.",
				 cur->event.ulEventLength);
			result = TCSERR(TSS_E_OUTOFMEMORY);
			goto free_list;
		}
		memset(cur->event.rgbEvent, 0, cur->event.ulEventLength);
		if (fread(cur->event.rgbEvent, 1, cur->event.ulEventLength, fp) != cur->event.ulEventLength) {
			free(cur->event.rgbPcrValue);
			LogError("Failed to read event log file");
			result = TCSERR(TSS_E_INTERNAL_ERROR);
			goto free_list;
		}

		copied_events++;

		if (copied_events == *count)
			goto copy_events;

		cur->next = calloc(1, sizeof(struct event_wrapper));
		if (cur->next == NULL) {
			LogError("malloc of %zd bytes failed.",
				 sizeof(struct event_wrapper));
			result = TCSERR(TSS_E_OUTOFMEMORY);
			goto free_list;
		}
		cur = cur->next;
	}

copy_events:
	/* we've copied all the events we need to from this PCR, now
	 * copy them all into one contiguous memory block
	 */
	*events = calloc(copied_events, sizeof(TSS_PCR_EVENT));
	if (*events == NULL) {
		LogError("malloc of %zd bytes failed.", copied_events * sizeof(TSS_PCR_EVENT));
		result = TCSERR(TSS_E_OUTOFMEMORY);
		goto free_list;
	}

	cur = list;
	for (i = 0; i < copied_events; i++) {
		memcpy(&((*events)[i]), &(cur->event), sizeof(TSS_PCR_EVENT));
		cur = cur->next;
	}

	*count = copied_events;
	/* assume we're in an error path until we get here */
	error_path = 0;
	result = TSS_SUCCESS;

free_list:
	cur = list->next;
	while (cur != NULL) {
		if (error_path) {
			free(cur->event.rgbEvent);
			free(cur->event.rgbPcrValue);
		}
		free(list);
		list = cur;
		cur = list->next;
	}
	free(list);
	return result;
}

TSS_RESULT
ima_get_entry(FILE *handle, UINT32 pcr_index, UINT32 *num, TSS_PCR_EVENT **ppEvent)
{
	int pcr_value, ptr = 0;
	uint len;
	char page[IMA_READ_SIZE];
	UINT32 seen_indices = 0;
	TSS_RESULT result = TCSERR(TSS_E_INTERNAL_ERROR);
	TSS_PCR_EVENT *event = NULL;
	FILE *fp = (FILE *) handle;
	char name[EVLOG_FILENAME_MAXSIZE];

	rewind(fp);
	while (fread(page, 24, 1, fp)) {
		/* copy the initial 4 bytes (PCR index) XXX endianess ignored */
		ptr = 0;
		memcpy(&pcr_value, &page[ptr], sizeof(int));

		if (pcr_index == (UINT32)pcr_value) {
			ptr += sizeof(int);
			/* This is the case where we're looking for a specific event number in a
			 * specific PCR index. When we've reached the correct event, malloc
			 * space for it, copy it in, then break out of the while loop */
			if (ppEvent && seen_indices == *num) {
				/* grab this entry */
				event = calloc(1, sizeof(TSS_PCR_EVENT));
				event->ulPcrIndex = pcr_value;
				event->rgbPcrValue = NULL;
				event->rgbEvent = NULL;
				event->ulPcrValueLength = 20;
				event->rgbPcrValue = malloc(event->ulPcrValueLength);
				if (event->rgbPcrValue == NULL) {
					LogError("malloc of %d bytes failed.", 20);
					free(event);
					result = TCSERR(TSS_E_OUTOFMEMORY);
					goto done;
				}

				/* copy the template SHA1 XXX endianess ignored */
				memcpy(event->rgbPcrValue, &page[ptr],
						event->ulPcrValueLength);

				/* Get the template name size, template name */
				{
					char digest[20];

					if (fread(&len, 1, sizeof(len), fp) != sizeof(len)) {
						LogError("Failed to read event log file");
						result = TCSERR(TSS_E_INTERNAL_ERROR);
						goto done;
					}
					if (len > EVLOG_FILENAME_MAXSIZE) {
						LogError("Event log file name too big! Max size is %d", EVLOG_FILENAME_MAXSIZE);
						result = TCSERR(TSS_E_INTERNAL_ERROR);
						goto done;
					}
					memset(name, 0, EVLOG_FILENAME_MAXSIZE);
					if (fread(name, 1, len, fp) != len) {
						LogError("Failed to read event log file");
						result = TCSERR(TSS_E_INTERNAL_ERROR);
						goto done;
					}
					if (fread(digest, 1, sizeof(digest), fp) != sizeof(digest)) {
						LogError("Failed to read event log file");
						result = TCSERR(TSS_E_INTERNAL_ERROR);
						goto done;
					}
				}
				/* Get the template data namelen and data */
				if (fread(&event->ulEventLength, 1, sizeof(int), fp) != sizeof(int)) {
					LogError("Failed to read event log file");
					result = TCSERR(TSS_E_INTERNAL_ERROR);
					goto done;
				}
				event->rgbEvent = malloc(event->ulEventLength + 1);
				if (event->rgbEvent == NULL) {
					LogError("malloc of %u bytes failed.",
							event->ulEventLength);
					result = TCSERR(TSS_E_OUTOFMEMORY);
					goto done;
				}
				memset(event->rgbEvent, 0, event->ulEventLength + 1);
				if (fread(event->rgbEvent, 1, event->ulEventLength, fp) != event->ulEventLength ) {
					LogError("Failed to read event log file");
					result = TCSERR(TSS_E_INTERNAL_ERROR);
					goto done;
				}
				
				*ppEvent = event;
				result = TSS_SUCCESS;
				break;
			}
		}
		if (fread(&len, 1, sizeof(len), fp) != sizeof(len)) {
			LogError("Failed to read event log file");
			result = TCSERR(TSS_E_INTERNAL_ERROR);
			goto done;
		}
		fseek(fp, len + 20, SEEK_CUR);
		if (fread(&len, 1, sizeof(len), fp) != sizeof(len)) {
			LogError("Failed to read event log file");
			result = TCSERR(TSS_E_INTERNAL_ERROR);
			goto done;
		}
		fseek(fp, len, SEEK_CUR);
		seen_indices++;
	}
done:
	if (result != TSS_SUCCESS) {
		if (event != NULL) {
			free(event->rgbPcrValue);
			free(event->rgbEvent);
		}
		free(event);
		event = NULL;
	}

	if (ppEvent == NULL)
		*num = seen_indices;

	return result;
}

int
ima_close(FILE *handle)
{
	fclose((FILE *)handle);

	return 0;
}
#endif
