/* $OpenBSD: keynote-verify.c,v 1.14 2004/06/29 11:35:56 msf Exp $ */
/*
 * The author of this code is Angelos D. Keromytis (angelos@dsl.cis.upenn.edu)
 *
 * This code was written by Angelos D. Keromytis in Philadelphia, PA, USA,
 * in April-May 1998
 *
 * Copyright (C) 1998, 1999 by Angelos D. Keromytis.
 *	
 * Permission to use, copy, and modify this software with or without fee
 * is hereby granted, provided that this entire notice is included in
 * all copies of any software which is or includes a copy or
 * modification of this software. 
 *
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTY. IN PARTICULAR, THE AUTHORS MAKES NO
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
 * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
 * PURPOSE.
 */

#include <sys/types.h>
#include <sys/stat.h>

#include <ctype.h>
#include <fcntl.h>
#include <getopt.h>
#include <memory.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "header.h"
#include "keynote.h"

void	verifyusage(void);

void
verifyusage(void)
{
    fprintf(stderr, "Arguments:\n");
    fprintf(stderr, "\t-h:             This message\n");
    fprintf(stderr,
	    "\t-r <valuelist>: Comma separated, ordered return-value list\n");
    fprintf(stderr, "\t-e <filename>:  Environment settings\n");
    fprintf(stderr, "\t-l <filename>:  Trusted (local) assertion\n");
    fprintf(stderr, "\t-k <filename>:  File containing key\n");
    fprintf(stderr, "Followed by a list of:\n");
    fprintf(stderr, "\t<filename>:     Non-local assertion\n");
}

void
keynote_verify(int argc, char *argv[])
{
#ifdef LoopTesting
    int loopvar = 1000;
#endif /* LoopTesting */
    int fd, i, ch, se = 0, cl = 8192, sk = 0, sl = 0, p, ac = argc;
    char *buf, **av = argv, **retv, **foov, *ptr;
    int numretv = 16, numret = 0, sn;
    struct stat sb;

    if (argc == 1)
    {
	verifyusage();
	exit(1);
    }

    if ((buf = (char *) calloc(cl, sizeof(char))) == (char *) NULL)
    {
	perror("calloc()");
	exit(1);
    }

#ifdef LoopTesting
    while(loopvar--) {
#endif /* LoopTesting */

    if ((retv = (char **) calloc(numretv, sizeof(char *))) == (char **) NULL)
    {
	perror("calloc()");
	exit(1);
    }

    /* "ac" and "av" are used for stress-testing, ignore otherwise */
    argv = av;
    argc = ac;
    sn = 0;
    opterr = 0;

    sessid = kn_init();
    if (sessid == -1)
    {
	fprintf(stderr, "kn_init() failed (errno %d).\n", keynote_errno);
	exit(keynote_errno);
    }
    
    while ((ch = getopt(argc, argv, "hqistl:e:k:r:")) != -1)
    {
	switch (ch) 
	{
	    case 'e':
		if (read_environment(optarg) == -1)
	 	  exit(1);
		se = 1;
		break;

	    case 'k':
		sk = 1;

		if ((fd = open(optarg, O_RDONLY, 0)) < 0)
		{
		    perror(optarg);
		    exit(1);
		}

		if (fstat(fd, &sb) < 0)
		{
		    perror("fstat()");
		    exit(1);
		}

		if (sb.st_size > cl - 1)
		{
		    free(buf);
		    cl = sb.st_size + 1;
		    buf = (char *) calloc(cl, sizeof(char));
		    if (buf == (char *) NULL)
		    {
			perror("calloc()");
			exit(1);
		    }
		}

		i = read(fd, buf, sb.st_size);
		if (i < 0)
		{
		    perror("read()");
		    exit(1);
		}

		close(fd);

		parse_key(buf);
		switch (keynote_errno)
		{
		    case 0:    /* No errors */
			break;

		    case ERROR_SYNTAX:
			fprintf(stderr, "Syntax error adding authorizer "
				"%s\n", optarg);
			exit(1);

		    case ERROR_MEMORY:
			perror("Out of memory.\n");
			exit(1);

		    default:
			fprintf(stderr, "Unknown error (%d).\n",
				keynote_errno);
		}

		break;

	    case 'h':
		verifyusage();
		exit(0);

	    case 'r':
		if (sn != 0)
		{
		    fprintf(stderr,
			    "Do not define two sets of return values.\n");
		    exit(1);
		}

		sn = 1;

		for (numret = 0;
		     (ptr = strchr(optarg, ',')) != (char *) NULL;
		     numret++)
		{
		    /* Running out of memory */
		    if (numret > numretv - 3)
		    {
			numretv *= 2;
			foov = (char **) calloc(numretv, sizeof(char **));
			if (foov == (char **) NULL)
			{
			    /* 
			     * If this were a real program, we 'd be freeing
			     * retv here. Since we're exiting, we can be a
			     * little sloppy.
			     */
			    perror("calloc()");
			    exit(1);
			}

			memcpy(foov, retv, numretv * sizeof(char **));
			free(retv);
			retv = foov;
		    }

		    retv[numret] = (char *) calloc((ptr - optarg) + 1,
						       sizeof(char));
		    if (retv[numret] == (char *) NULL)
		    {
			/* Comment from above applies here as well */
			perror("calloc()");
			exit(1);
		    }

		    /* Copy */
		    memcpy(retv[numret], optarg, ptr - optarg);
		    optarg = ptr + 1;
		}

		/* Last component */
		retv[numret] = (char *) strdup(optarg);
		if (retv[numret] == (char *) NULL)
		{
		    perror("calloc()");
		    exit(1);
		}

		numret++;
		break;

	    case 'l':
		if ((fd = open(optarg, O_RDONLY, 0)) < 0)
		{
		    perror(optarg);
		    exit(1);
		}

		if (fstat(fd, &sb) < 0)
		{
		    perror("fstat()");
		    exit(1);
		}

		if (sb.st_size > cl - 1)
		{
		    free(buf);
		    cl = sb.st_size + 1;
		    buf = (char *) calloc(cl, sizeof(char));
		    if (buf == (char *) NULL)
		    {
			perror("calloc()");
			exit(1);
		    }
		}

		i = read(fd, buf, sb.st_size);
		if (i < 0)
		{
		    perror("read()");
		    exit(1);
		}

		close(fd);
		p = kn_add_assertion(sessid, buf, i, ASSERT_FLAG_LOCAL);
		if (p == -1)
		{
		    fprintf(stderr,
			    "Error for assertion in file <%s>, errno %d.\n",
			   optarg, keynote_errno);
		    keynote_errno = 0;
		}

		memset(buf, 0, sb.st_size);
		sl = 1;
		break;

	    case '?':
	    default:
		verifyusage();
		exit(1);
	}
    }

    argc -= optind;
    argv += optind;
    optind = 1;

#ifdef LoopTesting
    optreset = 1;
#endif /* LoopTesting */

    if (sn == 0)
    {
	fprintf(stderr,
		"Should set return values before evaluations begin.\n");
	exit(1);
    }

    if (se == 0)
    {
	fprintf(stderr, "Should set environment before evaluations begin.\n");
	exit(1);
    }

    if (sk == 0)
    {
	fprintf(stderr, "Should specify at least one action authorizer.\n");
	exit(1);
    }

    if (sl == 0)
    {
	fprintf(stderr,
		"Should specify at least one trusted assertion (POLICY).\n");
	exit(1);
    }

    while (argc--)
    {
	if ((fd = open(argv[argc], O_RDONLY, 0)) < 0)
	{
	    perror(argv[argc]);
	    exit(1);
	}

	if (fstat(fd, &sb) < 0)
	{
	    perror("fstat()");
	    exit(1);
	}

	if (sb.st_size > cl - 1)
	{
	    free(buf);
	    cl = sb.st_size + 1;
	    buf = (char *) calloc(cl, sizeof(char));
	    if (buf == (char *) NULL)
	    {
		perror("calloc()");
		exit(1);
	    }
	}

	i = read(fd, buf, sb.st_size);
	if (i < 0)
	{
	    perror("read()");
	    exit(1);
	}

	close(fd);
	p = kn_add_assertion(sessid, buf, i, 0);
	if (p == -1)
	{
	    fprintf(stderr, "Error for assertion in file <%s>, errno %d.\n",
		    argv[argc], keynote_errno);
	    keynote_errno = 0;
	}

	memset(buf, 0, sb.st_size);
    }

    p = kn_do_query(sessid, retv, numret); /* Evaluation time */

#ifndef LoopTesting
    printf("Query result = ");

    switch (keynote_errno)
    {
	case ERROR_MEMORY:
	    printf("<out of memory>\n");
	    exit(1);

	case ERROR_SYNTAX:
	    printf("<uninitialized authorizers or all POLICY "
		   "assertions are malformed!>\n");
	    exit(1);

	case ERROR_NOTFOUND:
	    printf("<session or other information not found!>\n");
	    exit(1);

	case 0:	/* No errors */
	    break;

	default:
	    printf("<should never happen (%d)!>\n", keynote_errno);
	    exit(1);
    }

    printf("%s\n", retv[p]);
#endif /* LoopTesting */

    keynote_errno = 0;

    while ((i = kn_get_failed(sessid, KEYNOTE_ERROR_MEMORY, 0)) != -1)
    {
        printf("Failed assertion %d due to memory error.\n", i);
        kn_remove_assertion(sessid, i);
    }

    while ((i = kn_get_failed(sessid, KEYNOTE_ERROR_SYNTAX, 0)) != -1)
    {
        printf("Failed assertion %d due to syntax or semantic error.\n", i);
        kn_remove_assertion(sessid, i);
    }

    while ((i = kn_get_failed(sessid, KEYNOTE_ERROR_SIGNATURE, 0)) != -1)
    {
        printf("Failed assertion %d due to signature verification failure.\n",
	       i);
        kn_remove_assertion(sessid, i);
    }

    while ((i = kn_get_failed(sessid, KEYNOTE_ERROR_ANY, 0)) != -1)
    {
        printf("Failed assertion %d due to unspecified error.\n", i);
        kn_remove_assertion(sessid, i);
    }

    kn_close(sessid);

#ifdef LoopTesting
    }
#endif /* LoopTesting */

    /* This is a reminder that return values are not free'ed by KeyNote */
    for (sn = 0; sn < numret; sn++)
      free(retv[sn]);
    free(retv);
    retv = (char **) NULL;

    exit(0);
}
