/* $OpenBSD: parse_assertion.c,v 1.13 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 <ctype.h>
#include <limits.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "keynote.h"
#include "assertion.h"
#include "signature.h"

/*
 * Recurse on graph discovery.
 */
static int
rec_evaluate_query(struct assertion *as)
{
    struct assertion *ast;
    struct keylist *kl;
    int i, s;

    as->as_kresult = KRESULT_IN_PROGRESS;

    /*
     * If we get the minimum result or an error from evaluating this
     * assertion, we don't need to recurse. 
     */
    keynote_evaluate_assertion(as);
    if (keynote_errno != 0)
    {
	as->as_kresult = KRESULT_DONE;
	if (keynote_errno)
	  as->as_error = keynote_errno;
	if (keynote_errno == ERROR_MEMORY)
	  return -1;
	else
	{
	    keynote_errno = 0;  /* Ignore syntax errors for now */
	    return 0;
	}
    }

    if (as->as_result == 0)
    {
        as->as_kresult = KRESULT_DONE;
        return as->as_result;
    }

    for (kl = as->as_keylist;
	 kl != (struct keylist *) NULL;
	 kl = kl->key_next)
    {
	switch (keynote_in_action_authorizers(kl->key_key, kl->key_alg))
	{
	    case -1:
		as->as_kresult = KRESULT_DONE;
		if (keynote_errno == ERROR_MEMORY)
		{
		    as->as_error = ERROR_MEMORY;
		    return -1;
		}
		else
		{
		    keynote_errno = 0; /* Reset */
		    continue;
		}

	    case RESULT_FALSE:    /* Not there, check for assertions instead */
		break;

	    case RESULT_TRUE:     /* Ok, don't bother with assertions */
		keynote_current_assertion = (struct assertion *) NULL;
		continue;
	}

	for (i = 0;; i++)
	{
	    ast = keynote_find_assertion(kl->key_key, i, kl->key_alg);
	    if (ast == (struct assertion *) NULL)
	      break;

	    if (ast->as_kresult == KRESULT_IN_PROGRESS) /* Cycle detected */
	      continue;

	    if (ast->as_kresult == KRESULT_UNTOUCHED)   /* Recurse if needed */
	      rec_evaluate_query(ast);

	    /* Check for errors */
	    if (keynote_errno == ERROR_MEMORY)
	    {
		as->as_error = ERROR_MEMORY;
		as->as_kresult = KRESULT_DONE;
		return -1;
	    }
	    else
	      keynote_errno = 0; /* Reset */
	}
    }

    keynote_current_assertion = as;
    s = keynote_parse_keypred(as, 0);
    keynote_current_assertion = (struct assertion *) NULL;

    if (keynote_errno == ERROR_MEMORY)
    {
	as->as_error = ERROR_MEMORY;
	as->as_kresult = KRESULT_DONE;
	return -1;
    }
    else
      if (keynote_errno)
      {
	  keynote_errno = 0;
	  s = 0;
      }

    /* Keep lower of two */
    as->as_result = (as->as_result < s ? as->as_result : s);

    /* Check the signature now if we haven't done so already */
    if (as->as_sigresult == SIGRESULT_UNTOUCHED)
    {
	if (!(as->as_flags & ASSERT_FLAG_LOCAL))
	  as->as_sigresult = keynote_sigverify_assertion(as);
	else
	  as->as_sigresult = SIGRESULT_TRUE;    /* Trusted assertion */
    }

    if (as->as_sigresult != SIGRESULT_TRUE)
    {
	as->as_result = 0;
	as->as_sigresult = SIGRESULT_FALSE;
	if (keynote_errno != ERROR_MEMORY)
	  keynote_errno = 0; /* Reset */
	else
	{
	    as->as_error = ERROR_MEMORY;
	    as->as_kresult = KRESULT_DONE;
	    return -1;
	}
    }

    as->as_kresult = KRESULT_DONE;
    return as->as_result;
}

/*
 * Fix the Authorizer/Licencees/Signature fields. If the first argument is
 * empty, fix all assertions. The second argument specifies whether the
 * Signature field should be parsed or not.
 */
static int
keynote_fix_fields(struct assertion *ast, int sigfield)
{
    struct assertion *as;
    int i;
 
    /* Signature generation/verification handling, no need to eval Licensees */
    if (ast != (struct assertion *) NULL)
    {
	/* Authorizer */
	if (keynote_evaluate_authorizer(ast, 1) != RESULT_TRUE)
	  return -1;

	/* Signature */
	if ((sigfield) && (ast->as_signature_string_s != (char *) NULL))
	  if (keynote_evaluate_authorizer(ast, 0) != RESULT_TRUE)
	    return -1;

	return RESULT_TRUE;
    }
    
    for (i = 0; i < HASHTABLESIZE; i++)
      for (as = keynote_current_session->ks_assertion_table[i];
	   as != (struct assertion *) NULL;
	   as = as->as_next)
      {
	  if (!(as->as_internalflags & ASSERT_IFLAG_NEEDPROC) &&
	      !(as->as_internalflags & ASSERT_IFLAG_WEIRDLICS) &&
	      !(as->as_internalflags & ASSERT_IFLAG_WEIRDAUTH) &&
	      !(as->as_internalflags & ASSERT_IFLAG_WEIRDSIG))
	    continue;
	  
	  /* Parse the Signature field */
	  if (((as->as_internalflags & ASSERT_IFLAG_WEIRDSIG) ||
	       (as->as_internalflags & ASSERT_IFLAG_NEEDPROC)) &&
	      (as->as_signature_string_s != (char *) NULL))
	    if (keynote_evaluate_authorizer(as, 0) == -1)
	    {
		if (keynote_errno)
		  as->as_error = keynote_errno;
		if (keynote_errno == ERROR_MEMORY)
		  return -1;
		else
		  keynote_errno = 0;
	    }
	  
	  /* Parse the Licensees field */
	  if ((as->as_internalflags & ASSERT_IFLAG_WEIRDLICS) ||
	      (as->as_internalflags & ASSERT_IFLAG_NEEDPROC))
	    if (keynote_parse_keypred(as, 1) == -1)
	    {
		if (keynote_errno)
		    as->as_error = keynote_errno;
		if (keynote_errno == ERROR_MEMORY)
		  return -1;
		else
		  keynote_errno = 0;
	    }

	  /* Parse the Authorizer field */
	  if ((as->as_internalflags & ASSERT_IFLAG_WEIRDAUTH) ||
	      (as->as_internalflags & ASSERT_IFLAG_NEEDPROC))
	    if (keynote_evaluate_authorizer(as, 1) == -1)
	    {
		if (keynote_errno)
		  as->as_error = keynote_errno;
		if (keynote_errno == ERROR_MEMORY)
		  return -1;
		else
		  keynote_errno = 0;
	    }
      }

    /* Reposition if necessary */
    for (i = 0; i < HASHTABLESIZE; i++)
      for (as = keynote_current_session->ks_assertion_table[i];
	   as != (struct assertion *) NULL;
	   as = as->as_next)
	if (((as->as_internalflags & ASSERT_IFLAG_WEIRDAUTH) &&
	     !(as->as_internalflags & ASSERT_IFLAG_PROCESSED)) ||
	    (as->as_internalflags & ASSERT_IFLAG_NEEDPROC))
	{
	    as->as_internalflags &= ~ASSERT_IFLAG_NEEDPROC;
	    as->as_internalflags |= ASSERT_IFLAG_PROCESSED;
	    keynote_sremove_assertion(keynote_current_session->ks_id,
				      as->as_id);

	    if (keynote_add_htable(as, 1) != RESULT_TRUE)
	      return -1;

	    /* Point to begining of the previous list. */
	    i--;
	    break;
	}

    return RESULT_TRUE;
}

/*
 * Find the trust graph. This is a depth-first search, starting at
 * POLICY assertions.
 */
int
keynote_evaluate_query(void)
{
    struct assertion *as;
    int p, prev;
    int i;

    /* Fix the authorizer/licensees/signature fields */
    if (keynote_fix_fields((struct assertion *) NULL, 0) != RESULT_TRUE)
      return -1;

    /* Find POLICY assertions and try to evaluate the query. */
    for (i = 0, prev = 0; i < HASHTABLESIZE; i++)
      for (as = keynote_current_session->ks_assertion_table[i];
	   as != (struct assertion *) NULL;
	   as = as->as_next)
	if ((as->as_authorizer != (void *) NULL) &&      /* Paranoid */
            (as->as_signeralgorithm == KEYNOTE_ALGORITHM_NONE))
	  if ((!strcmp("POLICY", as->as_authorizer)) &&
	      (as->as_flags & ASSERT_FLAG_LOCAL))
	  {
	      if ((p = rec_evaluate_query(as)) == -1)
	      {
		  if (keynote_errno)
		    as->as_error = keynote_errno;
		  if (keynote_errno == ERROR_MEMORY)
		    return -1;
		  else
		  {
		      keynote_errno = 0;
		      continue;
		  }
	      }

	      if (p > prev)
		prev = p;

	      /* If we get the highest possible return value, just return */
	      if (prev == (keynote_current_session->ks_values_num - 1))
		return prev;
	  }
    
    return prev;
}

/*
 * Return keyword type.
 */
static int
whichkeyword(char *start, char *end)
{
    int len = (end - start);

    if (len <= 0)
    {
	keynote_errno = ERROR_MEMORY;
	return -1;
    }

    if (!strncasecmp("keynote-version:", start, len))
      return KEYWORD_VERSION;

    if (!strncasecmp("local-constants:", start, len))
      return KEYWORD_LOCALINIT;

    if (!strncasecmp("authorizer:", start, len))
      return KEYWORD_AUTHORIZER;

    if (!strncasecmp("licensees:", start, len))
      return KEYWORD_LICENSEES;

    if (!strncasecmp("conditions:", start, len))
      return KEYWORD_CONDITIONS;

    if (!strncasecmp("signature:", start, len))
      return KEYWORD_SIGNATURE;

    if (!strncasecmp("comment:", start, len))
      return KEYWORD_COMMENT;

    keynote_errno = ERROR_SYNTAX;
    return -1;
}

/* 
 * Parse an assertion. Set keynote_errno to ERROR_SYNTAX if parsing
 * failed due to certificate badness, and ERROR_MEMORY if memory
 * problem. If more than one assertions have been passed in the
 * buffer, they will be linked.
 */
struct assertion *
keynote_parse_assertion(char *buf, int len, int assertion_flags)
{
    int k, i, j, seen_field = 0, ver = 0, end_of_assertion = 0;
    char *ks, *ke, *ts, *te = (char *) NULL;
    struct assertion *as;

    /* Allocate memory for assertion */
    as = (struct assertion *) calloc(1, sizeof(struct assertion));
    if (as == (struct assertion *) NULL)
    {
	keynote_errno = ERROR_MEMORY;
	return (struct assertion *) NULL;
    }

    /* Keep a copy of the assertion around */
    as->as_buf = strdup(buf);
    if (as->as_buf == (char *) NULL)
    {
	keynote_errno = ERROR_MEMORY;
	keynote_free_assertion(as);
	return (struct assertion *) NULL;
    }

    as->as_flags = assertion_flags & ~(ASSERT_FLAG_SIGGEN |
				       ASSERT_FLAG_SIGVER);

    /* Skip any leading whitespace */
    for (i = 0, j = len; i < j && isspace((int) as->as_buf[i]); i++)
     ;

    /* Keyword must start at begining of buffer or line */
    if ((i >= j) || ((i != 0) && (as->as_buf[i - 1] != '\n')))
    {
	keynote_free_assertion(as);
	keynote_errno = ERROR_SYNTAX;
	return (struct assertion *) NULL;
    }

    while (i < j)			/* Decomposition loop */
    {
	ks = as->as_buf + i;

	/* Mark begining of assertion for signature purposes */
	if (as->as_startofsignature == (char *) NULL)
	  as->as_startofsignature = ks;

	/* This catches comments at the begining of an assertion only */
	if (as->as_buf[i] == '#')	/* Comment */
	{
	    seen_field = 1;

   	    /* Skip until the end of line */
	    while ((i< j) && as->as_buf[++i] != '\n')
	      ;

	    i++;
	    continue;  /* Loop */
	}

	/* Advance until we find a keyword separator */
	for (; (as->as_buf[i] != ':') && (i < j); i++)
	  ;

	if (i + 1 > j)
	{
	    keynote_free_assertion(as);
	    keynote_errno = ERROR_SYNTAX;
	    return (struct assertion *) NULL;
	}

	/* ks points at begining of keyword, ke points at end */
	ke = as->as_buf + i;

	/* ts points at begining of value field */
	ts = as->as_buf + i + 1;	/* Skip ':' */

	/*
	 * Find the end of the field -- means end of buffer,
	 * a newline followed by a non-whitespace character,
	 * or two newlines.
	 */
	while (++i <= j)
	{
	    /* If end of buffer, we're at the end of the field */
	    if (i == j)
	    {
		end_of_assertion = 1;
		te = as->as_buf + i;
		break;
	    }

	    /* If two newlines, end of assertion */
	    if ((as->as_buf[i] == '\n') && (i + 1 < j) && 
		(as->as_buf[i + 1] == '\n'))
	    {
		end_of_assertion = 1;
		te = as->as_buf + i;
		break;
	    }

	    /* If newline followed by non-whitespace or comment character */
	    if ((as->as_buf[i] == '\n') && 
		(!isspace((int) as->as_buf[i + 1])) &&
                (as->as_buf[i + 1] != '#'))
	    {
	        te = as->as_buf + i;
	        break;
	    }
	}

	i++;

	/* 
	 * On each of the cases (except the first), we check that:
	 *  - we've already seen a keynote-version field (and that
	 *    it's the first one that appears in the assertion)
	 *  - the signature field, if present, is the last one
	 *  - no field appears more than once
	 */
	switch (whichkeyword(ks, ke))
	{
	    case -1:
		keynote_free_assertion(as);
		return (struct assertion *) NULL;

	    case KEYWORD_VERSION:
		if ((ver == 1) || (seen_field == 1))
		{
		    keynote_free_assertion(as);
		    keynote_errno = ERROR_SYNTAX;
		    return (struct assertion *) NULL;
		}

		/* Test for version correctness */
		keynote_get_envlist(ts, te, 1);
		if (keynote_errno != 0)
		{
		    keynote_free_assertion(as);
		    return (struct assertion *) NULL;
		}

		ver = 1;
		break;

	    case KEYWORD_LOCALINIT:
		if (as->as_env != (struct environment *) NULL)
		{
		    keynote_free_assertion(as);
		    keynote_errno = ERROR_SYNTAX;
		    return (struct assertion *) NULL;
		}

		as->as_env = keynote_get_envlist(ts, te, 0);
		if (keynote_errno != 0)
		{
		    keynote_free_assertion(as);
		    return (struct assertion *) NULL;
		}
		break;

	    case KEYWORD_AUTHORIZER:
		if (as->as_authorizer_string_s != (void *) NULL)
		{
		    keynote_free_assertion(as);
		    keynote_errno = ERROR_SYNTAX;
		    return (struct assertion *) NULL;
		}

		as->as_authorizer_string_s = ts;
		as->as_authorizer_string_e = te;
		break;

	    case KEYWORD_LICENSEES:
		if (as->as_keypred_s != (char *) NULL)
		{
		    keynote_free_assertion(as);
		    keynote_errno = ERROR_SYNTAX;
		    return (struct assertion *) NULL;
		}

		as->as_keypred_s = ts;
		as->as_keypred_e = te;
		break;

	    case KEYWORD_CONDITIONS:
		if (as->as_conditions_s != (char *) NULL)
		{
		    keynote_free_assertion(as);
		    keynote_errno = ERROR_SYNTAX;
		    return (struct assertion *) NULL;
		}

		as->as_conditions_s = ts;
		as->as_conditions_e = te;
		break;

	    case KEYWORD_SIGNATURE:
		if (as->as_signature_string_s != (char *) NULL)
		{
		    keynote_free_assertion(as);
		    keynote_errno = ERROR_SYNTAX;
		    return (struct assertion *) NULL;
		}

		end_of_assertion = 1;
		as->as_allbutsignature = ks;
		as->as_signature_string_s = ts;
		as->as_signature_string_e = te;
		break;

	    case KEYWORD_COMMENT:
		if (as->as_comment_s != (char *) NULL)
		{
		    keynote_free_assertion(as);
		    keynote_errno = ERROR_SYNTAX;
		    return (struct assertion *) NULL;
		}

		as->as_comment_s = ts;
		as->as_comment_e = te;
		break;
	}

	seen_field = 1;
	if (end_of_assertion == 1)
	{
	    /* End of buffer, good termination */
	    if ((te == as->as_buf + len) || (te + 1 == as->as_buf + len) ||
		(*(te) == '\0') || (*(te + 1) == '\0'))
	      break;

	    /* Check whether there's something else following */
	    for (k = 1; te + k < as->as_buf + len && *(te + k) != '\n'; k++)   
	      if (!isspace((int) *(te + k)))
	      {
		  keynote_free_assertion(as);
		  keynote_errno = ERROR_SYNTAX;
		  return (struct assertion *) NULL;
	      }

	    break; /* Assertion is "properly" terminated */
	}
    }

    /* Check that the basic fields are there */
    if (as->as_authorizer_string_s == (char *) NULL)
    {
	keynote_free_assertion(as);
	keynote_errno = ERROR_SYNTAX;
	return (struct assertion *) NULL;
    }

    /* Signature generation/verification handling */
    if (assertion_flags & ASSERT_FLAG_SIGGEN)
    {
        if (keynote_fix_fields(as, 0) != RESULT_TRUE)
        {
	    keynote_free_assertion(as);
	    return (struct assertion *) NULL;
        }
    }
    else
      if (assertion_flags & ASSERT_FLAG_SIGVER)
	if (keynote_fix_fields(as, 1) != RESULT_TRUE)
	{
	    keynote_free_assertion(as);
	    return (struct assertion *) NULL;
	}

    return as;
}
