639 lines
15 KiB
C
639 lines
15 KiB
C
/* $OpenBSD: parse_assertion.c,v 1.17 2022/12/27 17:10:06 jmc 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 != 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 = NULL;
|
|
continue;
|
|
}
|
|
|
|
for (i = 0;; i++)
|
|
{
|
|
ast = keynote_find_assertion(kl->key_key, i, kl->key_alg);
|
|
if (ast == 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 = 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 != NULL)
|
|
{
|
|
/* Authorizer */
|
|
if (keynote_evaluate_authorizer(ast, 1) != RESULT_TRUE)
|
|
return -1;
|
|
|
|
/* Signature */
|
|
if ((sigfield) && (ast->as_signature_string_s != 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 != 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 != 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 != 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 beginning 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(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 != NULL;
|
|
as = as->as_next)
|
|
if ((as->as_authorizer != 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 = NULL;
|
|
struct assertion *as;
|
|
|
|
/* Allocate memory for assertion */
|
|
as = calloc(1, sizeof(struct assertion));
|
|
if (as == NULL)
|
|
{
|
|
keynote_errno = ERROR_MEMORY;
|
|
return NULL;
|
|
}
|
|
|
|
/* Keep a copy of the assertion around */
|
|
as->as_buf = strdup(buf);
|
|
if (as->as_buf == NULL)
|
|
{
|
|
keynote_errno = ERROR_MEMORY;
|
|
keynote_free_assertion(as);
|
|
return NULL;
|
|
}
|
|
|
|
as->as_flags = assertion_flags & ~(ASSERT_FLAG_SIGGEN |
|
|
ASSERT_FLAG_SIGVER);
|
|
|
|
/* Skip any leading whitespace */
|
|
for (i = 0, j = len; i < j && isspace((unsigned char)as->as_buf[i]); i++)
|
|
;
|
|
|
|
/* Keyword must start at beginning of buffer or line */
|
|
if ((i >= j) || ((i != 0) && (as->as_buf[i - 1] != '\n')))
|
|
{
|
|
keynote_free_assertion(as);
|
|
keynote_errno = ERROR_SYNTAX;
|
|
return NULL;
|
|
}
|
|
|
|
while (i < j) /* Decomposition loop */
|
|
{
|
|
ks = as->as_buf + i;
|
|
|
|
/* Mark beginning of assertion for signature purposes */
|
|
if (as->as_startofsignature == NULL)
|
|
as->as_startofsignature = ks;
|
|
|
|
/* This catches comments at the beginning 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 NULL;
|
|
}
|
|
|
|
/* ks points at beginning of keyword, ke points at end */
|
|
ke = as->as_buf + i;
|
|
|
|
/* ts points at beginning 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((unsigned char)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 NULL;
|
|
|
|
case KEYWORD_VERSION:
|
|
if ((ver == 1) || (seen_field == 1))
|
|
{
|
|
keynote_free_assertion(as);
|
|
keynote_errno = ERROR_SYNTAX;
|
|
return NULL;
|
|
}
|
|
|
|
/* Test for version correctness */
|
|
keynote_get_envlist(ts, te, 1);
|
|
if (keynote_errno != 0)
|
|
{
|
|
keynote_free_assertion(as);
|
|
return NULL;
|
|
}
|
|
|
|
ver = 1;
|
|
break;
|
|
|
|
case KEYWORD_LOCALINIT:
|
|
if (as->as_env != NULL)
|
|
{
|
|
keynote_free_assertion(as);
|
|
keynote_errno = ERROR_SYNTAX;
|
|
return NULL;
|
|
}
|
|
|
|
as->as_env = keynote_get_envlist(ts, te, 0);
|
|
if (keynote_errno != 0)
|
|
{
|
|
keynote_free_assertion(as);
|
|
return NULL;
|
|
}
|
|
break;
|
|
|
|
case KEYWORD_AUTHORIZER:
|
|
if (as->as_authorizer_string_s != NULL)
|
|
{
|
|
keynote_free_assertion(as);
|
|
keynote_errno = ERROR_SYNTAX;
|
|
return NULL;
|
|
}
|
|
|
|
as->as_authorizer_string_s = ts;
|
|
as->as_authorizer_string_e = te;
|
|
break;
|
|
|
|
case KEYWORD_LICENSEES:
|
|
if (as->as_keypred_s != NULL)
|
|
{
|
|
keynote_free_assertion(as);
|
|
keynote_errno = ERROR_SYNTAX;
|
|
return NULL;
|
|
}
|
|
|
|
as->as_keypred_s = ts;
|
|
as->as_keypred_e = te;
|
|
break;
|
|
|
|
case KEYWORD_CONDITIONS:
|
|
if (as->as_conditions_s != NULL)
|
|
{
|
|
keynote_free_assertion(as);
|
|
keynote_errno = ERROR_SYNTAX;
|
|
return NULL;
|
|
}
|
|
|
|
as->as_conditions_s = ts;
|
|
as->as_conditions_e = te;
|
|
break;
|
|
|
|
case KEYWORD_SIGNATURE:
|
|
if (as->as_signature_string_s != NULL)
|
|
{
|
|
keynote_free_assertion(as);
|
|
keynote_errno = ERROR_SYNTAX;
|
|
return 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 != NULL)
|
|
{
|
|
keynote_free_assertion(as);
|
|
keynote_errno = ERROR_SYNTAX;
|
|
return 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((unsigned char)*(te + k)))
|
|
{
|
|
keynote_free_assertion(as);
|
|
keynote_errno = ERROR_SYNTAX;
|
|
return NULL;
|
|
}
|
|
|
|
break; /* Assertion is "properly" terminated */
|
|
}
|
|
}
|
|
|
|
/* Check that the basic fields are there */
|
|
if (as->as_authorizer_string_s == NULL)
|
|
{
|
|
keynote_free_assertion(as);
|
|
keynote_errno = ERROR_SYNTAX;
|
|
return NULL;
|
|
}
|
|
|
|
/* Signature generation/verification handling */
|
|
if (assertion_flags & ASSERT_FLAG_SIGGEN)
|
|
{
|
|
if (keynote_fix_fields(as, 0) != RESULT_TRUE)
|
|
{
|
|
keynote_free_assertion(as);
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
if (assertion_flags & ASSERT_FLAG_SIGVER)
|
|
if (keynote_fix_fields(as, 1) != RESULT_TRUE)
|
|
{
|
|
keynote_free_assertion(as);
|
|
return NULL;
|
|
}
|
|
|
|
return as;
|
|
}
|