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

/*
 * biosem.c
 *
 * Routines for handling PCR events from the TCG Compliant BIOS
 *
 */

#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_BIOS

struct ext_log_source bios_source = {
	bios_open,
	bios_get_entries_by_pcr,
	bios_get_entry,
	bios_close
};

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

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

	*handle = fd;

	return 0;
}

TSS_RESULT
bios_get_entries_by_pcr(FILE *handle, UINT32 pcr_index, UINT32 first,
			UINT32 *count, TSS_PCR_EVENT **events)
{
	char page[BIOS_READ_SIZE];
	int error_path = 1;
	UINT32 seen_indices = 0, copied_events = 0, i;
	struct event_wrapper *list = calloc(1, sizeof(struct event_wrapper));
	struct event_wrapper *cur = list;
	TSS_RESULT result = TSS_E_INTERNAL_ERROR;
	TCG_PCClientPCREventStruc *event = NULL;
	int num=0;

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

	if (*count == 0) {
		result = TSS_SUCCESS;
		goto free_list;
	}

	while (1) {
		/* read event header from the file */
		if ((fread(page, 32, 1, handle)) <= 0) {
			goto copy_events;
		}

		event = (TCG_PCClientPCREventStruc *)page;

		/* if the index is the one we're looking for, grab the entry */
		if (pcr_index == event->pcrIndex) {
			if (seen_indices >= first) {
				/* grab this entry */
				cur->event.rgbPcrValue = malloc(20);
				if (cur->event.rgbPcrValue == NULL) {
					LogError("malloc of %d bytes failed.", 20);
					result = TSS_E_OUTOFMEMORY;
					goto free_list;
				}

				cur->event.ulPcrIndex = pcr_index;
				cur->event.eventType = event->eventType;
				cur->event.ulPcrValueLength = 20;

				/* copy the SHA1 XXX endianess ignored */
				memcpy(cur->event.rgbPcrValue, event->digest, 20);

				/* copy the event name XXX endianess ignored */
				cur->event.ulEventLength = event->eventDataSize;

				if (event->eventDataSize>0) {
					cur->event.rgbEvent = malloc(event->eventDataSize);
					if (cur->event.rgbEvent == NULL) {
						LogError("malloc of %d bytes failed.",
							 event->eventDataSize);
						free(cur->event.rgbPcrValue);
						result = TSS_E_OUTOFMEMORY;
						goto free_list;
					}
					if ((fread(cur->event.rgbEvent,
						   event->eventDataSize, 1, handle)) <= 0) {
						LogError("read from event source failed: %s",
							 strerror(errno));
						goto free_list;
					}
				} else {
					cur->event.rgbEvent = NULL;
				}

				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 = TSS_E_OUTOFMEMORY;
					goto free_list;
				}
				cur = cur->next;
			} else {
				/* skip */
				if (event->eventDataSize > 0)
					fseek(handle,event->eventDataSize,SEEK_CUR);
			}
			seen_indices++;
		} else {
			if (event->eventDataSize > 0)
				fseek(handle,event->eventDataSize,SEEK_CUR);
			}
		num++;
	}

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 = 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
bios_get_entry(FILE *handle, UINT32 pcr_index, UINT32 *num, TSS_PCR_EVENT **ppEvent)
{
	char page[BIOS_READ_SIZE];
	UINT32 seen_indices = 0;
	TSS_RESULT result = TSS_E_INTERNAL_ERROR;
	TSS_PCR_EVENT *e = NULL;
	TCG_PCClientPCREventStruc *event = NULL;

	while (1) {
		/* read event header from the file */
		if ((fread(page, 32, 1, handle)) == 0) {
			goto done;
		}

		event = (TCG_PCClientPCREventStruc *)page;

		if (pcr_index == event->pcrIndex) {
			if (ppEvent && !*ppEvent && seen_indices == *num) {
				*ppEvent = calloc(1, sizeof(TSS_PCR_EVENT));
				if (*ppEvent == NULL) {
					LogError("malloc of %zd bytes failed.",
						 sizeof(TSS_PCR_EVENT));
					return TSS_E_INTERNAL_ERROR;
				}

				e = *ppEvent;

				e->rgbPcrValue = malloc(20);
				if (e->rgbPcrValue == NULL) {
					LogError("malloc of %d bytes failed.", 20);
					free(e);
					e = NULL;
					break;
				}

				e->ulPcrIndex = pcr_index;
				e->eventType = event->eventType;
				e->ulPcrValueLength = 20;

				/* copy the SHA1 XXX endianess ignored */
				memcpy(e->rgbPcrValue, event->digest, 20);

				e->ulEventLength = event->eventDataSize;

				if (event->eventDataSize>0) {
					e->rgbEvent = malloc(e->ulEventLength);
					if (e->rgbEvent == NULL) {
						LogError("malloc of %d bytes failed.",
							 e->ulEventLength);
						free(e->rgbPcrValue);
						free(e);
						e = NULL;
						break;
					}
					if ((fread(e->rgbEvent,
						   event->eventDataSize,
						   1, handle)) <= 0) {
						LogError("read from event source failed: %s",
							 strerror(errno));
						return result;
					}
				} else {
					e->rgbEvent = NULL;
				}
				result = TSS_SUCCESS;

				break;
			} else {
				/* skip */
				if (event->eventDataSize > 0) {
					fseek(handle,event->eventDataSize,SEEK_CUR);
				}
			}
			seen_indices++;
		} else {
			/* skip */
			if (event->eventDataSize > 0) {
				fseek(handle,event->eventDataSize,SEEK_CUR);
			}
		}
	}

done:
	if (!ppEvent) {
		*num = seen_indices;
		result = TSS_SUCCESS;
	} else if (e == NULL)
		*ppEvent = NULL;

	return result;
}

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

	return 0;
}

#endif
