/*
 * The Initial Developer of the Original Code is International
 * Business Machines Corporation. Portions created by IBM
 * Corporation are Copyright (C) 2007 International Business
 * Machines Corporation. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the Common Public License as published by
 * IBM Corporation; either version 1 of the License, 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
 * Common Public License for more details.
 *
 * You should have received a copy of the Common Public License
 * along with this program; if not, a copy can be viewed at
 * http://www.opensource.org/licenses/cpl1.0.php.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <tss/platform.h>
#include <tss/tss_defines.h>
#include <tss/tss_typedef.h>
#include <tss/tss_structs.h>
#include <tss/tss_error.h>
#include <tss/tspi.h>

#include <getopt.h>

// Local TCSD
#define SERVER    NULL

/* IMA */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

struct measure_request {
	int fd;
	u_int16_t label;
};

/*

 TPM Extend 

 - via IMA only

*/

int hex2bin(void *dest, const void *src, size_t n);
void printhex(char *str, unsigned char *buf, int len);

/* options */
const struct option long_option[] = {
	{"ima", no_argument, NULL, 'i'},
	{"file", required_argument, NULL, 'f'},
	{"tss", no_argument, NULL, 'T'},
	{"pcrindex", required_argument, NULL, 'p'},
	{"digest", required_argument, NULL, 'd'},
	{"type", required_argument, NULL, 't'},
	{"help", no_argument, NULL, 'h'},
	{0, 0, 0, 0}
};
const char short_option[] = "if:Tp:d:t:h";

void usage() {
	printf("Usage: tpm_extend [options]\n");
	printf("\t-h, --help\n");
	printf("\t\tDisplay command usage info.\n");
	
	printf("\t-f, --file FILE\n");
	printf("\t\tFilename containing data to extend.\n");	
	
	printf("\t-t, --type EVENTTYPE\n");
	printf("\t\tSet Event Type. default value is zero\n");	

	// IMA use fixed PCR, 10
	//printf("\t-p, --pcrindex NUMBER\n");
	//printf("\t\tPCR to read to.  Default is none.  This option can be specified multiple times to choose more than one PCR.\n");

}

int main(int argc, char *argv[])
{
	TSS_RESULT result;
	TSS_HCONTEXT hContext;

	TSS_HTPM hTPM;

	UINT32 ulSubCapLength;
	UINT32 rgbSubCap;
	UINT32 pulRespDataLength;
	BYTE *prgbRespData;
	UINT32 pcrnum;

	BYTE *digest = NULL;

	UINT32 ulPcrValueLength;
	BYTE *rgbPcrValue;

	TSS_PCR_EVENT event;

	char *filename = NULL;

	int so;
	int pcrindex = 15;
	int ima = 1;
	int rc;
	int eventtype = 0;

	/* we parse the option args */

	while (1) {
		so = getopt_long(argc, argv, short_option, long_option, 0);
		if (so == -1)
			break;	// END

		switch (so) {
		case 'f':	/* set target file name */
			filename = optarg;
			break;
			
		// TBD
		case 'i':	/* IMA (default) */
			ima = 1;
			break;

		case 'T':	/* TSS */
			ima = 0;
			break;

		case 'd':	/* nonce */
			if (strlen(optarg) != 40) {
				printf("ERROR invalid digest size, %s\n",
				       optarg);
				goto close;
			}
			digest = malloc(20);
			if (digest == NULL) {
				printf("ERROR no memory \n");
				goto close;
			}
			hex2bin(digest, optarg, 40);
			break;

		case 'p':	/* PCR */
			pcrindex = atoi(optarg);
			break;

		case 't':	/* eventtype */
			eventtype = atoi(optarg);
			break;
			
		case 'h':	/* help */
			usage();
			goto end;

		default:
			usage();
			goto end;
		}
	}


	if (ima == 1) {
		int fd_measurere;
		int fd;
		struct measure_request mr;

		/* Check IMA */
		fd_measurere =
		    open("/sys/kernel/security/ima/measurereq", O_WRONLY);
		if (fd_measurere <= 0) {
			printf("ERROR: no IMA? or root?\n");
			result = -1;	// TODO
			goto end;
		}

		/* open file */
		fd = open(filename, O_RDONLY);
		if (fd <= 0) {
			printf("ERROR: file open failed %s?\n", filename);
			result = -1;	// TODO
			goto end;
		}

		/* measure the file before using it */
		mr.fd = fd;
		mr.label = eventtype;	//0x2155;

		rc = write(fd_measurere, (void *) &mr,
			   sizeof(struct measure_request));
		if (rc < 0) {
			printf("ERROR: ima write failed\n");
			result = -1;	// TODO
			goto end;
		}
		close(fd);
		close(fd_measurere);
	} else {		// TSS - TBD 
		/* Calc SHA1 Digest of File */
		
		
		/* Connect to TCSD */

		result = Tspi_Context_Create(&hContext);
		if (result != TSS_SUCCESS) {
			printf
			    ("ERROR: Tspi_Context_Create failed rc=0x%x\n",
			     result);
			goto close;
		}

		result = Tspi_Context_Connect(hContext, SERVER);
		if (result != TSS_SUCCESS) {
			printf
			    ("ERROR: Tspi_Context_Connect failed rc=0x%x\n",
			     result);
			goto close;
		}

		/* Get TPM handle */

		result = Tspi_Context_GetTpmObject(hContext, &hTPM);
		if (result != TSS_SUCCESS) {
			printf
			    ("ERROR: Tspi_Context_GetTpmObject failed rc=0x%x\n",
			     result);
			goto close;
		}

		/* Get PCR Num */

		ulSubCapLength = sizeof(UINT32);
		rgbSubCap = TSS_TPMCAP_PROP_PCR;

		result = Tspi_TPM_GetCapability(hTPM,
						TSS_TPMCAP_PROPERTY,
						ulSubCapLength,
						(BYTE *) & rgbSubCap,
						&pulRespDataLength,
						&prgbRespData);

		if (result != TSS_SUCCESS) {
			printf("ERROR: failed rc=0x%x\n", result);
			goto close;
		}

		pcrnum = (UINT32) * prgbRespData;
		// TODO Block PCR0-7
		if (pcrindex > pcrnum) {
			goto close;
		}

		printf("pcrnums=%d\n", pcrnum);

		 /**/ memset(&event, 0, sizeof(TSS_PCR_EVENT));
		event.ulPcrIndex = pcrindex;

		/* TPM_Extend */

		result = Tspi_TPM_PcrExtend(hTPM,
					    pcrindex,
					    20,
					    digest,
					    &event,
					    &ulPcrValueLength,
					    &rgbPcrValue);
		if (result != TSS_SUCCESS) {
			printf("ERROR: failed rc=0x%x\n", result);
			goto free;
		}

		printhex("PcrValue=", rgbPcrValue, ulPcrValueLength);

	      free:
		Tspi_Context_FreeMemory(hContext, NULL);
		free(digest);

		/* Close TSS/TPM */

	      close:
		Tspi_Context_Close(hContext);
	}

      end:
	return result;
}
