524 lines
11 KiB
C
524 lines
11 KiB
C
/* $OpenBSD: auxil.c,v 1.12 2022/01/14 09:08:03 tb 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 <openssl/dsa.h>
|
|
#include <openssl/rsa.h>
|
|
|
|
#include "keynote.h"
|
|
#include "assertion.h"
|
|
#include "signature.h"
|
|
|
|
/*
|
|
* Get some sort of key-hash for hash table indexing purposes.
|
|
*/
|
|
static int
|
|
keynote_keyhash(void *key, int alg)
|
|
{
|
|
struct keynote_binary *bn;
|
|
unsigned int res = 0, i;
|
|
DSA *dsa;
|
|
RSA *rsa;
|
|
|
|
if (key == NULL)
|
|
return 0;
|
|
|
|
switch (alg)
|
|
{
|
|
case KEYNOTE_ALGORITHM_DSA:
|
|
dsa = (DSA *) key;
|
|
res += BN_mod_word(DSA_get0_p(dsa), HASHTABLESIZE);
|
|
res += BN_mod_word(DSA_get0_q(dsa), HASHTABLESIZE);
|
|
res += BN_mod_word(DSA_get0_g(dsa), HASHTABLESIZE);
|
|
res += BN_mod_word(DSA_get0_pub_key(dsa), HASHTABLESIZE);
|
|
return res % HASHTABLESIZE;
|
|
|
|
case KEYNOTE_ALGORITHM_RSA:
|
|
rsa = (RSA *) key;
|
|
res += BN_mod_word(RSA_get0_n(rsa), HASHTABLESIZE);
|
|
res += BN_mod_word(RSA_get0_e(rsa), HASHTABLESIZE);
|
|
return res % HASHTABLESIZE;
|
|
|
|
case KEYNOTE_ALGORITHM_X509: /* RSA-specific */
|
|
rsa = (RSA *) key;
|
|
res += BN_mod_word(RSA_get0_n(rsa), HASHTABLESIZE);
|
|
res += BN_mod_word(RSA_get0_e(rsa), HASHTABLESIZE);
|
|
return res % HASHTABLESIZE;
|
|
|
|
case KEYNOTE_ALGORITHM_BINARY:
|
|
bn = (struct keynote_binary *) key;
|
|
for (i = 0; i < bn->bn_len; i++)
|
|
res = (res + ((unsigned char) bn->bn_key[i])) % HASHTABLESIZE;
|
|
|
|
return res;
|
|
|
|
case KEYNOTE_ALGORITHM_NONE:
|
|
return keynote_stringhash(key, HASHTABLESIZE);
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return RESULT_TRUE if key appears in the action authorizers.
|
|
*/
|
|
int
|
|
keynote_in_action_authorizers(void *key, int algorithm)
|
|
{
|
|
struct keylist *kl, *kl2;
|
|
void *s;
|
|
int alg;
|
|
|
|
if (algorithm == KEYNOTE_ALGORITHM_UNSPEC)
|
|
{
|
|
kl2 = keynote_keylist_find(keynote_current_assertion->as_keylist, key);
|
|
if (kl2 == NULL)
|
|
return RESULT_FALSE; /* Shouldn't ever happen */
|
|
|
|
s = kl2->key_key;
|
|
alg = kl2->key_alg;
|
|
}
|
|
else
|
|
{
|
|
s = key;
|
|
alg = algorithm;
|
|
}
|
|
|
|
for (kl = keynote_current_session->ks_action_authorizers;
|
|
kl != NULL;
|
|
kl = kl->key_next)
|
|
if ((kl->key_alg == alg) ||
|
|
((kl->key_alg == KEYNOTE_ALGORITHM_RSA) &&
|
|
(alg == KEYNOTE_ALGORITHM_X509)) ||
|
|
((kl->key_alg == KEYNOTE_ALGORITHM_X509) &&
|
|
(alg == KEYNOTE_ALGORITHM_RSA)))
|
|
if (kn_keycompare(kl->key_key, s, alg) == RESULT_TRUE)
|
|
return RESULT_TRUE;
|
|
|
|
return RESULT_FALSE;
|
|
}
|
|
|
|
/*
|
|
* Add a key to the keylist. Return RESULT_TRUE on success, -1 (and set
|
|
* keynote_errno) otherwise. We are not supposed to make a copy of the
|
|
* argument.
|
|
*/
|
|
int
|
|
keynote_keylist_add(struct keylist **keylist, char *key)
|
|
{
|
|
struct keynote_deckey dc;
|
|
struct keylist *kl;
|
|
|
|
if (keylist == NULL)
|
|
{
|
|
keynote_errno = ERROR_MEMORY;
|
|
return -1;
|
|
}
|
|
|
|
kl = calloc(1, sizeof(struct keylist));
|
|
if (kl == NULL)
|
|
{
|
|
keynote_errno = ERROR_MEMORY;
|
|
return -1;
|
|
}
|
|
|
|
if (kn_decode_key(&dc, key, KEYNOTE_PUBLIC_KEY) != 0)
|
|
{
|
|
free(kl);
|
|
return -1;
|
|
}
|
|
|
|
kl->key_key = dc.dec_key;
|
|
kl->key_alg = dc.dec_algorithm;
|
|
kl->key_stringkey = key;
|
|
kl->key_next = *keylist;
|
|
*keylist = kl;
|
|
return RESULT_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Remove an action authorizer.
|
|
*/
|
|
int
|
|
kn_remove_authorizer(int sessid, char *key)
|
|
{
|
|
struct keynote_session *ks;
|
|
struct keylist *kl, *kl2;
|
|
|
|
keynote_errno = 0;
|
|
if ((keynote_current_session == NULL) ||
|
|
(keynote_current_session->ks_id != sessid))
|
|
{
|
|
keynote_current_session = keynote_find_session(sessid);
|
|
if (keynote_current_session == NULL)
|
|
{
|
|
keynote_errno = ERROR_NOTFOUND;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ks = keynote_current_session;
|
|
|
|
/* If no action authorizers present */
|
|
if ((kl = ks->ks_action_authorizers) == NULL)
|
|
{
|
|
keynote_errno = ERROR_NOTFOUND;
|
|
return -1;
|
|
}
|
|
|
|
/* First in list */
|
|
if (!strcmp(kl->key_stringkey, key))
|
|
{
|
|
ks->ks_action_authorizers = kl->key_next;
|
|
kl->key_next = NULL;
|
|
keynote_keylist_free(kl);
|
|
return 0;
|
|
}
|
|
|
|
for (; kl->key_next != NULL; kl = kl->key_next)
|
|
if (!strcmp(kl->key_next->key_stringkey, key))
|
|
{
|
|
kl2 = kl->key_next;
|
|
kl->key_next = kl2->key_next;
|
|
kl2->key_next = NULL;
|
|
keynote_keylist_free(kl2);
|
|
return 0;
|
|
}
|
|
|
|
keynote_errno = ERROR_NOTFOUND;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Add an action authorizer.
|
|
*/
|
|
int
|
|
kn_add_authorizer(int sessid, char *key)
|
|
{
|
|
char *stringkey;
|
|
|
|
keynote_errno = 0;
|
|
if ((keynote_current_session == NULL) ||
|
|
(keynote_current_session->ks_id != sessid))
|
|
{
|
|
keynote_current_session = keynote_find_session(sessid);
|
|
if (keynote_current_session == NULL)
|
|
{
|
|
keynote_errno = ERROR_NOTFOUND;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
stringkey = strdup(key);
|
|
if (stringkey == NULL)
|
|
{
|
|
keynote_errno = ERROR_MEMORY;
|
|
return -1;
|
|
}
|
|
|
|
if (keynote_keylist_add(&(keynote_current_session->ks_action_authorizers),
|
|
stringkey) == -1)
|
|
{
|
|
free(stringkey);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Find a keylist entry based on the key_stringkey entry.
|
|
*/
|
|
struct keylist *
|
|
keynote_keylist_find(struct keylist *kl, char *s)
|
|
{
|
|
for (; kl != NULL; kl = kl->key_next)
|
|
if (!strcmp(kl->key_stringkey, s))
|
|
return kl;
|
|
|
|
return kl;
|
|
}
|
|
|
|
/*
|
|
* Free keylist list.
|
|
*/
|
|
void
|
|
keynote_keylist_free(struct keylist *kl)
|
|
{
|
|
struct keylist *kl2;
|
|
|
|
while (kl != NULL)
|
|
{
|
|
kl2 = kl->key_next;
|
|
free(kl->key_stringkey);
|
|
keynote_free_key(kl->key_key, kl->key_alg);
|
|
free(kl);
|
|
kl = kl2;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Free a key.
|
|
*/
|
|
void
|
|
kn_free_key(struct keynote_deckey *dc)
|
|
{
|
|
if (dc)
|
|
keynote_free_key(dc->dec_key, dc->dec_algorithm);
|
|
}
|
|
|
|
/*
|
|
* Find the num-th assertion given the authorizer. Return NULL if not found.
|
|
*/
|
|
struct assertion *
|
|
keynote_find_assertion(void *authorizer, int num, int algorithm)
|
|
{
|
|
struct assertion *as;
|
|
unsigned int h;
|
|
|
|
if (authorizer == NULL)
|
|
return NULL;
|
|
|
|
h = keynote_keyhash(authorizer, algorithm);
|
|
for (as = keynote_current_session->ks_assertion_table[h];
|
|
as != NULL;
|
|
as = as->as_next)
|
|
if ((as->as_authorizer != NULL) &&
|
|
((as->as_signeralgorithm == algorithm) ||
|
|
((as->as_signeralgorithm == KEYNOTE_ALGORITHM_RSA) &&
|
|
(algorithm == KEYNOTE_ALGORITHM_X509)) ||
|
|
((as->as_signeralgorithm == KEYNOTE_ALGORITHM_X509) &&
|
|
(algorithm == KEYNOTE_ALGORITHM_RSA))))
|
|
if (kn_keycompare(authorizer, as->as_authorizer, algorithm) ==
|
|
RESULT_TRUE)
|
|
if (num-- == 0)
|
|
return as;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Add an assertion to the hash table. Return RESULT_TRUE on success,
|
|
* ERROR_MEMORY for memory failure, ERROR_SYNTAX if some problem with
|
|
* the assertion is detected.
|
|
*/
|
|
int
|
|
keynote_add_htable(struct assertion *as, int which)
|
|
{
|
|
char *hashname;
|
|
unsigned int i;
|
|
|
|
if (as == NULL)
|
|
{
|
|
keynote_errno = ERROR_MEMORY;
|
|
return -1;
|
|
}
|
|
|
|
if (!which)
|
|
hashname = as->as_authorizer_string_s;
|
|
else
|
|
hashname = as->as_authorizer;
|
|
|
|
if (hashname == NULL)
|
|
{
|
|
keynote_errno = ERROR_SYNTAX;
|
|
return -1;
|
|
}
|
|
|
|
i = keynote_keyhash(hashname, as->as_signeralgorithm);
|
|
as->as_next = keynote_current_session->ks_assertion_table[i];
|
|
keynote_current_session->ks_assertion_table[i] = as;
|
|
return RESULT_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Parse and store an assertion in the internal hash table.
|
|
* Return the result of the evaluation, if doing early evaluation.
|
|
* If an error was encountered, set keynote_errno.
|
|
*/
|
|
int
|
|
kn_add_assertion(int sessid, char *asrt, int len, int assertion_flags)
|
|
{
|
|
struct assertion *as;
|
|
|
|
keynote_errno = 0;
|
|
if ((keynote_current_session == NULL) ||
|
|
(keynote_current_session->ks_id != sessid))
|
|
{
|
|
keynote_current_session = keynote_find_session(sessid);
|
|
if (keynote_current_session == NULL)
|
|
{
|
|
keynote_errno = ERROR_NOTFOUND;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
as = keynote_parse_assertion(asrt, len, assertion_flags);
|
|
if ((as == NULL) || (keynote_errno != 0))
|
|
{
|
|
if (keynote_errno == 0)
|
|
keynote_errno = ERROR_SYNTAX;
|
|
|
|
return -1;
|
|
}
|
|
|
|
as->as_id = keynote_current_session->ks_assertioncounter++;
|
|
|
|
/* Check for wrap around...there has to be a better solution to this */
|
|
if (keynote_current_session->ks_assertioncounter < 0)
|
|
{
|
|
keynote_free_assertion(as);
|
|
keynote_errno = ERROR_SYNTAX;
|
|
return -1;
|
|
}
|
|
|
|
if (keynote_add_htable(as, 0) != RESULT_TRUE)
|
|
{
|
|
keynote_free_assertion(as);
|
|
return -1;
|
|
}
|
|
|
|
as->as_internalflags |= ASSERT_IFLAG_NEEDPROC;
|
|
return as->as_id;
|
|
}
|
|
|
|
/*
|
|
* Remove an assertion from the hash table.
|
|
*/
|
|
static int
|
|
keynote_remove_assertion(int sessid, int assertid, int deleteflag)
|
|
{
|
|
struct assertion *ht, *ht2;
|
|
int i;
|
|
|
|
if ((keynote_current_session == NULL) ||
|
|
(keynote_current_session->ks_id != sessid))
|
|
{
|
|
keynote_current_session = keynote_find_session(sessid);
|
|
if (keynote_current_session == NULL)
|
|
{
|
|
keynote_errno = ERROR_NOTFOUND;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < HASHTABLESIZE; i++)
|
|
{
|
|
ht = keynote_current_session->ks_assertion_table[i];
|
|
if (ht == NULL)
|
|
continue;
|
|
|
|
/* If first entry in bucket */
|
|
if (ht->as_id == assertid)
|
|
{
|
|
keynote_current_session->ks_assertion_table[i] = ht->as_next;
|
|
if (deleteflag)
|
|
keynote_free_assertion(ht);
|
|
return 0;
|
|
}
|
|
|
|
for (; ht->as_next != NULL; ht = ht->as_next)
|
|
if (ht->as_next->as_id == assertid) /* Got it */
|
|
{
|
|
ht2 = ht->as_next;
|
|
ht->as_next = ht2->as_next;
|
|
if (deleteflag)
|
|
keynote_free_assertion(ht2);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
keynote_errno = ERROR_NOTFOUND;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* API wrapper for deleting assertions.
|
|
*/
|
|
int
|
|
kn_remove_assertion(int sessid, int assertid)
|
|
{
|
|
keynote_errno = 0;
|
|
return keynote_remove_assertion(sessid, assertid, 1);
|
|
}
|
|
|
|
/*
|
|
* Internally-used wrapper for removing but not deleting assertions.
|
|
*/
|
|
int
|
|
keynote_sremove_assertion(int sessid, int assertid)
|
|
{
|
|
return keynote_remove_assertion(sessid, assertid, 0);
|
|
}
|
|
|
|
/*
|
|
* Free an assertion structure.
|
|
*/
|
|
void
|
|
keynote_free_assertion(struct assertion *as)
|
|
{
|
|
if (as == NULL)
|
|
return;
|
|
|
|
free(as->as_buf);
|
|
|
|
free(as->as_signature);
|
|
|
|
if (as->as_env != NULL)
|
|
keynote_env_cleanup(&(as->as_env), 1);
|
|
|
|
if (as->as_keylist != NULL)
|
|
keynote_keylist_free(as->as_keylist);
|
|
|
|
if (as->as_authorizer != NULL)
|
|
keynote_free_key(as->as_authorizer, as->as_signeralgorithm);
|
|
|
|
free(as);
|
|
}
|
|
|
|
unsigned int
|
|
keynote_stringhash(char *name, unsigned int size)
|
|
{
|
|
unsigned int hash_val = 0;
|
|
unsigned int i;
|
|
|
|
if ((size == 0) || (size == 1))
|
|
return 0;
|
|
|
|
for (; *name; name++)
|
|
{
|
|
hash_val = (hash_val << 2) + *name;
|
|
if ((i = hash_val & 0x3fff) != 0)
|
|
hash_val = ((hash_val ^ (i >> 12)) & 0x3fff);
|
|
}
|
|
|
|
return hash_val % size;
|
|
}
|