diff options
author | Angelos D. Keromytis <angelos@cvs.openbsd.org> | 1999-05-23 22:11:10 +0000 |
---|---|---|
committer | Angelos D. Keromytis <angelos@cvs.openbsd.org> | 1999-05-23 22:11:10 +0000 |
commit | dbaaafa705ca548059aac958082776f219b2daef (patch) | |
tree | e6e62c393c7d7a9d42f10a0efc315995b73d3ca8 | |
parent | c0c2076f62b1c430c14737fbd3d8896a37bd2f6b (diff) |
KeyNote version 2 trust-management system (security policy handling).
Utilities to follow.
46 files changed, 11142 insertions, 0 deletions
diff --git a/lib/libkeynote/HOWTO.add.crypto b/lib/libkeynote/HOWTO.add.crypto new file mode 100644 index 00000000000..16abff224d7 --- /dev/null +++ b/lib/libkeynote/HOWTO.add.crypto @@ -0,0 +1,71 @@ +# $OpenBSD: HOWTO.add.crypto,v 1.1 1999/05/23 22:11:04 angelos Exp $ + +This document describes how to add support for digital signature algorithms, +hash functions, and ASCII encoding mechanisms in this implementation. + +For a signature algorithm: + +- Add the appropriate include files in keynote.h +- Create one or more strings describing the signature algorithm + prefixes, and add those to keynote.h (the SIG_* definitions). +- Add a definition for the algorithm in keynote.h (the + KEYNOTE_ALGORITHM_* definitions). +- Define the algorithm public key prefixes, and add them to + signature.h (the *_HEX, *_HEX_LEN, *_BASE64, *_BASE64_LEN + definitions). +- In aux.c, function keynote_keyhash(), add to the switch statement + a case handling the new algorithm; the return value is an integer, + and is used as an index into a hash table. +- In signature.c: + - In keynote_free_key(), add code to free any memory allocated for + storing a key for the new algorithm. + - In keynote_get_sig_algorithm(), add code that checks whether a + signature string begins with one of the prefixes for the new + algorithm that were defined in keynote.h + - Similarly, in keynote_get_key_algorithm() for key strings, using + the key prefixes defined in signature.h + - In kn_decode_key(), add code that converts a bit string to + the new algorithm's structure for storing a key (use the DSA + code as a guide). + - Similarly for kn_encode_key() + - In keynote_keycompare(), add code that compares two keys and + returns RETURN_TRUE if they are equal, and RETURN_FALSE otherwise. + - In keynote_signverify_assertion, add code that verifies a + signature for the new algorithm. + - Likewise for signature generation in keynote_sign_assertion() +- In keynote-keygen.c, replicate the code for DSA key generation to + support the new algorithm. + +For a hash algorithm: + +- Add the necessary include files in keynote.h +- Add a KEYNOTE_HASH_* definition for the algorithm in signature.h +- In signature.h, if the length of the new hash function's result is + more than LARGEST_HASH_SIZE (currently 20 bytes, for SHA1), then + replace that value with the new function's hash result length. +- In signature.c: + - In keynote_sigverify_assertion(), add code in the switch statement + for generating a hash of the assertion and the signature algorithm + name (use the SHA1 code as an example). + - Likewise in keynote_sign_assertion() + +For an ASCII-encoding algorithm: + +- Add the necessary include files in keynote.h +- Add additional SIG_* definitions in keynote.h +- Add an ENCODING_* definition in keynote.h +- Add additional key prefix string definitions in signature.h +- In signature.c: + - In keynote_get_sig_algorithm(), add code for detecting signatures + with this encoding. + - Likewise for keys in keynote_get_key_algorithm() + - In kn_decode_key(), add code in the switch statement for decoding + ASCII-encoded keys. + - Likewise in kn_encode_key() for encoding keys. + - Likewise in keynote_sigverify_assertion() for decoding signatures. + - Add the necessary checks in keynote_sign_assertion() for handling + the new encoding, and code in the switch statement for doing the + encoding of the signature. +- Add the necessary checks in keynote-keygen.c for handling the + new algorithm. + diff --git a/lib/libkeynote/LICENSE b/lib/libkeynote/LICENSE new file mode 100644 index 00000000000..eb5d22d59be --- /dev/null +++ b/lib/libkeynote/LICENSE @@ -0,0 +1,21 @@ +/* $OpenBSD: LICENSE,v 1.1 1999/05/23 22:11:07 angelos 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 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. + */ diff --git a/lib/libkeynote/Makefile b/lib/libkeynote/Makefile new file mode 100644 index 00000000000..a39407f390b --- /dev/null +++ b/lib/libkeynote/Makefile @@ -0,0 +1,31 @@ +# $OpenBSD: Makefile,v 1.1 1999/05/23 22:11:04 angelos Exp $ + +LIB= keynote +MAN= man/keynote.3 man/keynote.4 + +CFLAGS+= -DCRYPTO +LEXFLAGS = -Cr -Pkn -s -i +YACCFLAGS = -d -p kn -b k + +HDRS= keynote.h +SRCS= k.tab.c lex.kn.c environment.c parse_assertion.c signature.c aux.c \ + base64.c + +CLEANFILES+= k.tab.c lex.kn.c k.tab.h + +k.tab.c: keynote.y environment.h signature.h + $(YACC) $(YACCFLAGS) keynote.y + +lex.kn.c: keynote.l k.tab.h environment.h assertion.h signature.h + $(LEX.l) $(LEXFLAGS) keynote.l + +includes: + @cd ${.CURDIR}; for i in $(HDRS); do \ + j="cmp -s $$i ${DESTDIR}/usr/include/$$i || \ + ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m 444 $$i \ + ${DESTDIR}/usr/include"; \ + echo $$j; \ + eval "$$j"; \ + done + +.include <bsd.lib.mk> diff --git a/lib/libkeynote/Makefile.dist b/lib/libkeynote/Makefile.dist new file mode 100644 index 00000000000..b893d505050 --- /dev/null +++ b/lib/libkeynote/Makefile.dist @@ -0,0 +1,180 @@ +# +# 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 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. + +VERSION = 2-beta2 +DISTFILE = keynote-${VERSION}.tar.gz +KNSUBDIR = KeyNote-${VERSION} + +RANLIB = ranlib +MKDIR = mkdir +YACC = yacc +#YACC = bison +TRUE = true +LEX = flex +TAR = tar +CC = gcc +RM = rm +AR = ar +NROFF = nroff + +TARFLAGS = -cvzf ${DISTFILE} +YACCFLAGS2 = -d -p kv -b z +YACCFLAGS = -d -p kn -b k +LEXFLAGS2 = -Pkv -s -i +LEXFLAGS = -Cr -Pkn -s -i +CFLAGS = -O2 #-Wall -g +RMFLAGS2 = -rf +RMFLAGS = -f +NROFFFLAGS = -mandoc + +# SSLeay/OpenSSL pointers +SSLINC = -I/usr/local/include -I/usr/local/ssl/include \ + -I/usr/local/openssl/include +SSLLIB = -L/usr/lib -L/usr/local/lib -L/usr/local/ssl/lib \ + -L/usr/local/openssl/lib -L/usr/local/openssl/ -lcrypto + +# No-crypto compile/link flags and definitions +NOCRYPTODEFS = #-DPGPLIB -DNO_SNPRINTF -DNEED_GETOPT -DPILOT +NOCRYPTOINC = -I. +NOCRYPTOLIBS = -L. -lkeynote -lm + +# Final compile/link flags and definitions +DEFS = -DCRYPTO ${NOCRYPTODEFS} +INC = $(SSLINC) ${NOCRYPTOINC} +LIBS = ${NOCRYPTOLIBS} ${SSLLIB} + +TARGET = libkeynote.a +TARGET2 = keynote-verify +TARGET3 = keynote-sign +TARGET4 = keynote-sigver +TARGET5 = keynote-keygen + +#GETOPT = getopt.o +OBJS = k.tab.o lex.kn.o environment.o parse_assertion.o \ + signature.o aux.o base64.o $(GETOPT) +OBJS2 = z.tab.o lex.kv.o keynote-verify.o +OBJS3 = keynote-sign.o +OBJS4 = keynote-sigver.o +OBJS5 = keynote-keygen.o + +crypto: all + +nocrypto: + ${MAKE} LIBS="${NOCRYPTOLIBS}" INC="${NOCRYPTOINC}" \ + DEFS="${NOCRYPTODEFS}" + +all: $(TARGET) $(TARGET2) $(TARGET3) $(TARGET4) $(TARGET5) + +library: $(TARGET) + +$(TARGET): $(OBJS) + $(AR) -cvr $(TARGET) $(OBJS) + $(RANLIB) $(TARGET) + +$(TARGET2): $(TARGET) $(OBJS2) + $(CC) $(CFLAGS) -o $(TARGET2) $(OBJS2) $(LIBS) + +$(TARGET3): $(TARGET) $(OBJS3) + $(CC) $(CFLAGS) -o $(TARGET3) $(OBJS3) $(LIBS) + +$(TARGET4): $(TARGET) $(OBJS4) + $(CC) $(CFLAGS) -o $(TARGET4) $(OBJS4) $(LIBS) + +$(TARGET5): $(TARGET) $(OBJS5) + $(CC) $(CFLAGS) -o $(TARGET5) $(OBJS5) $(LIBS) + +k.tab.c: keynote.y environment.h signature.h + $(YACC) $(YACCFLAGS) keynote.y + +z.tab.c: keynote-ver.y keynote.h + $(YACC) $(YACCFLAGS2) keynote-ver.y + +lex.kn.c: keynote.l k.tab.h environment.h assertion.h signature.h + $(LEX) $(LEXFLAGS) keynote.l + +lex.kv.c: keynote-ver.l z.tab.h keynote.h + $(LEX) $(LEXFLAGS2) keynote-ver.l + +.c.o: + $(CC) $(CFLAGS) $(DEFS) $(INC) -c $< + +aux.c: environment.h signature.h +parse_assertion.c: assertion.h environment.h signature.h +environment.c: environment.h +keynote-verify.c: keynote.h +environment.h: keynote.h +assertion.h: keynote.h +signature.h: assertion.h +signature.c: signature.h +keynote-keygen.c: signature.h +keynote-sign.c: signature.h +keynote-sigver.c: signature.h +base64.c: keynote.h + +clean: + $(RM) $(RMFLAGS) $(OBJS) $(OBJS2) $(OBJS3) $(OBJS4) $(OBJS5) + $(RM) $(RMFLAGS) a.out *.core *~ */*~ + +cleandir: cleanall + +cleanall: clean + $(RM) $(RMFLAGS) *.o k.tab.c lex.kn.c k.tab.h z.tab.c z.tab.h + $(RM) $(RMFLAGS) lex.kv.c y.output z.output ${DISTFILE} + $(RM) $(RMFLAGS) $(TARGET) $(TARGET2) $(TARGET3) + $(RM) $(RMFLAGS) $(TARGET4) $(TARGET5) man/*.0 + +test: all + ./$(TARGET2) -e testsuite/test-env \ + -r false,maybe,probably,true \ + -k testsuite/auth1 -k testsuite/auth2 -k testsuite/auth3 \ + -k testsuite/auth4 \ + -l testsuite/test-assertion1 -l testsuite/test-assertion2 \ + -l testsuite/test-assertion3 -l testsuite/test-assertion4 \ + -l testsuite/test-assertion5 -l testsuite/test-assertion6 \ + -l testsuite/test-assertion7 || ${TRUE} + +manpages: mankeynote mansystem mansign manver mansigver mankeygen + +mankeynote: + ${NROFF} ${NROFFFLAGS} man/keynote.3 > man/keynote.0 + +mansystem: + ${NROFF} ${NROFFFLAGS} man/keynote.4 > man/keynote-system.0 + +mansign: + ${NROFF} ${NROFFFLAGS} man/keynote-sign.1 > man/keynote-sign.0 + +mansigver: + ${NROFF} ${NROFFFLAGS} man/keynote-sigver.1 > man/keynote-sigver.0 + +manver: + ${NROFF} ${NROFFFLAGS} man/keynote-verify.1 > man/keynote-verify.0 + +mankeygen: + ${NROFF} ${NROFFFLAGS} man/keynote-keygen.1 > man/keynote-keygen.0 + +distribution: test cleanall manpages + ${MKDIR} ${KNSUBDIR} + $(TAR) cf - . | (cd ${KNSUBDIR}; ${TAR} xf -) + ${RM} ${RMFLAGS2} ${KNSUBDIR}/CVS ${KNSUBDIR}/testsuite/CVS \ + ${KNSUBDIR}/Misc/CVS ${KNSUBDIR}/${KNSUBDIR} \ + ${KNSUBDIR}/.cvsignore ${KNSUBDIR}/man/CVS \ + ${KNSUBDIR}/man/.cvsignore ${KNSUBDIR}/doc/CVS + $(TAR) $(TARFLAGS) ${KNSUBDIR} + ${RM} ${RMFLAGS2} ${KNSUBDIR} diff --git a/lib/libkeynote/Misc/getopt.c b/lib/libkeynote/Misc/getopt.c new file mode 100644 index 00000000000..4e12b012775 --- /dev/null +++ b/lib/libkeynote/Misc/getopt.c @@ -0,0 +1,83 @@ +/* $OpenBSD: getopt.c,v 1.1 1999/05/23 22:11:07 angelos Exp $ */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +/*** getopt + * + * This function is the public domain version of getopt, the command + * line argument processor, and hence is NOT copyrighted by Microsoft, + * IBM, or AT&T. + */ + +#define ERR(s, c) if(opterr){\ + (void) fputs(argv[0], stderr);\ + (void) fputs(s, stderr);\ + (void) fputc(c, stderr);\ + (void) fputc('\n', stderr);} + +int opterr = 1; /* flag:error message on unrecognzed options */ +int optind = 1; /* last touched cmdline argument */ +int optopt; /* last returned option */ +char *optarg; /* argument to optopt */ +int getopt(int argc, char **argv, char *opts); + +/* int argc is the number of arguments on cmdline */ +/* char **argv is the pointer to array of cmdline arguments */ +/* char *opts is the string of all valid options */ +/* each char case must be given; options taking an arg are followed by = +':' */ +int getopt(int argc, char **argv, char *opts) +{ + static int sp = 1; + register int c; + register char *cp; + if(sp == 1) + /* check for end of options */ + if(optind >= argc || + (argv[optind][0] != '/' && + argv[optind][0] != '-') || + argv[optind][1] == '\0') + return(EOF); + else if(!strcmp(argv[optind], "--")) { + optind++; + return(EOF); + } + optopt = c = argv[optind][sp]; + if(c == ':' || (cp=strchr(opts, c)) == NULL) { + /* if arg sentinel as option or other invalid option, + handle the error and return '?' */ + ERR(": illegal option -- ", (char)c); + if(argv[optind][++sp] == '\0') { + optind++; + sp = 1; + } + return('?'); + } + if(*++cp == ':') { + /* if option is given an argument... */ + if(argv[optind][sp+1] != '\0') + /* and the OptArg is in that CmdLineArg, return it... */ + optarg = &argv[optind++][sp+1]; + else if(++optind >= argc) { + /* but if the OptArg isn't there and the next CmdLineArg + isn't either, handle the error... */ + ERR(": option requires an argument -- ", (char)c); + sp = 1; + return('?'); + } else + /* but if there is another CmdLineArg there, return that */ + optarg = argv[optind++]; + /* and set up for the next CmdLineArg */ + sp = 1; + } else { + /* no arg for this opt, so null arg and set up for next option */ + if(argv[optind][++sp] == '\0') { + sp = 1; + optind++; + } + optarg = NULL; + } + return(c); +} diff --git a/lib/libkeynote/Misc/getopt.h b/lib/libkeynote/Misc/getopt.h new file mode 100644 index 00000000000..17211043f1f --- /dev/null +++ b/lib/libkeynote/Misc/getopt.h @@ -0,0 +1,6 @@ +/* $OpenBSD: getopt.h,v 1.1 1999/05/23 22:11:07 angelos Exp $ */ + +extern int opterr; /* flag:error message on unrecognzed options */ +extern int optind; /* last touched cmdline argument */ +extern char *optarg; /* argument to optopt */ +int getopt(int argc, char **argv, char *opts); diff --git a/lib/libkeynote/Misc/keynote.btm b/lib/libkeynote/Misc/keynote.btm new file mode 100644 index 00000000000..434c5286420 --- /dev/null +++ b/lib/libkeynote/Misc/keynote.btm @@ -0,0 +1,14 @@ +#define keynote1_width 32 +#define keynote1_height 32 +static unsigned char keynote1_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0xec, 0xff, 0x01, 0x00, 0xc4, 0xff, 0x01, 0x00, 0xec, 0xff, 0x00, 0x00, + 0xfc, 0x90, 0x00, 0x00, 0x78, 0xd0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x1f, 0x00, 0x10, 0x00, 0x10, 0x00, + 0xd0, 0xff, 0x3f, 0x00, 0x50, 0x00, 0x20, 0x00, 0x50, 0xff, 0x2f, 0x00, + 0x50, 0x00, 0x20, 0x00, 0x50, 0xff, 0x23, 0x00, 0x50, 0x00, 0x20, 0x00, + 0x50, 0xff, 0x2f, 0x00, 0x50, 0x00, 0x20, 0x00, 0x50, 0xff, 0x27, 0x00, + 0x50, 0x00, 0x20, 0x00, 0x50, 0x7f, 0x20, 0x00, 0x50, 0x00, 0x20, 0x00, + 0x50, 0x00, 0x20, 0x00, 0x50, 0x00, 0x20, 0x00, 0x50, 0x00, 0x20, 0x00, + 0x50, 0x00, 0x20, 0x00, 0x50, 0x00, 0x20, 0x00, 0x70, 0x00, 0x20, 0x00, + 0xc0, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/lib/libkeynote/Misc/keynote.gif b/lib/libkeynote/Misc/keynote.gif Binary files differnew file mode 100644 index 00000000000..577ca8c5f3e --- /dev/null +++ b/lib/libkeynote/Misc/keynote.gif diff --git a/lib/libkeynote/README b/lib/libkeynote/README new file mode 100644 index 00000000000..4eba6dcbe47 --- /dev/null +++ b/lib/libkeynote/README @@ -0,0 +1,97 @@ +# $OpenBSD: README,v 1.1 1999/05/23 22:11:03 angelos Exp $ + +This is release 2-beta2 of the KeyNote trust management library reference +implementation. + +For details on the KeyNote spec, read the file keynote-spec, included in +this distribution (in the doc/ directory). + +To build the distribution, just type "make" or "make crypt". To test the +distribution, type "make test". The query should evaluate to "true" (look +at the last few lines of output). To build without crypto support, use +"make nocrypto". + +Compile tips: +- You need the SSLeay/OpenSSL library if you compile with crypto + (default), version 0.8.1b or later. You can find it in various + crypto software repositories, or at: + ftp://ftp.psy.uq.oz.au/pub/Crypto/SSL/ + + OpenSSL can be found at: + http://www.openssl.com/ + + Edit this distribution's Makefile, changing the variables SSLINC and + SSLLIB to reflect the location of the include files and libraries + respectively for SSLeay/OpenSSL. + +- Similarly, if you compile with -DPGPLIB you will need PGPlib-1.1 + from ftp://dslab1.cs.uit.no/pub/PGPlib-1.1.tar.gz + ** Notice: there is no support for PGPLIB yet ** + + Make sure PGPINC and PGPLIB (in Makefile) point at the right + locations for the include files and the library respectively. + +- You may need to add support for initialization of the random + generator routines. There is currently support for most BSDs and + Linux. Look in keynote-keygen.c and environment.c for calls to + RAND_seed(). Bear in mind that you need high-quality + (cryptographic-grade) randomness. + +- If your system does not have snprintf(), uncomment the -DNO_SNPRINTF + in the Makefile (NOCRYPTODEFS variable). + +- If your system does not have getopt(), move the files getopt.c and + getopt.h from Misc/ and uncomment the GETOPT line in the Makefile, and + enable the -DNEED_GETOPT flag in NOCRYPTODEFS (you do not need to + for Windows). + +- For Windows, you should be able to compile using Visual C++ without + too much trouble (thanks to Dave Clark for testing release 0.1). You + can get a copy of a regular expression library from the KeyNote web + page (see below). + +The Makefile creates the libkeynote.a library and the keynote-verify, +keynote-keygen, keynote-sigver, and keynote-sign programs. There's a +man page for the library calls (keynote.3) and one for each of the +utilities in the man/ directory. There is also a man page about KeyNote +itself (keynote.4), which contains some text from the spec. + +To view them, use: + + nroff -mandoc keynote.3 | more + nroff -mandoc keynote.4 | more + nroff -mandoc keynote-verify.1 | more + nroff -mandoc keynote-keygen.1 | more + nroff -mandoc keynote-sign.1 | more + nroff -mandoc keynote-sigver.1 | more + +Alternatively, you can just install them in your manpath. If your +nroff does not support the -mandoc flag, use -man instead. For those +systems that do not have nroff, the text version of the man pages are +provided as well (the files with .0 suffixes in the same directory). + +The keynote-verify program can be used to verify a request, given a +set of assertions and an environment file. The directory testsuite/ +has some examples assertions. The keynote-keygen program can +be used to generate keys. The keynote-sign and keynote-sigver can be +used to sign assertions, and verify signed assertions respectively. + +The file base64.c was taken from the OpenBSD libc and was slightly +modified. + +Read the TODO file to see what's missing (and eventually coming). + +When in doubt on how to use a library call (despite the man pages), +consult the implementation of the various utilities. + +For any questions, comments, bug reports, praise, or anything else, +contact us at keynote@research.att.com + +There is also a users mailing list at keynote-users@nsa.research.att.com +To subscribe, send a message to majordomo@nsa.research.att.com with the word +"subscribe keynote-users" (without the quotes) in the message body. + +Finally, there is a web page for KeyNote at + http://www.cis.upenn.edu/~keynote + +Angelos D. Keromytis diff --git a/lib/libkeynote/TODO b/lib/libkeynote/TODO new file mode 100644 index 00000000000..7675ef9adbe --- /dev/null +++ b/lib/libkeynote/TODO @@ -0,0 +1,14 @@ +# $OpenBSD: TODO,v 1.1 1999/05/23 22:11:03 angelos Exp $ + +Short term TODOs: + - More interesting/comprehensive testsuite + - Add the proper RFC reference to the manpages and README + - Write key/signature algorithm draft(s) + - autoconf + - Document X509 support + +Long term TODOs: + - Some sort of tcl/tk GUI + - PalmPilot support + - PGPlib support + - ElGamal support diff --git a/lib/libkeynote/assertion.h b/lib/libkeynote/assertion.h new file mode 100644 index 00000000000..d559e52beeb --- /dev/null +++ b/lib/libkeynote/assertion.h @@ -0,0 +1,106 @@ +/* $OpenBSD: assertion.h,v 1.1 1999/05/23 22:11:03 angelos 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 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. + */ + +#ifndef __ASSERTION_H__ +#define __ASSERTION_H__ + +#include "keynote.h" + +struct keylist +{ + int key_alg; + void *key_key; + char *key_stringkey; + struct keylist *key_next; +}; + +struct assertion +{ + void *as_authorizer; + char *as_buf; + char *as_signature; + char *as_authorizer_string_s; + char *as_authorizer_string_e; + char *as_keypred_s; + char *as_keypred_e; + char *as_conditions_s; + char *as_conditions_e; + char *as_signature_string_s; + char *as_signature_string_e; + char *as_comment_s; + char *as_comment_e; + char *as_startofsignature; + char *as_allbutsignature; + int as_id; + int as_signeralgorithm; + int as_result; + int as_error; + u_char as_flags; + u_char as_internalflags; + char as_kresult; + char as_sigresult; + struct keylist *as_keylist; + struct environment *as_env; + struct assertion *as_next; +}; + +/* Internal flags */ +#define ASSERT_IFLAG_WEIRDLICS 0x0001 /* Needs Licensees re-processing */ +#define ASSERT_IFLAG_WEIRDAUTH 0x0002 /* Needs Authorizer re-processing */ +#define ASSERT_IFLAG_WEIRDSIG 0x0004 /* Needs Signature re-processing */ +#define ASSERT_IFLAG_NEEDPROC 0x0008 /* Needs "key field" processing */ +#define ASSERT_IFLAG_PROCESSED 0x0010 /* Handled repositioning already */ + +extern struct assertion *keynote_current_assertion; + +#define KRESULT_UNTOUCHED 0 +#define KRESULT_IN_PROGRESS 1 /* For cycle detection */ +#define KRESULT_DONE 2 + +#define KEYWORD_VERSION 1 +#define KEYWORD_LOCALINIT 2 +#define KEYWORD_AUTHORIZER 3 +#define KEYWORD_LICENSEES 4 +#define KEYWORD_CONDITIONS 5 +#define KEYWORD_SIGNATURE 6 +#define KEYWORD_COMMENT 7 + +#define KEYNOTE_FLAG_EXPORTALL 0x1 + +#define LEXTYPE_CHAR 0x1 + +struct keylist *keynote_keylist_find(struct keylist *, char *); +struct assertion *keynote_parse_assertion(char *, int, int); +int keynote_evaluate_authorizer(struct assertion *, int); +struct assertion *keynote_find_assertion(void *, int, int); +int keynote_evaluate_assertion(struct assertion *); +int keynote_parse_keypred(struct assertion *, int); +int keynote_keylist_add(struct keylist **, char *); +int keynote_add_htable(struct assertion *, int); +void keynote_free_assertion(struct assertion *); +void keynote_keylist_free(struct keylist *); +int keynote_in_authorizers(void *, int); +char *keynote_get_private_key(char *); +int keynote_evaluate_query(void); +int keynote_lex_add(void *, int); +void keynote_lex_remove(void *); +#endif /* __ASSERTION_H__ */ diff --git a/lib/libkeynote/aux.c b/lib/libkeynote/aux.c new file mode 100644 index 00000000000..18108c26ea5 --- /dev/null +++ b/lib/libkeynote/aux.c @@ -0,0 +1,511 @@ +/* $OpenBSD: aux.c,v 1.1 1999/05/23 22:11:04 angelos 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 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 <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <limits.h> +#ifndef PILOT +#include <time.h> +#endif /* PILOT */ +#include "environment.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; +#ifdef CRYPTO + DSA *dsa; + RSA *rsa; +#endif /* CRYPTO */ + + if (key == (void *) NULL) + return 0; + + switch (alg) + { +#ifdef CRYPTO + case KEYNOTE_ALGORITHM_DSA: + dsa = (DSA *) key; + res += BN_mod_word(dsa->p, HASHTABLESIZE); + res += BN_mod_word(dsa->q, HASHTABLESIZE); + res += BN_mod_word(dsa->g, HASHTABLESIZE); + res += BN_mod_word(dsa->pub_key, HASHTABLESIZE); + return res % HASHTABLESIZE; + + case KEYNOTE_ALGORITHM_RSA: + rsa = (RSA *) key; + res += BN_mod_word(rsa->n, HASHTABLESIZE); + res += BN_mod_word(rsa->e, HASHTABLESIZE); + return res % HASHTABLESIZE; + + case KEYNOTE_ALGORITHM_X509: /* RSA-specific */ + rsa = (RSA *) key; + res += BN_mod_word(rsa->n, HASHTABLESIZE); + res += BN_mod_word(rsa->e, HASHTABLESIZE); + return res % HASHTABLESIZE; +#endif /* CRYPTO */ + + 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 == (struct keylist *) 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 != (struct keylist *) NULL; + kl = kl->key_next) + if (kl->key_alg == alg) + if (keynote_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 == (struct keylist **) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + kl = (struct keylist *) calloc(1, sizeof(struct keylist)); + if (kl == (struct keylist *) 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 == (struct keynote_session *) NULL) || + (keynote_current_session->ks_id != sessid)) + { + keynote_current_session = keynote_find_session(sessid); + if (keynote_current_session == (struct keynote_session *) NULL) + { + keynote_errno = ERROR_NOTFOUND; + return -1; + } + } + + ks = keynote_current_session; + + /* If no action authorizers present */ + if ((kl = ks->ks_action_authorizers) == (struct keylist *) 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 = (struct keylist *) NULL; + keynote_keylist_free(kl); + return 0; + } + + for (; kl->key_next != (struct keylist *) NULL; kl = kl->key_next) + if (!strcmp(kl->key_stringkey, key)) + { + kl2 = kl->key_next; + kl->key_next = kl2->key_next; + kl2->key_next = (struct keylist *) 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 == (struct keynote_session *) NULL) || + (keynote_current_session->ks_id != sessid)) + { + keynote_current_session = keynote_find_session(sessid); + if (keynote_current_session == (struct keynote_session *) NULL) + { + keynote_errno = ERROR_NOTFOUND; + return -1; + } + } + + stringkey = strdup((char *) key); + if (stringkey == (char *) 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 != (struct keylist *) 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 != (struct keylist *) NULL) + { + kl2 = kl->key_next; + free(kl->key_stringkey); + keynote_free_key(kl->key_key, kl->key_alg); + free(kl); + kl = kl2; + } +} + +/* + * 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 == (char *) NULL) + return (struct assertion *) NULL; + + h = keynote_keyhash(authorizer, algorithm); + for (as = keynote_current_session->ks_assertion_table[h]; + as != (struct assertion *) NULL; + as = as->as_next) + if ((as->as_authorizer != (void *) NULL) && + (as->as_signeralgorithm == algorithm)) + if (keynote_keycompare(authorizer, as->as_authorizer, algorithm) == + RESULT_TRUE) + if (num-- == 0) + return as; + + return (struct assertion *) 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; + u_int i; + + if (as == (struct assertion *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + if (!which) + hashname = as->as_authorizer_string_s; + else + hashname = as->as_authorizer; + + if (hashname == (char *) 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 == (struct keynote_session *) NULL) || + (keynote_current_session->ks_id != sessid)) + { + keynote_current_session = keynote_find_session(sessid); + if (keynote_current_session == (struct keynote_session *) NULL) + { + keynote_errno = ERROR_NOTFOUND; + return -1; + } + } + + as = keynote_parse_assertion(asrt, len, assertion_flags); + if ((as == (struct assertion *) 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 == (struct keynote_session *) NULL) || + (keynote_current_session->ks_id != sessid)) + { + keynote_current_session = keynote_find_session(sessid); + if (keynote_current_session == (struct keynote_session *) NULL) + { + keynote_errno = ERROR_NOTFOUND; + return -1; + } + } + + for (i = 0; i < HASHTABLESIZE; i++) + { + ht = keynote_current_session->ks_assertion_table[i]; + if (ht == (struct assertion *) 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 != (struct assertion *) 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 == (struct assertion *) NULL) + return; + + if (as->as_buf != (char *) NULL) + free(as->as_buf); + + if (as->as_signature != (char *) NULL) + free(as->as_signature); + + if (as->as_env != (struct environment *) NULL) + keynote_env_cleanup(&(as->as_env), 1); + + if (as->as_keylist != (struct keylist *) NULL) + keynote_keylist_free(as->as_keylist); + + if (as->as_authorizer != (void *) NULL) + keynote_free_key(as->as_authorizer, as->as_signeralgorithm); + + free(as); +} + +/* + * Taken from "Compiler Design in C" by Aho. + */ +u_int +keynote_stringhash(char *name, u_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)) + hash_val = ((hash_val ^ (i >> 12)) & ~0x3fff); + } + + return hash_val % size; +} diff --git a/lib/libkeynote/base64.c b/lib/libkeynote/base64.c new file mode 100644 index 00000000000..51331ff8c21 --- /dev/null +++ b/lib/libkeynote/base64.c @@ -0,0 +1,348 @@ +/* $OpenBSD: base64.c,v 1.1 1999/05/23 22:11:06 angelos Exp $ */ + +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <sys/types.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "keynote.h" + +#define Assert(Cond) if (!(Cond)) return -1; + +static const char Base64[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +kn_encode_base64(src, srclength, target, targsize) +unsigned char const *src; +unsigned int srclength; +char *target; +unsigned int targsize; +{ + unsigned int datalength = 0; + unsigned char input[3]; + unsigned char output[4]; + int i; + + keynote_errno = 0; + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + Assert(output[3] < 64); + + if (datalength + 4 > targsize) + { + keynote_errno = ERROR_SYNTAX; + return (-1); + } + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + + if (datalength + 4 > targsize) + { + keynote_errno = ERROR_SYNTAX; + return (-1); + } + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + { + keynote_errno = ERROR_SYNTAX; + return (-1); + } + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +kn_decode_base64(src, target, targsize) +char const *src; +unsigned char *target; +unsigned int targsize; +{ + int tarindex, state, ch; + char *pos; + + keynote_errno = 0; + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + { + keynote_errno = ERROR_SYNTAX; + return (-1); + } + switch (state) { + case 0: + if (target) { + if (tarindex >= targsize) + { + keynote_errno = ERROR_SYNTAX; + return (-1); + } + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if (tarindex + 1 >= targsize) + { + keynote_errno = ERROR_SYNTAX; + return (-1); + } + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if (tarindex + 1 >= targsize) + { + keynote_errno = ERROR_SYNTAX; + return (-1); + } + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if (tarindex >= targsize) + { + keynote_errno = ERROR_SYNTAX; + return (-1); + } + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + keynote_errno = ERROR_SYNTAX; + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + { + keynote_errno = ERROR_SYNTAX; + return (-1); + } + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + { + keynote_errno = ERROR_SYNTAX; + return (-1); + } + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + { + keynote_errno = ERROR_SYNTAX; + return (-1); + } + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + { + keynote_errno = ERROR_SYNTAX; + return (-1); + } + } + + return (tarindex); +} diff --git a/lib/libkeynote/doc/keynote-spec b/lib/libkeynote/doc/keynote-spec new file mode 100644 index 00000000000..f59e9654363 --- /dev/null +++ b/lib/libkeynote/doc/keynote-spec @@ -0,0 +1,1562 @@ +# $OpenBSD: keynote-spec,v 1.1 1999/05/23 22:11:09 angelos Exp $ + +Network Working Group Matt Blaze +INTERNET DRAFT Joan Feigenbaum +Expires in six months John Ioannidis + AT&T Labs - Research + Angelos D. Keromytis + U. of Pennsylvania + March 1999 + + The KeyNote Trust-Management System + Version 2 + <draft-blaze-ietf-trustmgt-keynote-01.txt> + +Status of this Memo + + This document is an Internet-Draft and is in full conformance with + all provisions of Section 10 of RFC2026. + + Please direct comments to one of the authors (for the authors contact + information, see the end of this document), and/or to the + trustmgt@east.isi.edu mailing list. + + Internet Drafts are working documents of the Internet Engineering + Task Force (IETF), its areas, and its working Groups. Note that + other groups may also distribute working documents as Internet + Drafts. + + Internet-Drafts draft documents are valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress". + + The list of current Internet-Drafts can be accessed at + http://www.ietf.org/ietf/1id-abstracts.txt + + The list of Internet-Draft Shadow Directories can be accessed at + http://www.ietf.org/shadow.html. + + Distribution of this memo is unlimited. + +Abstract + + This memo describes version 2 of the KeyNote trust-management system. + It specifies the syntax and semantics of KeyNote `assertions,' + describes `action attribute' processing, and outlines the application + architecture into which a KeyNote implementation can be fit. The + KeyNote architecture and language are useful as building blocks for + the trust management aspects of a variety of Internet protocols and + services. + + +1. Introduction + + Trust management, introduced in the PolicyMaker system [BFL96], is a + unified approach to specifying and interpreting security policies, + credentials, and relationships; it allows direct authorization of + security-critical actions. A trust-management system provides + standard, general-purpose mechanisms for specifying application + security policies and credentials. Trust-management credentials + describe a specific delegation of trust and subsume the role of + public key certificates; unlike traditional certificates, which bind + keys to names, credentials can bind keys directly to the + authorization to perform specific tasks. + + A trust-management system has five basic components: + + * A language for describing `actions,' which are operations with + security consequences that are to be controlled by the system. + + * A mechanism for identifying `principals,' which are entities that + can be authorized to perform actions. + + * A language for specifying application `policies,' which govern the + actions that principals are authorized to perform. + + * A language for specifying `credentials,' which allow principals + to delegate authorization to other principals. + + * A `compliance checker,' which provides a service to applications + for determining how an action requested by principals should be + handled, given a policy and a set of credentials. + + The trust-management approach has a number of advantages over other + mechanisms for specifying and controlling authorization, especially + when security policy is distributed over a network or is otherwise + decentralized. + + Trust management unifies the notions of security policy, credentials, + access control, and authorization. An application that uses a trust- + management system can simply ask the compliance checker whether a + requested action should be allowed. Furthermore, policies and + credentials are written in standard languages that are shared by all + trust-managed applications; the security configuration mechanism for + one application carries exactly the same syntactic and semantic + structure as that of another, even when the semantics of the + applications themselves are quite different. + + Trust-management policies are easy to distribute across networks, + helping to avoid the need for application-specific distributed policy + configuration mechanisms, access control lists, and certificate + parsers and interpreters. + + For a general discussion of the use of trust management in + distributed system security, see [Bla99]. + + KeyNote is a simple and flexible trust-management system designed to + work well for a variety of large- and small- scale Internet-based + applications. It provides a single, unified language for both local + policies and credentials. KeyNote policies and credentials, called + `assertions,' contain predicates that describe the trusted actions + permitted by the holders of specific public keys. KeyNote assertions + are essentially small, highly-structured programs. A signed + assertion, which can be sent over an untrusted network, is also + called a `credential assertion.' Credential assertions, which also + serve the role of certificates, have the same syntax as policy + assertions but are also signed by the principal delegating the trust. + + In KeyNote: + + * Actions are specified as a collection of name-value pairs. + + * Principal names can be any convenient string and can directly + represent cryptographic public keys. + + * The same language is used for both policies and credentials. + + * The policy and credential language is concise, highly expressive, + human readable and writable, and compatible with a variety of + storage and transmission media, including electronic mail. + + * The compliance checker returns an application-configured `policy + compliance value' that describes how a request should be handled + by the application. Policy compliance values are always + positively derived from policy and credentials, facilitating + analysis of KeyNote-based systems. + + * Compliance checking is efficient enough for high-performance and + real-time applications. + + This document describes the KeyNote policy and credential assertion + language, the structure of KeyNote action descriptions, and the + KeyNote model of computation. + + We assume that applications communicate with a locally trusted + KeyNote compliance checker via a `function call' style interface, + sending a collection of KeyNote policy and credential assertions plus + an action description as input and accepting the resulting policy + compliance value as output. However, the requirements of different + applications, hosts, and environments may give rise to a variety of + different interfaces to KeyNote compliance checkers; this document + does not aim to specify a complete compliance checker API. + + +2. KeyNote Concepts + + In KeyNote, the authority to perform trusted actions is associated + with one or more `principals.' A principal may be a physical entity, + a process in an operating system, a public key, or any other + convenient abstraction. KeyNote principals are identified by a + string called a `Principal Identifier.' In some cases, a Principal + Identifier will contain a cryptographic key interpreted by the + KeyNote system (e.g., for credential signature verification). In + other cases, Principal Identifiers may have a structure that is + opaque to KeyNote. + + Principals perform two functions of concern to KeyNote: They request + `actions' and they issue `assertions.' Actions are any trusted + operations that an application places under KeyNote control. + Assertions delegate the authorization to perform actions to other + principals. + + Actions are described to the KeyNote compliance checker in terms of a + collection of name-value pairs called an `action attribute set.' The + action attribute set is created by the invoking application. Its + structure and format are described in detail in Section 3 of this + document. + + KeyNote provides advice to applications on the interpretation of + policy with regard to specific requested actions. Applications + invoke the KeyNote compliance checker by issuing a `query' containing + a proposed action attribute set and identifying the principal(s) + requesting it. The KeyNote system determines and returns an + appropriate `policy compliance value' from an ordered set of possible + responses. + + The policy compliance value returned from a KeyNote query advises the + application how to process the requested action. In the simplest + case, the compliance value is Boolean (e.g., "reject" or "approve"). + Assertions can also be written to select from a range of possible + compliance values, when appropriate for the application (e.g., "no + access", "restricted access", "full access"). Applications can + configure the relative ordering (from `weakest' to `strongest') of + compliance values at query time. + + Assertions are the basic programming unit for specifying policy and + delegating authority. Assertions describe the conditions under which + a principal authorizes actions requested by other principals. An + assertion identifies the principal that made it, which other + principals are being authorized, and the conditions under which the + authorization applies. The syntax of assertions is given in Section + 4. + + A special principal, whose identifier is "POLICY", provides the root + of trust in KeyNote. "POLICY" is therefore considered to be + authorized to perform any action. + + Assertions issued by the "POLICY" principal are called `policy + assertions' and are used to delegate authority to otherwise untrusted + principals. The KeyNote security policy of an application consists + of a collection of policy assertions. + + When a principal is identified by a public key, it can digitally sign + assertions and distribute them over untrusted networks for use by + other KeyNote compliance checkers. These signed assertions are also + called `credentials,' and serve a role similar to that of traditional + public key certificates. Policies and credentials share the same + syntax and are evaluated according to the same semantics. A + principal can therefore convert its policy assertions into + credentials simply by digitally signing them. + + KeyNote is designed to encourage the creation of human-readable + policies and credentials that are amenable to transmission and + storage over a variety of media. Its assertion syntax is inspired by + the format of RFC822-style message headers [Cro82]. A KeyNote + assertion contains a sequence of sections, called `fields,' each of + which specifying one aspect of the assertion's semantics. Fields + start with an identifier at the beginning of a line and continue + until the next field is encountered. For example: + + KeyNote-Version: 2 + Comment: A simple, if contrived, email certificate for user mab + Local-Constants: ATT_CA_key = "RSA:acdfa1df1011bbac" + mab_key = "DSA:deadbeefcafe001a" + Authorizer: ATT_CA_key + Licensees: mab_key + Conditions: ((app_domain == "email") # valid for email only + && (address == "mab@research.att.com")); + Signature: "RSA-SHA1:f00f2244" + + The meanings of the various sections are described in Sections 4 and + 5 of this document. + + KeyNote semantics resolve the relationship between an application's + policy and actions requested by other principals, as supported by + credentials. The KeyNote compliance checker processes the assertions + against the action attribute set to determine the policy compliance + value of a requested action. These semantics are defined in Section + 5. + + An important principle in KeyNote's design is `assertion + monotonicity'; the policy compliance value of an action is always + positively derived from assertions made by trusted principals. + Removing an assertion never results in increasing the compliance + value returned by KeyNote for a given query. The monotonicity + property can simplify the design and analysis of complex network- + based security protocols; network failures that prevent the + transmission of credentials can never result in spurious + authorization of dangerous actions. + + +3. Action Attributes + + Trusted actions to be evaluated by KeyNote are described by a + collection of name-value pairs called the `action attribute set.' + Action attributes are the mechanism by which applications communicate + requests to KeyNote and are the primary objects on which KeyNote + assertions operate. An action attribute set is passed to the KeyNote + compliance checker with each query. + + Each action attribute consists of a name and a value. The semantics + of the names and values are not interpreted by KeyNote itself; they + vary from application to application and must be agreed upon by the + writers of applications and the writers of the policies and + credentials that will be used by them. + + Action attribute names and values are represented by arbitrary-length + strings. KeyNote guarantees support of attribute names and values up + to 2048 characters long. The handling of longer attribute names or + values is not specified and is KeyNote- implementation-dependent. + Applications and assertions should therefore avoid depending on the + the use of attributes with names or values longer than 2048 + characters. The length of an attribute value is represented by an + implementation-specific mechanism (e.g., NUL-terminated strings, an + explicit length field, etc.). + + Attribute values are inherently untyped and are represented as + character strings by default. Attribute values may contain any non- + NUL ASCII character. Numeric attribute values should first be + converted to an ASCII text representation by the invoking + application, e.g., the value 1234.5 would be represented by the + string "1234.5". + + Attribute names are of the form: + + <AttributeID> ::= [a-zA-Z_][a-zA-Z0-9_]* + + That is, an <AttributeID> begins with an alphabetic or underscore + character and can be followed by any number of alphanumerics and + underscores. Attribute names are case-sensitive. + + The exact mechanism for passing the action attribute set to the + compliance checker is determined by the KeyNote implementation. + Depending on specific requirements, an implementation may provide a + mechanism for including the entire attribute set as an explicit + parameter of the query, or it may provide some form of callback + mechanism invoked as each attribute is dereferenced, e.g., for access + to kernel variables. + + If an action attribute is not defined its value is considered to be + the empty string. + + Attribute names beginning with the "_" character are reserved for use + by the KeyNote runtime environment and cannot be passed from + applications as part of queries. The following special attribute + names are used: + + Name Purpose + ------------------------ ------------------------------------ + _MIN_TRUST Lowest-order (minimum) compliance + value in query; see Section 5.1. + + _MAX_TRUST Highest-order (maximum) compliance + value in query; see Section 5.1. + + _VALUES Linearly ordered set of compliance + values in query; see Section 5.1. + Comma separated. + + _ACTION_AUTHORIZERS Names of principals directly + authorizing action in query. + Comma separated. + + In addition, attributes with names of the form "_<N>", where <N> is + an ASCII-encoded integer, are used by the regular expression matching + mechanism described in Section 5. + + The assignment and semantics of any other attribute names beginning + with "_" is unspecified and implementation-dependent. + + The names of other attributes in the action attribute set are not + specified by KeyNote but must be agreed upon by the writers of any + policies and credentials that are to inter-operate in a specific + KeyNote query evaluation. + + By convention, the name of the application domain over which action + attributes should be interpreted is given in the attribute named + "app_domain". The IANA (or some other suitable authority) will + provide a registry of reserved app_domain names. The registry will + list the names and meanings of each application's attributes. + + The app_domain convention helps to ensure that credentials are + interpreted as they were intended. An attribute with any given name + may be used in many different application domains but might have + different meanings in each of them. However, the use of a global + registry is not always required for small-scale, closed applications; + the only requirement is that the policies and credentials made + available to the KeyNote compliance checker interpret attributes + according to the same semantics assumed by the application that + created them. + + For example, an email application might reserve the app_domain + "RFC822-EMAIL" and might use the attributes named "address" (the + email address of a message's sender), "name" (the human name of the + message sender), and "organization" (the organization name). The + values of these attributes would be derived in the obvious way from + the email message headers. The public key of the message's signer + would be given in the "_ACTION_AUTHORIZERS" attribute. + + Note that "RFC822-EMAIL" is a hypothetical example; such a name may + or may not appear in the actual registry with these or different + attributes. (Indeed, we recognize that the reality of email security + is considerably more complex than this example might suggest.) + + +4. KeyNote Assertion Syntax + + In the following sections, the notation [X]* means zero or more + repetitions of character string X. The notation [X]+ means one or + more repetitions of X. Nonterminal grammar symbols are enclosed in + angled brackets. Quoted strings in grammar productions represent + terminals. + +4.1 Basic Structure + + All KeyNote assertions are encoded in ASCII. + + KeyNote assertions are divided into sections, called `fields,' that + serve various semantic functions. Each field starts with an + identifying label at the beginning of a line, followed by the ":" + character and the field's contents. There can be at most one field + per line. + + A field may be continued over more than one line by indenting + subsequent lines with at least one ASCII SPACE or TAB character. + Whitespace (a SPACE, TAB, or NEWLINE character) separates tokens but + is otherwise ignored outside of quoted strings. Comments with a + leading octothorp character (see Section 4.2) may begin in any + column. + + One mandatory field is required in all assertions: + + Authorizer + + Six optional fields may also appear: + + Comment + Conditions + KeyNote-Version + Licensees + Local-Constants + Signature + + All field names are case-insensitive. The "KeyNote-Version" field, + if present, appears first. The "Signature" field, if present, + appears last. Otherwise, fields may appear in any order. Each field + may appear at most once in any assertion. + + Blank lines are not permitted in assertions. Multiple assertions + stored in a file (e.g., in application policy configurations), + therefore, can be separated from one another unambiguously by the use + of blank lines between them. + +4.2 Comments + + The octothorp character ("#", ASCII 35 decimal) can be used to + introduce comments. Outside of quoted strings (see Section 4.3), all + characters from the "#" character through the end of the current line + are ignored. However, commented text is included in the computation + of assertion signatures (see Section 4.6.7). + +4.3 Strings + + A `string' is a lexical object containing a sequence of characters. + Strings may contain any non-NUL characters, including newlines and + nonprinting characters. Strings may be given as literals, computed + from complex expressions, or dereferenced from attribute names. + +4.3.1 String Literals + + A string literal directly represents the value of a string. String + literals must be quoted by preceding and following them with the + double-quote character (ASCII 34 decimal). + + A printable character may be `escaped' inside a quoted string literal + by preceding it with the backslash character (ASCII 92 decimal) + (e.g., "like \"this\"."). This permits the inclusion of the double- + quote and backslash characters inside string literals. + + A similar escape mechanism is also used to represent non-printable + characters. "\n" represents the newline character (ASCII character + 10 decimal), "\r" represents the carriage-return character (ASCII + character 13 decimal), "\t" represents the tab character (ASCII + character 9 decimal), and "\f" represents the form-feed character + (ASCII character 12 decimal). A backslash character followed by a + newline suppresses all subsequent whitespace (including the newline) + up to the next non-whitespace character (this allows the continuation + of long string constants across lines). Un-escaped newline and + return characters are illegal inside string literals. + + The constructs "\0o", "\0oo", and "\ooo" (where o represents any + octal digit) may be used to represent any non-NUL ASCII characters + with their corresponding octal values (thus, "\012" is the same as + "\n", "\101" is "A", and "\377" is the ASCII character 255 decimal). + However, the NUL character cannot be encoded in this manner; "\0", + "\00", and "\000" are converted to the strings "0", "00", and "000" + respectively. Similarly, all other escaped characters have the + leading backslash removed (e.g., "\a" becomes "a", and "\\" becomes + "\"). The following four strings are equivalent: + + "this string contains a newline\n followed by one space." + + "this string contains a newline\n \ + followed by one space." + + "this str\ + ing contains a \ + newline\n followed by one space." + + "this string contains a newline\012\040followed by one space." + +4.3.2 String Expressions + + In general, anywhere a quoted string literal is allowed, a `string + expression' can be used. A string expression constructs a string + from string constants, dereferenced attributes (described in Section + 4.4), and a string concatenation operator. String expressions may be + parenthesized. + + <StrEx> ::= <StrEx> "." <StrEx> /* String concatenation */ + | <StringLiteral> /* Quoted string */ + | "(" <StrEx> ")" + | <DerefAttribute> /* See Section 4.4 */ + | "$" <StrEx> /* See Section 4.4 */ + + The "$" operator has higher precedence than the "." operator. + +4.4 Dereferenced Attributes + + Action attributes provide the primary mechanism for applications to + pass information to assertions. Attribute names are strings from a + limited character set (<AttributeID> as defined in Section 3), and + attribute values are represented internally as strings. An attribute + is dereferenced simply by using its name. In general, KeyNote allows + the use of an attribute anywhere a string literal is permitted. + + Attributes are dereferenced as strings by default. When required, + dereferenced attributes can be converted to integers or floating + point numbers with the type conversion operators "@" and "&". Thus, + an attribute named "foo" having the value "1.2" may be interpreted as + the string "1.2" (foo), the integer value 1 (@foo), or the floating + point value 1.2 (&foo). + + Attributes converted to integer and floating point numbers are + represented according to the ANSI C `long' and `float' types, + respectively. + + Any uninitialized attribute has the empty-string value when + dereferenced as a string and the value zero when dereferenced as an + integer or float. + + Attribute names may be given literally or calculated from string + expressions and may be recursively dereferenced. In the simplest + case, an attribute is dereferenced simply by using its name outside + of quotes; e.g., the string value of the attribute named "foo" is by + reference to `foo' (outside of quotes). The "$<StrEx>" construct + dereferences the attribute named in the string expression <StrEx>. + For example, if the attribute named "foo" contains the string "bar", + the attribute named "bar" contains the string "xyz", and the + attribute "xyz" contains the string "qua", the following string + comparisons are all true: + + foo == "bar" + $("foo") == "bar" + $foo == "xyz" + $(foo) == "xyz" + $$foo == "qua" + + If <StrEx> evaluates to an invalid or uninitialized attribute name, + its value is considered to be the empty string (or zero if used as a + numeric). + + The <DerefAttribute> token is defined as: + + <DerefAttribute> ::= <AttributeID> + +4.5 Principal Identifiers + + Principals are represented as ASCII strings called `Principal + Identifiers.' Principal Identifiers may be arbitrary labels whose + structure is not interpreted by the KeyNote system or they may encode + cryptographic keys that are used by KeyNote for credential signature + verification. + + <PrincipalIdentifier> ::= <OpaqueID> + | <KeyID> + +4.5.1 Opaque Principal Identifiers + + Principal Identifiers that are used by KeyNote only as labels are + said to be `opaque.' Opaque identifiers are encoded in assertions as + strings (see Section 4.3): + + <OpaqueID> ::= <StrEx> + + Opaque identifier strings should not contain the ":" character. + +4.5.2 Cryptographic Principal Identifiers + + Principal Identifiers that are used by KeyNote as keys, e.g., to + verify credential signatures, are said to be `cryptographic.' + Cryptographic identifiers are also lexically encoded as strings: + + <KeyID> ::= <StrEx> + + Unlike Opaque Identifiers, however, Cryptographic Identifier strings + have a special form. To be interpreted by KeyNote (for signature + verification), an identifier string should be of the form: + + ALGORITHM:ENCODEDBITS + + "ALGORITHM" is an ASCII substring that describes the algorithms to be + used in interpreting the key's bits. The ALGORITHM identifies the + major cryptographic algorithm (e.g., RSA [RSA78], DSA [DSA94], etc.), + structured format (e.g., PKCS1 [PKCS1]), and key bit encoding (e.g., + HEX or BASE64). By convention, the ALGORITHM substring starts with + an alphabetic character and can contain letters, digits, underscores, + or dashes (i.e., it should match the regular expression "[a-zA-Z][a- + zA-Z0-9_-]*"). The IANA (or some other appropriate authority) will + provide a registry of reserved algorithm identifiers. + + "ENCODEDBITS" is a substring of characters representing the key's + bits, the encoding and format of which depends on the ALGORITHM. By + convention, hexadecimal encoded keys use lower-case ASCII characters. + + Cryptographic Principal Identifiers are converted to a normalized + canonical form for the purposes of any internal comparisons between + them; see Section 5.2. + + Note that the keys used in examples throughout this document are + fictitious and generally much shorter than would be required for + security in practice. + +4.6 KeyNote Fields + +4.6.1 The KeyNote-Version Field + + The KeyNote-Version field identifies the version of the KeyNote + assertion language under which the assertion was written. The + KeyNote-Version field is of the form + + <VersionField> ::= "KeyNote-Version:" <VersionString> + <VersionString> ::= <StringLiteral> + | <IntegerLiteral> + + where <VersionString> is an ASCII-encoded string. Assertions in + production versions of KeyNote use decimal digits in the version + representing the version number of the KeyNote language under which + they are to be interpreted. Assertions written to conform with this + document should be identified with the version string "2" (or the + integer 2). The KeyNote-Version field, if included, should appear + first. + +4.6.2 The Local-Constants Field + + This field adds or overrides action attributes in the current + assertion only. This mechanism allows the use of short names for + (frequently lengthy) cryptographic principal identifiers, especially + to make the Licensees field more readable. The Local-Constants field + is of the form: + + <LocalConstantsField> ::= "Local-Constants:" <Assignments> + <Assignments> ::= /* can be empty */ + | <AttributeID> "=" <StringLiteral> + | <Assignments> <Assignments> + + <AttributeID> is an attribute name from the action attribute + namespace as defined in Section 3. The name is available for use as + an attribute in any subsequent field. If the Local-Constants field + defines more than one identifier, it can occupy more than one line + and be indented. <StringLiteral> is a string literal as described in + Section 4.3. Attributes defined in the Local-Constants field + override any attributes with the same name passed in with the action + attribute set. + + An attribute may be initialized at most once in the Local-Constants + field. If an attribute is initialized more than once in an + assertion, the entire assertion is considered invalid and is not + considered by the KeyNote compliance checker in evaluating queries. + +4.6.3 The Authorizer Field + + The Authorizer identifies the Principal issuing the assertion. This + field is of the form + + <AuthField> ::= "Authorizer:" <AuthID> + <AuthID> ::= <PrincipalIdentifier> + | <DerefAttribute> + + The Principal Identifier may be given directly or by reference to the + attribute namespace (as defined in Section 4.4). + +4.6.4 The Licensees Field + + The Licensees field identifies the principals authorized by the + assertion. More than one principal can be authorized, and + authorization can be distributed across several principals through + the use of `and' and threshold constructs. This field is of the form + + <LicenseesField> ::= "Licensees:" <LicenseesExpr> + + <LicenseesExpr> ::= /* can be empty */ + | <PrincExpr> + + <PrincExpr> ::= "(" <PrincExpr> ")" + | <PrincExpr> "&&" <PrincExpr> + | <PrincExpr> "||" <PrincExpr> + | <K>"-of(" <PrincList> ")" /* Threshold */ + | <PrincipalIdentifier> + | <DerefAttribute> + + <PrincList> ::= <PrincipalIdentifier> + | <DerefAttribute> + | <PrincList> "," <PrincList> + + <K> ::= [1-9][0-9]* + + The "&&" operator has higher precedence than the "||" operator. <K> + is an ASCII-encoded positive decimal integer. If a <PrincList> + contains fewer than <K> principals, the entire assertion is omitted + from processing. + +4.6.5 The Conditions Field + + This field gives the `conditions' under which the Authorizer trusts + the Licensees to perform an action. `Conditions' are predicates that + operate on the action attribute set. The Conditions field is of the + form: + + <ConditionsField> ::= "Conditions:" <ConditionsProgram> + + <ConditionsProgram> ::= /* Can be empty */ + | <Clause> ";" <ConditionsProgram> + + <Clause> ::= <Test> "->" "{" <ConditionsProgram> "}" + | <Test> "->" <Value> + | <Test> + + <Value> ::= <StrEx> + + <Test> ::= <RelExpr> + + <RelExpr> ::= "(" <RelExpr> ")" /* Parentheses */ + | <RelExpr> "&&" <RelExpr> /* Logical AND */ + | <RelExpr> "||" <RelExpr> /* Logical OR */ + | "!" <RelExpr> /* Logical NOT */ + | <IntRelExpr> + | <FloatRelExpr> + | <StringRelExpr> + | "true" /* case insensitive */ + | "false" /* case insensitive */ + + <IntRelExpr> ::= <IntEx> "==" <IntEx> + | <IntEx> "!=" <IntEx> + | <IntEx> "<" <IntEx> + | <IntEx> ">" <IntEx> + | <IntEx> "<=" <IntEx> + | <IntEx> ">=" <IntEx> + + <FloatRelExpr> ::= <FloatEx> "<" <FloatEx> + | <FloatEx> ">" <FloatEx> + | <FloatEx> "<=" <FloatEx> + | <FloatEx> ">=" <FloatEx> + + <StringRelExpr> ::= <StrEx> "==" <StrEx> /* String equality */ + | <StrEx> "!=" <StrEx> /* String inequality */ + | <StrEx> "<" <StrEx> /* Alphanum. comparisons */ + | <StrEx> ">" <StrEx> + | <StrEx> "<=" <StrEx> + | <StrEx> ">=" <StrEx> + | <StrEx> "~=" <RegExpr> /* Regular expr. matching */ + + <IntEx> ::= <IntEx> "+" <IntEx> /* Integer */ + | <IntEx> "-" <IntEx> + | <IntEx> "*" <IntEx> + | <IntEx> "/" <IntEx> + | <IntEx> "%" <IntEx> + | <IntEx> "^" <IntEx> /* Exponentiation */ + | "-" <IntEx> + | "(" <IntEx> ")" + | <IntegerLiteral> + | "@" <StrEx> + + <FloatEx> ::= <FloatEx> "+" <FloatEx> /* Floating point */ + | <FloatEx> "-" <FloatEx> + | <FloatEx> "*" <FloatEx> + | <FloatEx> "/" <FloatEx> + | <FloatEx> "^" <FloatEx> /* Exponentiation */ + | "-" <FloatEx> + | "(" <FloatEx> ")" + | <FloatLiteral> + | "&" <StrEx> + + <IntegerLiteral> ::= [0-9]+ + <FloatLiteral> ::= [0-9]+\.[0-9]+ + + <StringLiteral> is a quoted string as defined in Section 4.3 + <AttributeID> is defined in Section 3. + + The operation precedence classes are (from highest to lowest): + + { (, ) } + {unary -, @, &, $} + {^} + {*, /, %} + {+, -, .} + + Operators in the same precedence class are evaluated left-to-right. + + The keywords "true" and "false" are not reserved; they can be used as + attribute or principal identifier names (although this practice makes + assertions difficult to understand and is discouraged). + + <RegExpr> is a standard regular expression, conforming to the POSIX + 1003.2 regular expression syntax and semantics. + + Any string expression (or attribute) containing the ASCII + representation of a numeric value can be converted to an integer or + float with the use of the "@" and "&" operators, respectively. Any + fractional component of an attribute value dereferenced as an integer + is rounded down. If an attribute dereferenced as a number cannot be + properly converted (e.g., it contains invalid characters or is empty) + its value is considered to be zero. + +4.6.6 The Comment Field + + The Comment field allows assertions to be annotated with information + describing their purpose. It is of the form + + <CommentField> ::= "Comment:" <text> + + No interpretation of the contents of this field is performed by + KeyNote. Note that this is one of two mechanisms for including + comments in KeyNote assertions; comments can also be inserted + anywhere in an assertion's body by preceeding them with the "#" + character. + +4.6.7 The Signature Field + + The Signature field identifies a signed assertions and gives the + encoded digital signature of the principal identified in the + Authorizer field. The Signature field is of the form: + + <SignatureField> ::= "Signature:" <Signature> + + <Signature> ::= <StrEx> + + The <Signature> string should be of the form: + + ALGORITHM:ENCODEDBITS + + The formats of the "ALGORITHM" and "ENCODEDBITS" substrings are as + described for Cryptographic Principal Identifiers in Section 4.4.2 + The algorithm name should be the same as that of the principal + appearing in the Authorizer field. The IANA (or some other suitable + authority) will provide a registry of reserved names. It is not + necessary that the encodings of the signature and the authorizer key + be the same. + + If the signature field is included, the principal named in the + Authorizer field must be a Cryptographic Principal Indentifier, the + algorithm must be known to the KeyNote implementation, and the + signature must be correct for the assertion body and authorizer key. + + The signature is computed over the assertion text, beginning with the + first field (including the field identifier string), up to (but not + including) the Signature field identifier. The newline preceeding + the signature field identifier is the last character included in + signature calculation. The signature is always the last field in a + KeyNote assertion. Text following this field is not considered part + of the assertion. + + The algorithms for computing and verifying signatures must be + configured into each KeyNote implementation and are defined and + documented separately. + + Note that all signatures used in examples in this document are + fictitious and generally much shorter than would be required for + security in practice. + + +5. Query Evaluation Semantics + + The KeyNote compliance checker finds and returns the Policy + Compliance Value of queries, as defined in Section 5.3, below. + +5.1 Query Parameters + + A KeyNote query has four parameters: + + * The identifier of the principal(s) requesting the action. + + * The action attribute set describing the action. + + * The set of compliance values of interest to the application, + ordered from _MIN_TRUST to _MAX_TRUST + + * The policy and credential assertions that should be included + in the evaluation. + + The mechanism for passing these parameters to the KeyNote evaluator + is application dependent. In particular, an evaluator might provide + for some parameters to be passed explicitly, while others are looked + up externally (e.g., credentials might be looked up in a network- + based distribution system), while still others might be requested + from the application as needed by the evaluator, through a `callback' + mechanism (e.g., for attribute values that represent values from + among a very large namespace). + +5.1.1 Action Requester + + At least one Principal must be identified in each query as the + `requester' of the action. Actions may be requested by several + principals, each considered to have individually requested it. This + allows policies that require multiple authorizations, e.g., `two + person control.' The set of authorizing principals is made available + in the special attribute "_ACTION_AUTHORIZERS"; if several principals + are authorizers, their identifiers are separated with commas. + +5.1.2 Ordered Compliance Value Set + + The set of compliance values of interest to an application (and their + relative ranking to one another) is determined by the invoking + application and passed to the KeyNote evaluator as a parameter of the + query. In many applications, this will be Boolean, e.g., the ordered + sets {FALSE, TRUE} or {REJECT, APPROVE}. Other applications may + require a range of possible values, e.g., {No_Access, Limited_Access, + Full_Access}. Note that applications should include in this set only + compliance values names that are actually returned by the assertions. + + The lowest-order and highest-order compliance value strings given in + the query are available in the special attributes named "_MIN_TRUST" + and "_MAX_TRUST", respectively. The complete set of query compliance + values is made available in ascending order (from _MIN_TRUST to + _MAX_TRUST) in the special attribute named "_VALUES". Values are + separated with commas; applications that use assertions that make use + of the _VALUES attribute should therefore avoid the use of compliance + value strings that themselves contain commas. + +5.2 Principal Identifier Normalization + + Principal identifier comparisons among Cryptographic Principal + Identifiers (that represent keys) in the Authorizer and Licensees + fields or in an action's direct authorizers are performed after + normalizing them by conversion to a canonical form. + + Every cryptographic algorithm used in KeyNote defines a method for + converting keys to their canonical form and that specifies how the + comparison for equality of two keys is performed. If the algorithm + named in the identifier is unknown to KeyNote, the identifier is + treated as opaque. + + Opaque identifiers are compared as case-sensitive strings. + + Notice that use of opaque identifiers in the Authorizer field + requires that the assertion's integrity be locally trusted (since it + cannot be cryptographically verified by the compliance checker). + +5.3 Policy Compliance Value Calculation + + The Policy Compliance Value of a query is the Principal Compliance + Value of the principal named "POLICY". This value is defined as + follows: + +5.3.1 Principal Compliance Value + + The Compliance Value of a principal <X> is the highest order + (maximum) of: + + - the Direct Authorization Value of principal <X>; and + + - the Assertion Compliance Values of all assertions identifying + <X> in the Authorizer field. + +5.3.2 Direct Authorization Value + + The Direct Authorization Value of a principal <X> is _MAX_TRUST if + <X> is listed in the query as an authorizer of the action. + Otherwise, the Direct Authorization Value of <X> is _MIN_TRUST. + +5.3.3 Assertion Compliance Value + + The Assertion Compliance Value of an assertion is the lowest order + (minimum) of the assertion's Conditions Compliance Value and its + Licensee Compliance Value. + +5.3.4 Conditions Compliance Value + + The Conditions Compliance Value of an assertion is the highest- order + (maximum) value among all successful clauses listed in the conditions + section. + + If no clause's test succeeds or the Conditions field is empty, an + assertion's conditions compliance value is considered to be the + _MIN_TRUST value, as defined Section 5.1. + + If an assertion's Conditions field is missing entirely, its + conditions compliance value is considered to be the _MAX_TRUST value, + as defined in Section 5.1. + + The set of successful test clause values is calculated as follows: + + Recall from the grammar of section 4.6.5 that each clause in the + conditions section has two logical parts: a `test' and an optional + `value,' which, if present, is separated from the test with the "->" + token. The test subclause is a predicate that either succeeds + (evaluates to logical `true') or fails (evaluates to logical + `false'). The value subclause is a string expression that evaluates + to one value from the ordered set of compliance values given with the + query. If the value subclause is missing, it is considered to be + _MAX_TRUST. That is, the clause + + foo=="bar"; + + is equivalent to + + foo=="bar" -> _MAX_TRUST; + + If the value component of a clause is present, in the simplest case + it contains a string expression representing a possible compliance + value. For example, consider an assertion with the following + Conditions field: + + Conditions: + @user_id == 0 -> "full_access"; # clause (1) + @user_id < 1000 -> "user_access"; # clause (2) + @user_id < 10000 -> "guest_access"; # clause (3) + user_name == "root" -> "full_access"; # clause (4) + + Here, if the value of the "user_id" attribute is "1073" and the + "user_name" attribute is "root", the possible compliance value set + would contain the values "guest_access" (by clause (3)) and + "full_access" (by clause (4)). If the ordered set of compliance + values given in the query (in ascending order) is {"no_access", + "guest_access", "user_access", "full_access"}, the conditions + compliance value of the assertion would be "full_access" (because + "full_access" has a higher-order value than "guest_access"). If the + "user_id" attribute had the value "19283" and the "user_name" + attribute had the value "nobody", no clause would succeed and the + conditions compliance value would be "no_access", which is the + lowest-order possible value (_MIN_TRUST). + + If a clause lists an explicit value, its value string must be named + in the query ordered compliance value set. Values not named in the + query compliance value set are considered equivalent to _MIN_TRUST. + + The value component of a clause can also contain recursively-nested + clauses. Recursively-nested clauses are evaluated only if their + parent test is true. That is, + + a=="b" -> { b=="c" -> "value1"; + d=="e" -> "value2"; + true -> "value3"; } ; + + is equivalent to + + (a=="b") && (b=="c") -> "value1"; + (a=="b") && (d=="e") -> "value2"; + (a=="b") -> "value3"; + + String comparisons are case-sensitive. + + A regular expression comparison ("~=") is considered true if the + left-hand-side string expression matches the right-hand-side regular + expression. If the POSIX regular expression group matching scheme is + used, the number of groups matched is placed in the temporary meta- + attribute "_0" (dereferenced as _0), and each match is placed in + sequence in the temporary attributes (_1, _2, ..., _N). These match + attributes values are valid only within subsequent references made + within the same clause. Regular expression evaluation is case- + sensitive. + + A runtime error occurring in the evaluation of a test, such as + division by zero or an invalid regular expression, causes the test to + be considered false. For example: + + foo == "bar" -> { + @a == 1/0 -> "oneval"; # subclause 1 + @a == 2 -> "anotherval"; # subclause 2 + }; + + Here, subclause 1 triggers a runtime error. Subclause 1 is therefore + false (and has the value _MIN_TRUST). Subclause 2, however, would be + evaluated normally. + + An invalid <RegExpr> is considered a runtime error and causes the + test in which it occurs to be considered false. + +5.3.5 Licensee Compliance Value + + The Licensee Compliance Value of an assertion is calculated by + evaluating the expression in the Licensees field, based on the + Principal Compliance Value of the principals named there. + + If an assertion's Licensees field is empty, its Licensee Compliance + Value is considered to be _MIN_TRUST. If an assertion's Licensees + field is missing altogether, its Licensee Compliance Value is + considered to be _MAX_TRUST. + + For each principal named in the Licensees field, its Principal + Compliance Value is substituted for its name. If no Principal + Compliance Value can be found for some named principal, its name is + substituted with the _MIN_TRUST value. + + The licensees expression (as defined in Section 4.6.4) is evaluated + as follows: + + * A "(...)" expression has the value of the enclosed subexpression. + + * A "&&" expression has the lower-order (minimum) of its two + subexpression values. + + * A "||" expression has the higher-order (maximum) of its two + subexpression values. + + * A "<K>-of(<List>)" expression has the K-th highest order + compliance value listed in <list>. Values that appear multiple + times are counted with multiplicity. For example, if K = 3 and + the orders of the listed compliance values are (0, 1, 2, 2, 3), + the value of the expression is the compliance value of order 2. + + For example, consider the following Licensees field: + + Licensees: ("alice" && "bob") || "eve" + + If the Principal Compliance Value is "yes" for principal "alice", + "no" for principal "bob", and "no" for principal "eve", and "yes" is + higher order than "no" in the query's Compliance Value Set, then the + resulting Licensee Compliance Value is "no". + + Observe that if there are exactly two possible compliance values + (e.g., "false" and "true"), the rules of Licensee Compliance Value + resolution reduce exactly to standard Boolean logic. + +5.4 Assertion Management + + Assertions may be either signed or unsigned. Only signed assertions + should be used as credentials or transmitted or stored on untrusted + media. Unsigned assertions should be used only to specify policy and + for assertions whose integrity has already been verified as + conforming to local policy by some mechanism external to the KeyNote + system itself (e.g., X.509 certificates converted to KeyNote + assertions by a trusted conversion program). + + Implementations that permit signed credentials to be verified by the + KeyNote compliance checker generally provide two `channels' through + which applications can make assertions available. Unsigned, locally- + trusted assertions are provided over a `trusted' interface, while + signed credentials are provided over an `untrusted' interface. The + KeyNote compliance checker verifies correct signatures for all + assertions submitted over the untrusted interface. The integrity of + KeyNote evaluation requires that only assertions trusted as + reflecting local policy are submitted to KeyNote via the trusted + interface. + + Note that applications that use KeyNote exclusively as a local policy + specification mechanism need use only trusted assertions. Other + applications might need only a small number of infrequently changed + trusted assertions to `bootstrap' a policy whose details are + specified in signed credentials issued by others and submitted over + the untrusted interface. + +5.5 Implementation Issues + + Informally, the semantics of KeyNote evaluation can be thought of as + involving the construction a directed graph of KeyNote assertions + rooted at a POLICY assertion that connects with at least one of the + principals that requested the action. + + Delegation of some authorization from principal <A> to a set of + principals <B> is expressed as an assertion with principal <A> given + in the Authorizer field, principal set <B> given in the Licensees + field, and the authorization to be delegated encoded in the + Conditions field. How the expression digraph is constructed is + implementation-dependent and implementations may use different + algorithms for optimizing the graph's construction. Some + implementations might use a `bottom up' traversal starting at the + principals that requested the action, others might follow a `top + down' approach starting at the POLICY assertions, and still others + might employ other heuristics entirely. + + + 6. Examples + + In this section, we give examples of KeyNote assertions that might be + used in hypothetical applications. These examples are intended + primarily to illustrate features of KeyNote assertion syntax and + semantics, and do not necessarily represent the best way to integrate + KeyNote into applications. + + In the interest of readability, we use much shorter keys than would + ordinarily be used in practice. Note that the Signature fields in + these examples do not represent the result of any real signature + calculation. + + 1. TRADITIONAL CA / EMAIL + + A. A policy unconditionally authorizing RSA key abc123 for all + actions. This essentially defers the ability to specify + policy to the holder of the secret key corresponding to + abc123: + + Authorizer: "POLICY" + Licensees: "RSA:abc123" + + B. A credential assertion in which RSA Key abc123 trusts either + RSA key 4401ff92 (called `Alice') or DSA key d1234f (called + `Bob') to perform actions in which the "app_domain" is + "RFC822-EMAIL", where the "address" matches the regular + expression "^.*@keynote\.research\.att\.com$". In other + words, abc123 trusts Alice and Bob as certification + authorities for the keynote.research.att.com domain. + + KeyNote-Version: 2 + Local-Constants: Alice="DSA:4401ff92" # Alice's key + Bob="RSA:d1234f" # Bob's key + Authorizer: "RSA:abc123" + Licensees: Alice || Bob + Conditions: (app_domain == "RFC822-EMAIL") && + (address ~= # only applies to one domain + "^.*@keynote\\.research\\.att\\.com$"); + Signature: "RSA-SHA1:213354f9" + + C. A certificate credential for a specific user whose email + address is mab@keynote.research.att.com and whose name, if + present, must be "M. Blaze". The credential was issued by the + `Alice' authority (whose key is certified in Example B + above): + + KeyNote-Version: 2 + Authorizer: "DSA:4401ff92" # the Alice CA + Licensees: "DSA:12340987" # mab's key + Conditions: ((app_domain == "RFC822-EMAIL") && + (name == "M. Blaze" || name == "") && + (address == "mab@keynote.research.att.com")); + Signature: "DSA-SHA1:ab23487" + + D. Another certificate credential for a specific user, also + issued by the `Alice' authority. This example allows three + different keys to sign as jf@keynote.research.att.com (each + for a different cryptographic algorithm). This is, in + effect, three credentials in one: + + KeyNote-Version: "2" + Authorizer: "DSA:4401ff92" # the Alice CA + Licensees: "DSA:abc991" || # jf's DSA key + "RSA:cde773" || # jf's RSA key + "BFIK:fd091a" # jf's BFIK key + Conditions: ((app_domain == "RFC822-EMAIL") && + (name == "J. Feigenbaum" || name == "") && + (address == "jf@keynote.research.att.com")); + Signature: "DSA-SHA1:8912aa" + + Observe that under policy A and credentials B, C and D, the + following action attribute sets are accepted (they return + _MAX_TRUST): + + _ACTION_AUTHORIZERS = "dsa:12340987" + app_domain = "RFC822-EMAIL" + address = "mab@keynote.research.att.com" + and + _ACTION_AUTHORIZERS = "dsa:12340987" + app_domain = "RFC822-EMAIL" + address = "mab@keynote.research.att.com" + name = "M. Blaze" + + while the following are not accepted (they return + _MIN_TRUST): + + _ACTION_AUTHORIZERS = "dsa:12340987" + app_domain = "RFC822-EMAIL" + address = "angelos@dsl.cis.upenn.edu" + and + _ACTION_AUTHORIZERS = "dsa:abc991" + app_domain = "RFC822-EMAIL" + address = "mab@keynote.research.att.com" + name = "M. Blaze" + and + _ACTION_AUTHORIZERS = "dsa:12340987" + app_domain = "RFC822-EMAIL" + address = "mab@keynote.research.att.com" + name = "J. Feigenbaum" + + 2. WORKFLOW/ELECTRONIC COMMERCE + + E. A policy that delegates authority for the "SPEND" application + domain to RSA key dab212 when the amount given in the + "dollars" attribute is less than 10000. + + Authorizer: "POLICY" + Licensees: "RSA:dab212" # the CFO's key + Conditions: (app_domain=="SPEND") && (@dollars < 10000); + + F. RSA key dab212 delegates authorization to any two signers, + from a list, one of which must be DSA key feed1234 in the + "SPEND" application when @dollars < 7500. If the amount in + @dollars is 2500 or greater, the request is approved but + logged. + + KeyNote-Version: 2 + Comment: This credential specifies a spending policy + Authorizer: "RSA:dab212" # the CFO + Licensees: "DSA:feed1234" && # The vice president + ("RSA:abc123" || # middle manager #1 + "DSA:bcd987" || # middle manager #2 + "DSA:cde333" || # middle manager #3 + "DSA:def975" || # middle manager #4 + "DSA:978add") # middle manager #5 + Conditions: (app_domain=="SPEND") # note nested clauses + -> { (@(dollars) < 2500) + -> _MAX_TRUST; + (@(dollars) < 7500) + -> "ApproveAndLog"; + }; + Signature: "RSA-SHA1:9867a1" + + G. According to this policy, any two signers from the list of + managers will do if @(dollars) < 1000: + + KeyNote-Version: 2 + Authorizer: "POLICY" + Licensees: 2-of("DSA:feed1234", # The VP + "RSA:abc123", # Middle management clones + "DSA:bcd987", + "DSA:cde333", + "DSA:def975", + "DSA:978add") + Conditions: (app_domain=="SPEND") && + (@(dollars) < 1000); + + H. A credential from dab212 with a similar policy, but only one + signer is required if @(dollars) < 500. A log entry is made if + the amount is at least 100. + + KeyNote-Version: 2 + Comment: This one credential is equivalent to six separate + credentials, one for each VP and middle manager. + Individually, they can spend up to $500, but if + it's $100 or more, we log it. + Authorizer: "RSA:dab212" # From the CFO + Licensees: "DSA:feed1234" || # The VP + "RSA:abc123" || # The middle management clones + "DSA:bcd987" || + "DSA:cde333" || + "DSA:def975" || + "DSA:978add" + Conditions: (app_domain="SPEND") # nested clauses + -> { (@(dollars) < 100) -> _MAX_TRUST; + (@(dollars) < 500) -> "ApproveAndLog"; + }; + Signature: "RSA-SHA1:186123" + + Assume a query in which the ordered set of Compliance Values is + {"Reject", "ApproveAndLog", "Approve"}. Under policies E and G, + and credentials F and H, the Policy Compliance Value is + "Approve" (_MAX_TRUST) when: + + _ACTION_AUTHORIZERS = "DSA:978add" + app_domain = "SPEND" + dollars = "45" + unmentioned_attribute = "whatever" + and + _ACTION_AUTHORIZERS = "RSA:abc123,DSA:cde333" + app_domain = "SPEND" + dollars = "550" + + The following return "ApproveAndLog": + + _ACTION_AUTHORIZERS = "DSA:feed1234,DSA:cde333" + app_domain = "SPEND" + dollars = "5500" + and + _ACTION_AUTHORIZERS = "DSA:cde333" + app_domain = "SPEND" + dollars = "150" + + However, the following return "Reject" (_MIN_TRUST): + + _ACTION_AUTHORIZERS = "DSA:def975" + app_domain = "SPEND" + dollars = "550" + and + _ACTION_AUTHORIZERS = "DSA:cde333,DSA:978add" + app_domain = "SPEND" + dollars = "5500" + + +7. Trust-Management Architecture + + KeyNote provides a simple mechanism for describing security policy + and representing credentials. It differs from traditional + certification systems in that the security model is based on binding + keys to predicates that describe what the key is authorized by policy + to do, rather than on resolving names. The infrastructure and + architecture to support a KeyNote system is therefore rather + different from that required for a name-based certification scheme. + The KeyNote trust-management architecture is based on that of + PolicyMaker [BFL96,BFS98]. + + It is important to understand the separation between the + responsibilities of the KeyNote system and those of the application + and other support infrastructure. A KeyNote compliance checker will + determine, based on policy and credential assertions, whether a + proposed action is permitted according to policy. The usefulness of + KeyNote output as a policy enforcement mechanism depends on a number + of factors: + + * The action attributes and the assignment of their values must + reflect accurately the security requirements of the application. + Identifying the attributes to include in the action attribute set + is perhaps the most important task in integrating KeyNote into + new applications. + + * The policy of the application must be correct and well-formed. + In particular, trust must be deferred only to principals that + should, in fact, be trusted by the application. + + * The application itself must be trustworthy. KeyNote does not + directly enforce policy; it only provides advice to the + applications that call it. In other words, KeyNote assumes that + the application itself is trusted and that the policy assertions + it specifies are correct. Nothing prevents an application from + submitting misleading or incorrect assertions to KeyNote or from + ignoring KeyNote altogether. + + It is also up to the application (or some service outside KeyNote) to + select the appropriate credentials and policy assertions with which + to run a particular query. Note, however, that even if inappropriate + credentials are provided to KeyNote, this cannot result in the + approval of an illegal action (as long as the policy assertions are + correct and the the action attribute set itself is correctly passed + to KeyNote). + + KeyNote is monotonic; adding an assertion to a query can never result + in a query's having a lower compliance value that it would have had + without the assertion. Omitting credentials may, of course, result + in legal actions being disallowed. Selecting appropriate credentials + (e.g., from a distributed database or `key server') is outside the + scope of the KeyNote language and may properly be handled by a remote + client making a request, by the local application receiving the + request, or by a network-based service, depending on the application. + + In addition, KeyNote does not itself provide credential revocation + services, although credentials can be written to expire after some + date by including a date test in the predicate. Applications that + require credential revocation can use KeyNote to help specify and + implement revocation policies. A future document will address + expiration and revocation services in KeyNote. + + Because KeyNote is designed to support a variety of applications, + several different application interfaces to a KeyNote implementation + are possible. In its simplest form, a KeyNote compliance checker + would exist as a stand-alone application, with other applications + calling it as needed. KeyNote might also be implemented as a library + to which applications are linked. Finally, a KeyNote implementation + might run as a local trusted service, with local applications + communicating their queries via some interprocess communication + mechanism. + + +8. Security Considerations + + Trust management is itself a security service. Bugs in or incorrect + use of a KeyNote compliance checker implementation could have + security implications for any applications in which it is used. + + +9. Acknowledgments + + We thank Lorrie Faith Cranor (AT&T Labs - Research) and Jonathan M. + Smith (University of Pennsylvania) for their suggestions and comments + on earlier versions of this. + + +References + + [BFL96] M. Blaze, J. Feigenbaum, J. Lacy. Decentralized Trust + Management. Proceedings of the 17th IEEE Symp. on Security + and Privacy. pp 164-173. IEEE Computer Society, + 1996. Available at + <ftp://ftp.research.att.com/dist/mab/policymaker.ps> + + [BFS98] M. Blaze, J. Feigenbaum, M. Strauss. Compliance-Checking in + the PolicyMaker Trust-Management System. Proc. 2nd + Financial Crypto Conference. Anguila 1998. LNCS #1465, pp + 251-265, Springer-Verlag, 1998. Available at + <ftp://ftp.research.att.com/dist/mab/pmcomply.ps> + + [Bla99] M. Blaze, J. Feigenbaum, J. Ioannidis, A. Keromytis. The + Role of Trust Management in Distributed System Security. + Chapter in Secure Internet Programming: Security Issues for + Mobile and Distributed Objects (Vitek and Jensen, eds.). + Springer-Verlag, 1999. Available at + <ftp://ftp.research.att.com/dist/mab/trustmgt.ps>. + + [Cro82] D. H. Crocker. Standard for the Format of ARPA Internet + Text Messages. RFC 822. August 1982. + + [DSA94] Digital Signature Standard. FIPS-186. National Institute of + Standards, U.S. Department of Commerce. May 1994. + + [PKCS1] PKCS #1: RSA Encryption Standard, Version 1.5. RSA + Laboratories. November 1993. + + [RSA78] R. L. Rivest, A. Shamir, L. M. Adleman. A Method for + Obtaining Digital Signatures and Public-Key Cryptosystems. + Communications of the ACM, v21n2. pp 120-126. February + 1978. + +Contacts + + Comments about this document should be discussed on the + trustmgt@east.isi.edu mailing list. The archive for that list can be + found at http://www.cairn.net/trustmgt/. + + Questions about this document can also be directed to the authors as + a group at the keynote@research.att.com alias, or to the individual + authors at: + + Matt Blaze Joan Feigenbaum John Ioannidis + mab@research.att.com jf@research.att.com ji@research.att.com + + AT&T Labs - Research + 180 Park Avenue + Florham Park, New Jersey 07932-0000 + + Angelos D. Keromytis + Distributed Systems Lab + CIS Department, University of Pennsylvania + 200 S. 33rd Street + Philadelphia, Pennsylvania 19104-6389 + Email: angelos@dsl.cis.upenn.edu + +Full Copyright Statement + + Copyright (C) The Internet Society (1999). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/libkeynote/environment.c b/lib/libkeynote/environment.c new file mode 100644 index 00000000000..a8c0fce540d --- /dev/null +++ b/lib/libkeynote/environment.c @@ -0,0 +1,874 @@ +/* $OpenBSD: environment.c,v 1.1 1999/05/23 22:11:04 angelos 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 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#ifdef WIN32 +#include <io.h> +#else +#include <unistd.h> +#endif + +#include "environment.h" +#include "signature.h" + +static int sessioncounter = 0; + +/* Globals */ +struct keynote_session *keynote_sessions[SESSIONTABLESIZE]; +struct keynote_session *keynote_current_session; +int keynote_justrecord = 0; +int keynote_returnvalue = 0; + +/* + * Construct the _ACTION_AUTHORIZERS variable value. + */ +static char * +keynote_get_action_authorizers(char *name) +{ + struct keylist *kl; + int len; + + if (!strcmp(name, KEYNOTE_CALLBACK_CLEANUP) || + !strcmp(name, KEYNOTE_CALLBACK_INITIALIZE)) + { + if (keynote_current_session->ks_authorizers_cache != (char *) NULL) + { + free(keynote_current_session->ks_authorizers_cache); + keynote_current_session->ks_authorizers_cache = (char *) NULL; + } + + return ""; + } + + if (keynote_current_session->ks_authorizers_cache != (char *) NULL) + return keynote_current_session->ks_authorizers_cache; + + for (len = 0, kl = keynote_current_session->ks_action_authorizers; + kl != (struct keylist *) NULL; + kl = kl->key_next) + if (kl->key_stringkey != (char *) NULL) + len += strlen(kl->key_stringkey) + 1; + + if (len == 0) + return ""; + + keynote_current_session->ks_authorizers_cache = (char *) calloc(len, sizeof(char)); + if (keynote_current_session->ks_authorizers_cache == (char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return (char *) NULL; + } + + for (len = 0, kl = keynote_current_session->ks_action_authorizers; + kl != (struct keylist *) NULL; + kl = kl->key_next) + if (kl->key_stringkey != (char *) NULL) + { + sprintf(keynote_current_session->ks_authorizers_cache + len, "%s,", + kl->key_stringkey); + len += strlen(kl->key_stringkey) + 1; + } + + keynote_current_session->ks_authorizers_cache[len - 1] = '\0'; + return keynote_current_session->ks_authorizers_cache; +} + +/* + * Construct the _VALUES variable value. + */ +static char * +keynote_get_values(char *name) +{ + int i, len; + + if (!strcmp(name, KEYNOTE_CALLBACK_CLEANUP) || + !strcmp(name, KEYNOTE_CALLBACK_INITIALIZE)) + { + if (keynote_current_session->ks_values_cache != (char *) NULL) + { + free(keynote_current_session->ks_values_cache); + keynote_current_session->ks_values_cache = (char *) NULL; + } + + return ""; + } + + if (keynote_current_session->ks_values_cache != (char *) NULL) + return keynote_current_session->ks_values_cache; + + for (len = 0, i = 0; i < keynote_current_session->ks_values_num; i++) + len += strlen(keynote_current_session->ks_values[i]) + 1; + + keynote_current_session->ks_values_cache = (char *) calloc(len, + sizeof(char)); + if (keynote_current_session->ks_values_cache == (char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return (char *) NULL; + } + + if (len == 0) + return ""; + + for (len = 0, i = 0; i < keynote_current_session->ks_values_num; i++) + { + sprintf(keynote_current_session->ks_values_cache + len, "%s,", + keynote_current_session->ks_values[i]); + len += strlen(keynote_current_session->ks_values[i]) + 1; + } + + keynote_current_session->ks_values_cache[len - 1] = '\0'; + return keynote_current_session->ks_values_cache; +} + +/* + * Free an environment structure. + */ +void +keynote_free_env(struct environment *en) +{ + if (en == (struct environment *) NULL) + return; + + if (en->env_name != (char *) NULL) + free(en->env_name); + + if (en->env_flags & ENVIRONMENT_FLAG_REGEX) + regfree(&(en->env_regex)); + + if (!(en->env_flags & ENVIRONMENT_FLAG_FUNC)) + { + if (en->env_value != (char *) NULL) + free(en->env_value); + } + else + ((char * (*) (char *))en->env_value)(KEYNOTE_CALLBACK_CLEANUP); + + free(en); +} + +/* + * Lookup for variable "name" in the hash table. If hashsize is 1, + * then the second argument is actually a pointer to a list. Last + * argument specifies case-insensitivity. + */ +char * +keynote_env_lookup(char *name, struct environment **table, u_int hashsize) +{ + struct environment *en; + + for (en = table[keynote_stringhash(name, hashsize)]; + en != (struct environment *) NULL; + en = en->env_next) + if (((en->env_flags & ENVIRONMENT_FLAG_REGEX) && + (regexec(&(en->env_regex), name, 0, (regmatch_t *) NULL, 0) == + 0)) || (!strcmp(name, en->env_name))) + { + if ((en->env_flags & ENVIRONMENT_FLAG_FUNC) && + (en->env_value != (char *) NULL)) + return ((char * (*) (char *)) en->env_value)(name); + else + return en->env_value; + } + + return (char *) NULL; +} + +/* + * Delete a variable from hash table. Return RESULT_TRUE if the deletion was + * successful, and RESULT_FALSE if the variable was not found. + */ +int +keynote_env_delete(char *name, struct environment **table, u_int hashsize) +{ + struct environment *en, *en2; + u_int h; + + h = keynote_stringhash(name, hashsize); + + if (table[h] != (struct environment *) NULL) + { + if (!strcmp(table[h]->env_name, name)) + { + en = table[h]; + table[h] = en->env_next; + keynote_free_env(en); + return RESULT_TRUE; + } + else + for (en = table[h]; + en->env_next != (struct environment *) NULL; + en = en->env_next) + if (!strcmp(en->env_next->env_name, name)) + { + en2 = en->env_next; + en->env_next = en2->env_next; + keynote_free_env(en2); + return RESULT_TRUE; + } + } + + return RESULT_FALSE; +} + +/* + * Add a new variable in hash table. Return RESULT_TRUE on success, + * ERROR_MEMORY on failure. If hashsize is 1, second argument is + * actually a pointer to a list. The arguments are duplicated. + */ +int +keynote_env_add(char *name, char *value, struct environment **table, + u_int hashsize, int flags) +{ + struct environment *en; + u_int h, i; + + en = calloc(1, sizeof(struct environment)); + if (en == (struct environment *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + en->env_name = strdup(name); + if (en->env_name == (char *) NULL) + { + keynote_free_env(en); + keynote_errno = ERROR_MEMORY; + return -1; + } + + if (flags & ENVIRONMENT_FLAG_REGEX) /* Regular expression for name */ + { + if ((i = regcomp(&(en->env_regex), name, REG_EXTENDED)) != 0) + { + keynote_free_env(en); + if (i == REG_ESPACE) + keynote_errno = ERROR_MEMORY; + else + keynote_errno = ERROR_SYNTAX; + return -1; + } + en->env_flags |= ENVIRONMENT_FLAG_REGEX; + } + + if (flags & ENVIRONMENT_FLAG_FUNC) /* Callback registration */ + { + en->env_value = value; + en->env_flags |= ENVIRONMENT_FLAG_FUNC; + ((char * (*) (char *))en->env_value)(KEYNOTE_CALLBACK_INITIALIZE); + if (keynote_errno != 0) + { + keynote_free_env(en); + return -1; + } + } + else + { + en->env_value = strdup(value); + if (en->env_value == (char *) NULL) + { + keynote_free_env(en); + keynote_errno = ERROR_MEMORY; + return -1; + } + } + + /* + * This means that new assignments of existing variable will override + * the old ones. + */ + h = keynote_stringhash(name, hashsize); + en->env_next = table[h]; + table[h] = en; + return RESULT_TRUE; +} + +/* + * Cleanup an environment table. + */ +void +keynote_env_cleanup(struct environment **table, u_int hashsize) +{ + struct environment *en2; + + if ((hashsize == 0) || (table == (struct environment **) NULL)) + return; + + while (hashsize > 0) + { + while (table[hashsize - 1] != (struct environment *) NULL) + { + en2 = table[hashsize - 1]->env_next; + keynote_free_env(table[hashsize - 1]); + table[hashsize - 1] = en2; + } + + hashsize--; + } +} + +/* + * Zero out the attribute structures, seed the RNG. + */ +static int +keynote_init_environment(void) +{ +#ifdef CRYPTO +#if defined(KEYNOTERNDFILENAME) + int cnt = KEYNOTE_RAND_INIT_LEN, i; + + do + { + if ((i = RAND_load_file(KEYNOTERNDFILENAME, cnt)) <= 0) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + cnt -= i; + } while (cnt > 0); +#else /* KEYNOTERNDFILENAME */ +#error "You need to seed the RNG." +#endif /* KEYNOTERNDFILENAME */ +#endif /* CRYPTO */ + memset(keynote_current_session->ks_env_table, 0, + HASHTABLESIZE * sizeof(struct environment *)); + memset(keynote_current_session->ks_assertion_table, 0, + HASHTABLESIZE * sizeof(struct assertion *)); + keynote_current_session->ks_env_regex = (struct environment *) NULL; + + if (keynote_env_add("_ACTION_AUTHORIZERS", + (char *) keynote_get_action_authorizers, + keynote_current_session->ks_env_table, HASHTABLESIZE, + ENVIRONMENT_FLAG_FUNC) != RESULT_TRUE) + return -1; + + if (keynote_env_add("_VALUES", (char *) keynote_get_values, + keynote_current_session->ks_env_table, HASHTABLESIZE, + ENVIRONMENT_FLAG_FUNC) != RESULT_TRUE) + return -1; + + return RESULT_TRUE; +} + +/* + * Return the index of argument in keynote_values[]. + */ +int +keynote_retindex(char *s) +{ + int i; + + for (i = 0; i < keynote_current_session->ks_values_num; i++) + if (!strcmp(s, keynote_current_session->ks_values[i])) + return i; + + return -1; +} + +/* + * Find a session by its id. + */ +struct keynote_session * +keynote_find_session(int sessid) +{ + unsigned int h = sessid % SESSIONTABLESIZE; + struct keynote_session *ks; + + for (ks = keynote_sessions[h]; + ks != (struct keynote_session *) NULL; + ks = ks->ks_next) + if (ks->ks_id == sessid) + return ks; + + return (struct keynote_session *) NULL; +} + +/* + * Add a session in the hash table. + */ +static void +keynote_add_session(struct keynote_session *ks) +{ + unsigned int h = ks->ks_id % SESSIONTABLESIZE; + + ks->ks_next = keynote_sessions[h]; + if (ks->ks_next != (struct keynote_session *) NULL) + ks->ks_next->ks_prev = ks; + + keynote_sessions[h] = ks; +} + +/* + * Initialize a KeyNote session. + */ +int +kn_init(void) +{ + keynote_errno = 0; + keynote_current_session = (struct keynote_session *) calloc(1, sizeof(struct keynote_session)); + if (keynote_current_session == (struct keynote_session *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + while (keynote_find_session(sessioncounter) != + (struct keynote_session *) NULL) + { + sessioncounter++; + if (sessioncounter < 0) + sessioncounter = 0; + } + + keynote_current_session->ks_id = sessioncounter++; + keynote_init_environment(); + keynote_add_session(keynote_current_session); + return keynote_current_session->ks_id; +} + +/* + * Close a session. + */ +int +kn_close(int sessid) +{ + struct keynote_session *ks; + struct assertion *as, *as2; + int i; + + keynote_errno = 0; + if ((keynote_current_session == (struct keynote_session *) NULL) || + (keynote_current_session->ks_id != sessid)) + { + keynote_current_session = keynote_find_session(sessid); + if (keynote_current_session == (struct keynote_session *) NULL) + { + keynote_errno = ERROR_NOTFOUND; + return -1; + } + } + + ks = keynote_current_session; + + /* Cleanup environment */ + keynote_env_cleanup(ks->ks_env_table, HASHTABLESIZE); + keynote_env_cleanup(&(ks->ks_env_regex), 1); + + /* Cleanup assertions */ + for (i = 0; i < HASHTABLESIZE; i++) + for (as = ks->ks_assertion_table[i]; + as != (struct assertion *) NULL; + as = as2) + { + as2 = as->as_next; + keynote_free_assertion(as); + } + + /* Cleanup action authorizers */ + keynote_keylist_free(ks->ks_action_authorizers); + + /* Unlink from chain */ + if (ks->ks_prev == (struct keynote_session *) NULL) + { + keynote_sessions[ks->ks_id % SESSIONTABLESIZE] = ks->ks_next; + if (ks->ks_next != (struct keynote_session *) NULL) + ks->ks_next->ks_prev = (struct keynote_session *) NULL; + + } + else + { + ks->ks_prev->ks_next = ks->ks_next; + if (ks->ks_next != (struct keynote_session *) NULL) + ks->ks_next->ks_prev = ks->ks_prev; + } + + free(ks); + keynote_current_session = (struct keynote_session *) NULL; + return 0; +} + +/* + * Add an action attribute. + */ +int +kn_add_action(int sessid, char *name, char *value, int flags) +{ + int i; + + keynote_errno = 0; + if ((name == (char *) NULL) || (value == (char *) NULL) || + (name[0] == '_')) + { + keynote_errno = ERROR_SYNTAX; + return -1; + } + + if ((keynote_current_session == (struct keynote_session *) NULL) || + (keynote_current_session->ks_id != sessid)) + { + keynote_current_session = keynote_find_session(sessid); + if (keynote_current_session == (struct keynote_session *) NULL) + { + keynote_errno = ERROR_NOTFOUND; + return -1; + } + } + + if (flags & ENVIRONMENT_FLAG_REGEX) + i = keynote_env_add(name, value, + &(keynote_current_session->ks_env_regex), 1, flags); + else + i = keynote_env_add(name, value, keynote_current_session->ks_env_table, + HASHTABLESIZE, flags); + + if (i == RESULT_TRUE) + return 0; + else + return -1; +} + +/* + * Remove an action attribute. + */ +int +kn_remove_action(int sessid, char *name) +{ + int i; + + keynote_errno = 0; + if ((name == (char *) NULL) || (name[0] == '_')) + { + keynote_errno = ERROR_SYNTAX; + return -1; + } + + if ((keynote_current_session == (struct keynote_session *) NULL) || + (keynote_current_session->ks_id != sessid)) + { + keynote_current_session = keynote_find_session(sessid); + if (keynote_current_session == (struct keynote_session *) NULL) + { + keynote_errno = ERROR_NOTFOUND; + return -1; + } + } + + i = keynote_env_delete(name, keynote_current_session->ks_env_table, + HASHTABLESIZE); + if (i == RESULT_TRUE) + return 0; + + i = keynote_env_delete(name, &(keynote_current_session->ks_env_regex), + HASHTABLESIZE); + if (i == RESULT_TRUE) + return 0; + + keynote_errno = ERROR_NOTFOUND; + return -1; +} + +/* + * Execute a query. + */ +int +kn_do_query(int sessid, char **returnvalues, int numvalues) +{ + struct assertion *as; + int i; + + keynote_errno = 0; + if ((keynote_current_session == (struct keynote_session *) NULL) || + (keynote_current_session->ks_id != sessid)) + { + keynote_current_session = keynote_find_session(sessid); + if (keynote_current_session == (struct keynote_session *) NULL) + { + keynote_errno = ERROR_NOTFOUND; + return -1; + } + } + + /* Check that we have at least one action authorizer */ + if (keynote_current_session->ks_action_authorizers == + (struct keylist *) NULL) + { + keynote_errno = ERROR_NOTFOUND; + return -1; + } + + /* + * We may use already set returnvalues, or use new ones, + * but we must have some before we can evaluate. + */ + if ((returnvalues == (char **) NULL) && + (keynote_current_session->ks_values == (char **) NULL)) + { + keynote_errno = ERROR_SYNTAX; + return -1; + } + + /* Replace any existing returnvalues */ + if (returnvalues != (char **) NULL) + { + keynote_current_session->ks_values = returnvalues; + keynote_current_session->ks_values_num = numvalues; + } + + /* Reset assertion state from any previous queries */ + for (i = 0; i < HASHTABLESIZE; i++) + for (as = keynote_current_session->ks_assertion_table[i]; + as != (struct assertion *) NULL; + as = as->as_next) + { + as->as_kresult = KRESULT_UNTOUCHED; + as->as_result = 0; + as->as_internalflags &= ~ASSERT_IFLAG_PROCESSED; + as->as_error = 0; + if (as->as_internalflags & ASSERT_IFLAG_WEIRDSIG) + as->as_sigresult = SIGRESULT_UNTOUCHED; + } + + return keynote_evaluate_query(); +} + +/* + * Return assertions that failed, by error type. + */ +int +kn_get_failed(int sessid, int type, int num) +{ + struct assertion *as; + int i; + + if ((keynote_current_session == (struct keynote_session *) NULL) || + (keynote_current_session->ks_id != sessid)) + { + keynote_current_session = keynote_find_session(sessid); + if (keynote_current_session == (struct keynote_session *) NULL) + { + keynote_errno = ERROR_NOTFOUND; + return -1; + } + } + + for (i = 0; i < HASHTABLESIZE; i++) + for (as = keynote_current_session->ks_assertion_table[i]; + as != (struct assertion *) NULL; + as = as->as_next) + switch (type) + { + case KEYNOTE_ERROR_ANY: + if ((as->as_error != 0) || + ((as->as_sigresult != SIGRESULT_TRUE) && + !(as->as_flags & ASSERT_FLAG_LOCAL))) + if (num-- == 0) /* Return it if it's the num-th found */ + return as->as_id; + break; + + case KEYNOTE_ERROR_MEMORY: + if (as->as_error == ERROR_MEMORY) + if (num-- == 0) + return as->as_id; + break; + + case KEYNOTE_ERROR_SYNTAX: + if (as->as_error == ERROR_SYNTAX) + if (num-- == 0) + return as->as_id; + break; + + case KEYNOTE_ERROR_SIGNATURE: + if ((as->as_sigresult != SIGRESULT_TRUE) && + !(as->as_flags & ASSERT_FLAG_LOCAL)) + if (num-- == 0) + return as->as_id; + break; + } + + keynote_errno = ERROR_NOTFOUND; + return -1; +} + +/* + * Simple API for doing a single KeyNote query. + */ +int +kn_query(struct environment *env, char **retvalues, int numval, + char **trusted, int *trustedlen, int numtrusted, + char **untrusted, int *untrustedlen, int numuntrusted, + char **authorizers, int numauthorizers) +{ + struct environment *en; + int sessid, i, serrno; + + keynote_errno = 0; + if ((sessid = kn_init()) == -1) + return -1; + + /* Action set */ + for (en = env; en != (struct environment *) NULL; en = en->env_next) + kn_add_action(sessid, en->env_name, en->env_value, en->env_flags); + + /* Locally trusted assertions */ + for (i = 0; i < numtrusted; i++) + kn_add_assertion(sessid, trusted[i], trustedlen[i], ASSERT_FLAG_LOCAL); + + /* Untrusted assertions */ + for (i = 0; i < numuntrusted; i++) + kn_add_assertion(sessid, untrusted[i], untrustedlen[i], 0); + + /* Authorizers */ + for (i = 0; i < numauthorizers; i++) + kn_add_authorizer(sessid, authorizers[i]); + + i = kn_do_query(sessid, retvalues, numval); + serrno = keynote_errno; + kn_close(sessid); + + if (serrno) + keynote_errno = serrno; + + return i; +} + +/* + * Read a buffer, break it up in assertions. + */ +char ** +kn_read_asserts(char *buffer, int bufferlen, int *numassertions) +{ + int bufsize = 32, i, flag, valid; + char **buf, **tempbuf, *ptr; + + keynote_errno = 0; + + if (buffer == (char *) NULL) + { + keynote_errno = ERROR_SYNTAX; + return (char **) NULL; + } + + buf = (char **) calloc(bufsize, sizeof(char *)); + if (buf == (char **) NULL) + { + keynote_errno = ERROR_MEMORY; + return (char **) NULL; + } + + /* + * We'll go through the whole buffer looking for consecutive newlines, + * which imply newline separation. We use the valid flag to keep + * track of whether there may be an assertion after the last pair of + * newlines, or whether there may be an assertion in the buffer to + * begin with, if there are no consecutive newlines. + */ + for (i = 0, flag = 0, valid = 0, *numassertions = 0, ptr = buffer; + i < bufferlen; + i++) + { + if (buffer[i] == '\n') + { + if (flag) /* Two newlines in a row, copy if there's anything */ + { + if (valid) /* Something there */ + { + /* Allocate enough memory */ + buf[*numassertions] = (char *) calloc((buffer + i) - ptr + + 1, sizeof(char)); + if (buf[*numassertions] == (char *) NULL) + { + /* Free any already-allocated strings */ + for (flag = 0; flag < *numassertions; flag++) + free(buf[flag]); + free(buf); + keynote_errno = ERROR_MEMORY; + return (char **) NULL; + } + + /* Copy string */ + bcopy(ptr, buf[*numassertions], (buffer + i) - ptr); + (*numassertions)++; + } + + valid = 0; /* Reset */ + flag = 0; + ptr = buffer + i + 1; /* Point right after this newline */ + + /* See if we need to resize the buffer */ + if (*numassertions > bufsize - 4) + { + /* Allocate twice the space */ + tempbuf = (char **) realloc(buf, 2 * bufsize * + sizeof(char *)); + if (tempbuf == (char **) NULL) + { + for (flag = 0; flag < *numassertions; flag++) + free(buf[flag]); + free(buf); + keynote_errno = ERROR_MEMORY; + return (char **) NULL; + } + + free(buf); /* Free old buffer */ + buf = tempbuf; + bufsize *= 2; + } + } + else + flag = 1; /* One newline so far */ + + continue; + } + else + flag = 0; + + if (!isspace(buffer[i])) + valid = 1; + } + + /* + * There may be a valid assertion after the last pair of newlines. + * Notice that because of the resizing check above, there will be + * a valid memory location to store this last string. + */ + if (valid) + { + /* This one's easy, we can just use strdup() */ + if ((buf[*numassertions] = strdup(ptr)) == (char *) NULL) + { + for (flag = 0; flag < *numassertions; flag++) + free(buf[flag]); + free(buf); + keynote_errno = ERROR_MEMORY; + return (char **) NULL; + } + (*numassertions)++; + } + + return buf; +} diff --git a/lib/libkeynote/environment.h b/lib/libkeynote/environment.h new file mode 100644 index 00000000000..5baba46e290 --- /dev/null +++ b/lib/libkeynote/environment.h @@ -0,0 +1,68 @@ +/* $OpenBSD: environment.h,v 1.1 1999/05/23 22:11:04 angelos 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 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. + */ + +#ifndef __ENVIRONMENT_H__ +#define __ENVIRONMENT_H__ + +#include "keynote.h" + +#define KEYNOTE_RAND_INIT_LEN 1024 + +/* + * These can be changed to reflect more assertions/session or more + * sessions respectively + */ +#define HASHTABLESIZE 37 +#define SESSIONTABLESIZE 37 + +struct keynote_session +{ + int ks_id; + int ks_assertioncounter; + int ks_values_num; + struct environment *ks_env_table[HASHTABLESIZE]; + struct environment *ks_env_regex; + struct keylist *ks_action_authorizers; + struct assertion *ks_assertion_table[HASHTABLESIZE]; + char **ks_values; + char *ks_authorizers_cache; + char *ks_values_cache; + struct keynote_session *ks_prev; + struct keynote_session *ks_next; +}; + +int keynote_env_add(char *, char *, struct environment **, u_int, int); +char *keynote_env_lookup(char *, struct environment **, u_int); +int keynote_env_delete(char *, struct environment **, u_int); +struct environment *keynote_get_envlist(char *, char *, int); +void keynote_env_cleanup(struct environment **, u_int); +struct keynote_session *keynote_find_session(int); +void keynote_free_env(struct environment *); +int keynote_sremove_assertion(int, int); +u_int keynote_stringhash(char *, u_int); +void keynote_cleanup_kth(void); +int keynote_retindex(char *); + +extern struct keynote_session *keynote_current_session; +extern int keynote_returnvalue; +extern int keynote_justrecord; +#endif /* __ENVIRONMENT_H__ */ diff --git a/lib/libkeynote/keynote-keygen.c b/lib/libkeynote/keynote-keygen.c new file mode 100644 index 00000000000..11dcfc371cc --- /dev/null +++ b/lib/libkeynote/keynote-keygen.c @@ -0,0 +1,408 @@ +/* $OpenBSD: keynote-keygen.c,v 1.1 1999/05/23 22:11:06 angelos 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 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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> + +#ifdef WIN32 +#include <ctype.h> +#include <io.h> +#else +#include <unistd.h> +#endif + +#include "signature.h" + +#define DEFAULT_PUBLIC 0x10001 + +void +usage(void) +{ + fprintf(stderr, "Arguments:\n"); + fprintf(stderr, "\t<AlgorithmName> <keysize> " + "<PublicKeyFile> <PrivateKeyFile> [<printf-offset> " + "<print-length>]\n"); +} + +#define SEED_LEN 40 +#define RND_BYTES 1024 + +#define KEY_PRINT_OFFSET 12 +#define KEY_PRINT_LENGTH 50 + +/* + * Print the specified number of spaces. + */ +void +print_space(FILE *fp, int n) +{ + while (n--) + fprintf(fp, " "); +} + +/* + * Output a key, properly formatted. + */ +void +print_key(FILE *fp, char *algname, char *key, int start, int length) +{ + int i, k; + + print_space(fp, start); + fprintf(fp, "\"%s", algname); + + for (i = 0, k = strlen(algname) + 2; i < strlen(key); i++, k++) + { + if (k == length) + { + if (i == strlen(key)) + { + fprintf(fp, "\"\n"); + return; + } + + fprintf(fp, "\\\n"); + print_space(fp, start); + i--; + k = 0; + } + else + fprintf(fp, "%c", key[i]); + } + + fprintf(fp, "\"\n"); +} + +#ifdef WIN32 +void +#else +int +#endif +main(int argc, char *argv[]) +{ + int begin = KEY_PRINT_OFFSET, prlen = KEY_PRINT_LENGTH; +#if defined(CRYPTO) || defined(PGPLIB) + char *foo, *privalgname, seed[SEED_LEN]; + int alg, enc, ienc, len = 0, counter; + struct keynote_deckey dc; + unsigned long h; + DSA *dsa; + RSA *rsa; + FILE *fp; +#if defined(KEYNOTERNDFILENAME) + int fd, cnt = RND_BYTES; +#endif /* KEYNOTERNDFILENAME */ +#endif /* CRYPTO || PGPLIB */ + char *algname; + + if ((argc != 5) && (argc != 6) && (argc != 7)) + { + usage(); + exit(0); + } + + /* Fix algorithm name */ + if (argv[1][strlen(argv[1]) - 1] != ':') + { + fprintf(stderr, "Algorithm name [%s] should be terminated with a " + "colon, fixing.\n", argv[1]); + algname = (char *) calloc(strlen(argv[1]) + 2, sizeof(char)); + if (algname == (char *) NULL) + { + perror("calloc()"); + exit(-1); + } + + strcpy(algname, argv[1]); + algname[strlen(algname)] = ':'; + } + else + algname = argv[1]; + + if (argc > 5) + { + begin = atoi(argv[5]); + if (begin <= -1) + { + fprintf(stderr, "Erroneous value for print-offset parameter.\n"); + exit(-1); + } + } + + if (argc > 6) + { + prlen = atoi(argv[6]); + if (prlen <= 0) + { + fprintf(stderr, "Erroneous value for print-length parameter.\n"); + exit(-1); + } + } + + if (strlen(algname) + 2 > prlen) + { + fprintf(stderr, "Parameter ``print-length'' should be larger " + "than the length of AlgorithmName (%d)\n", strlen(algname)); + exit(-1); + } + +#if defined(CRYPTO) || defined(PGPLIB) + alg = keynote_get_key_algorithm(algname, &enc, &ienc); + len = atoi(argv[2]); + + if (len <= 0) + { + fprintf(stderr, "Invalid specified keysize %d\n", len); + exit(-1); + } +#endif /* CRYPTO || PGPLIB */ + +#if defined(CRYPTO) +#if defined(KEYNOTERNDFILENAME) + fd = open(KEYNOTERNDFILENAME, O_RDONLY, 0); + if (fd < 0) + { + perror("open(\"/dev/urandom\")"); + exit(-1); + } + + for (h = 0; h < 5; h++) + { + if (read(fd, seed, SEED_LEN) <= 0) + { + perror("read()"); + exit(-1); + } + + RAND_seed(seed, SEED_LEN); + } + + if (read(fd, seed, SEED_LEN) < SEED_LEN) + { + perror("read()"); + exit(-1); + } + + close(fd); + + /* Make sure we read RND_BYTES bytes */ + do + { + if ((fd = RAND_load_file(KEYNOTERNDFILENAME, cnt)) <= 0) + { + perror("RAND_load_file()"); + exit(-1); + } + + cnt -= fd; + } while (cnt > 0); + +#else /* KEYNOTERNDFILENAME */ +#error "No RNG available!" +#endif /* KEYNOTERNDFILENAME */ + + if ((alg == KEYNOTE_ALGORITHM_DSA) && + (ienc == INTERNAL_ENC_ASN1) && + ((enc == ENCODING_HEX) || (enc == ENCODING_BASE64))) + { + dsa = DSA_generate_parameters(len, seed, SEED_LEN, &counter, &h, NULL +#if SSLEAY_VERSION_NUMBER >= 0x0900 + , NULL +#endif /* SSLEAY_VERSION_NUMBER */ + ); + + if (dsa == (DSA *) NULL) + { + ERR_print_errors_fp(stderr); + exit(-1); + } + + if (DSA_generate_key(dsa) != 1) + { + ERR_print_errors_fp(stderr); + exit(-1); + } + + dc.dec_algorithm = KEYNOTE_ALGORITHM_DSA; + dc.dec_key = (void *) dsa; + + foo = kn_encode_key(&dc, ienc, enc, KEYNOTE_PUBLIC_KEY); + if (foo == (char *) NULL) + { + fprintf(stderr, "Error encoding key (errno %d)\n", keynote_errno); + exit(-1); + } + + if (!strcmp(argv[3], "-")) + fp = stdout; + else + { + fp = fopen(argv[3], "w"); + if (fp == (FILE *) NULL) + { + perror("fopen()"); + exit(-1); + } + } + + print_key(fp, algname, foo, begin, prlen); + free(foo); + + if (strcmp(argv[3], "-")) + fclose(fp); + + foo = kn_encode_key(&dc, ienc, enc, KEYNOTE_PRIVATE_KEY); + if (foo == (char *) NULL) + { + fprintf(stderr, "Error encoding key (errno %d)\n", keynote_errno); + exit(-1); + } + + if (!strcmp(argv[4], "-")) + { + fp = stdout; + if (!strcmp(argv[3], "-")) + printf("===========================\n"); + } + else + { + fp = fopen(argv[4], "w"); + if (fp == (FILE *) NULL) + { + perror("fopen()"); + exit(-1); + } + } + + privalgname = (char *) calloc(strlen(KEYNOTE_PRIVATE_KEY_PREFIX) + + strlen(foo) + 1, sizeof(char)); + if (privalgname == (char *) NULL) + { + perror("calloc()"); + exit(-1); + } + sprintf(privalgname, "%s%s", KEYNOTE_PRIVATE_KEY_PREFIX, algname); + print_key(fp, privalgname, foo, begin, prlen); + free(privalgname); + free(foo); + + if (strcmp(argv[4], "-")) + fclose(fp); + + exit(0); + } + + if ((alg == KEYNOTE_ALGORITHM_RSA) && + (ienc == INTERNAL_ENC_PKCS1) && + ((enc == ENCODING_HEX) || (enc == ENCODING_BASE64))) + { + rsa = RSA_generate_key(len, DEFAULT_PUBLIC, NULL +#if SSLEAY_VERSION_NUMBER >= 0x0900 + , NULL +#endif /* SSLEAY_VERSION_NUMBER */ + ); + + if (rsa == (RSA *) NULL) + { + ERR_print_errors_fp(stderr); + exit(-1); + } + + dc.dec_algorithm = KEYNOTE_ALGORITHM_RSA; + dc.dec_key = (void *) rsa; + + foo = kn_encode_key(&dc, ienc, enc, KEYNOTE_PUBLIC_KEY); + if (foo == (char *) NULL) + { + fprintf(stderr, "Error encoding key (errno %d)\n", keynote_errno); + exit(-1); + } + + if (!strcmp(argv[3], "-")) + fp = stdout; + else + { + fp = fopen(argv[3], "w"); + if (fp == (FILE *) NULL) + { + perror("fopen()"); + exit(-1); + } + } + + print_key(fp, algname, foo, begin, prlen); + free(foo); + + if (strcmp(argv[3], "-")) + fclose(fp); + + foo = kn_encode_key(&dc, ienc, enc, KEYNOTE_PRIVATE_KEY); + if (foo == (char *) NULL) + { + fprintf(stderr, "Error encoding key (errno %d)\n", keynote_errno); + exit(-1); + } + + if (!strcmp(argv[4], "-")) + { + fp = stdout; + if (!strcmp(argv[3], "-")) + printf("===========================\n"); + } + else + { + fp = fopen(argv[4], "w"); + if (fp == (FILE *) NULL) + { + perror("fopen()"); + exit(-1); + } + } + + privalgname = (char *) calloc(strlen(KEYNOTE_PRIVATE_KEY_PREFIX) + + strlen(foo) + 1, sizeof(char)); + if (privalgname == (char *) NULL) + { + perror("calloc()"); + exit(-1); + } + sprintf(privalgname, "%s%s", KEYNOTE_PRIVATE_KEY_PREFIX, algname); + print_key(fp, privalgname, foo, begin, prlen); + free(privalgname); + free(foo); + + if (strcmp(argv[4], "-")) + fclose(fp); + + exit(0); + } + + /* More algorithms here */ +#endif /* CRYPTO */ + + fprintf(stderr, "Unknown/unsupported algorithm [%s]\n", algname); + exit(-1); +} diff --git a/lib/libkeynote/keynote-sign.c b/lib/libkeynote/keynote-sign.c new file mode 100644 index 00000000000..467dcf5426a --- /dev/null +++ b/lib/libkeynote/keynote-sign.c @@ -0,0 +1,250 @@ +/* $OpenBSD: keynote-sign.c,v 1.1 1999/05/23 22:11:04 angelos 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 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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <ctype.h> + +#ifdef WIN32 +#include <ctype.h> +#include <io.h> +#else +#include <unistd.h> +#endif + +#include "assertion.h" +#include "signature.h" + +#define SIG_PRINT_OFFSET 12 +#define SIG_PRINT_LENGTH 50 + +extern struct assertion *asp; + +void +usage(void) +{ + fprintf(stderr, "Arguments:\n"); + fprintf(stderr, "\t[-v] <AlgorithmName> <AssertionFile> " + "<PrivateKeyFile>\n"); +} + +/* + * Print the specified number of spaces. + */ +void +print_space(FILE *fp, int n) +{ + while (n--) + fprintf(fp, " "); +} + +/* + * Output a signature, properly formatted. + */ +void +print_sig(FILE *fp, char *sig, int start, int length) +{ + int i, k; + + print_space(fp, start); + fprintf(fp, "\""); + + for (i = 0, k = 2; i < strlen(sig); i++, k++) + { + if (k == length) + { + if (i == strlen(sig)) + { + fprintf(fp, "\"\n"); + return; + } + + fprintf(fp, "\\\n"); + print_space(fp, start); + i--; + k = 0; + } + else + fprintf(fp, "%c", sig[i]); + } + + fprintf(fp, "\"\n"); +} + +#ifdef WIN32 +void +#else +int +#endif +main(int argc, char *argv[]) +{ + int begin = SIG_PRINT_OFFSET, prlen = SIG_PRINT_LENGTH; + char *buf, *buf2, *sig, *algname; + int fd, flg = 0, buflen; + struct stat sb; + + if ((argc != 4) && + (argc != 5)) + { + usage(); + exit(-1); + } + + if (argc == 5) + { + if (!strcmp("-v", argv[1])) + flg = 1; + else + { + fprintf(stderr, + "Invalid first argument [%s] or too many arguments\n", + argv[1]); + exit(-1); + } + } + + /* Fix algorithm name */ + if (argv[1 + flg][strlen(argv[1 + flg]) - 1] != ':') + { + fprintf(stderr, "Algorithm name [%s] should be terminated with a " + "colon, fixing.\n", argv[1 + flg]); + algname = (char *) calloc(strlen(argv[1 + flg]) + 2, sizeof(char)); + if (algname == (char *) NULL) + { + perror("calloc()"); + exit(-1); + } + + strcpy(algname, argv[1 + flg]); + algname[strlen(algname)] = ':'; + } + else + algname = argv[1 + flg]; + + /* Read assertion */ + fd = open(argv[2 + flg], O_RDONLY, 0); + if (fd < 0) + { + perror(argv[2 + flg]); + exit(-1); + } + + if (fstat(fd, &sb) < 0) + { + perror("fstat()"); + exit(-1); + } + + if (sb.st_size == 0) /* Paranoid */ + { + fprintf(stderr, "Error: zero-sized assertion-file.\n"); + exit(-1); + } + + buflen = sb.st_size + 1; + buf = (char *) calloc(buflen, sizeof(char)); + if (buf == (char *) NULL) + { + perror("calloc()"); + exit(-1); + } + + if (read(fd, buf, buflen - 1) < 0) + { + perror("read()"); + exit(-1); + } + + close(fd); + + /* Read private key file */ + fd = open(argv[3 + flg], O_RDONLY, 0); + if (fd < 0) + { + perror(argv[3 + flg]); + exit(-1); + } + + if (fstat(fd, &sb) < 0) + { + perror("fstat()"); + exit(-1); + } + + if (sb.st_size == 0) /* Paranoid */ + { + fprintf(stderr, "Illegal key-file size 0\n"); + exit(-1); + } + + buf2 = (char *) calloc(sb.st_size + 1, sizeof(char)); + if (buf2 == (char *) NULL) + { + perror("calloc()"); + exit(-1); + } + + if (read(fd, buf2, sb.st_size) < 0) + { + perror("read()"); + exit(-1); + } + + close(fd); + + sig = kn_sign_assertion(buf, buflen, buf2, algname, flg); + + /* Free buffers */ + free(buf); + free(buf2); + + if (sig == (char *) NULL) + { + switch (keynote_errno) + { + case ERROR_MEMORY: + fprintf(stderr, "Out of memory while creating signature.\n"); + break; + + case ERROR_SYNTAX: + fprintf(stderr, "Bad assertion or algorithm format, or " + "unsupported algorithm while creating signature.\n"); + break; + + default: + fprintf(stderr, "Unknown error while creating signature.\n"); + } + + exit(-1); + } + + /* Print signature string */ + print_sig(stdout, sig, begin, prlen); + + free(sig); /* Just a reminder that the result is malloc'ed */ + + exit(0); +} diff --git a/lib/libkeynote/keynote-sigver.c b/lib/libkeynote/keynote-sigver.c new file mode 100644 index 00000000000..c73af47982b --- /dev/null +++ b/lib/libkeynote/keynote-sigver.c @@ -0,0 +1,137 @@ +/* $OpenBSD: keynote-sigver.c,v 1.1 1999/05/23 22:11:06 angelos 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 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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <ctype.h> + +#ifdef WIN32 +#include <ctype.h> +#include <io.h> +#else +#include <unistd.h> +#endif + +#include "assertion.h" +#include "signature.h" + +void +usage(void) +{ + fprintf(stderr, "Arguments:\n"); + fprintf(stderr, "\t<AssertionFile>\n"); +} + +#ifdef WIN32 +void +#else +int +#endif +main(int argc, char *argv[]) +{ + struct stat sb; + int fd, i; + char *buf; + + if (argc != 2) + { + usage(); + exit(0); + } + + /* Open and read assertion file */ + fd = open(argv[1], O_RDONLY, 0); + if (fd < 0) + { + perror("open()"); + exit(-1); + } + + if (fstat(fd, &sb) < 0) + { + perror("fstat()"); + exit(-1); + } + + if (sb.st_size == 0) /* Paranoid */ + { + fprintf(stderr, "Illegal assertion-file size 0\n"); + exit(-1); + } + + buf = (char *) calloc(sb.st_size + 1, sizeof(char)); + if (buf == (char *) NULL) + { + perror("calloc()"); + exit(-1); + } + + if (read(fd, buf, sb.st_size) < 0) + { + perror("read()"); + exit(-1); + } + + close(fd); + + i = kn_verify_assertion(buf, sb.st_size); + if (i == -1) + { + switch (keynote_errno) + { + case ERROR_MEMORY: + fprintf(stderr, + "Out of memory while parsing the assertion.\n"); + break; + + case ERROR_SYNTAX: + fprintf(stderr, + "Syntax error while parsing the assertion.\n"); + break; + + default: + fprintf(stderr, + "Unknown error while parsing the assertion.\n"); + } + + exit(-1); + } + + free(buf); + + if (i == SIGRESULT_TRUE) + fprintf(stdout, "Signature verified.\n"); + else + { + if (keynote_errno != 0) + fprintf(stdout, "Signature could not be verified " + "(keynote_errno = %d).\n", keynote_errno); + else + fprintf(stdout, "Signature did not verify!\n"); + } + + exit(0); +} diff --git a/lib/libkeynote/keynote-ver.l b/lib/libkeynote/keynote-ver.l new file mode 100644 index 00000000000..9084d8e6ad5 --- /dev/null +++ b/lib/libkeynote/keynote-ver.l @@ -0,0 +1,273 @@ +%{ +/* $OpenBSD: keynote-ver.l,v 1.1 1999/05/23 22:11:04 angelos 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 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 <unistd.h> +#include <ctype.h> +#include <time.h> +#include <string.h> +#include "z.tab.h" +#include "keynote.h" + +static void mystrncpy(char *, char *, int); + +extern int kvparse(); +%} +string ([a-zA-Z])([a-zA-Z0-9_-])* +vstring [a-zA-Z0-9][a-zA-Z0-9_]* +litstring \"(([^\"\n])|(\\[\"\n\f\r\t.]))*\" +comment "#"[^\n]* +%s FIRSTPART MIDDLEPART SECONDPART KEYSTATE +%pointer +%option noyywrap yylineno never-interactive +%% + +<MIDDLEPART>"=" { + BEGIN(SECONDPART); + return EQ; + } +<FIRSTPART>{vstring} { kvlval.s.string = calloc(strlen(kvtext) + 1, + sizeof(char)); + if (kvlval.s.string == (char *) NULL) + return ERROR_MEMORY; + strcpy(kvlval.s.string, kvtext); + BEGIN(MIDDLEPART); + return VSTRING; + } +<KEYSTATE,SECONDPART>{string} { kvlval.s.string = calloc(strlen(kvtext) + 1, + sizeof(char)); + if (kvlval.s.string == (char *) NULL) + return ERROR_MEMORY; + strncpy(kvlval.s.string, kvtext, + strlen(kvtext)); + BEGIN(FIRSTPART); + return STRING; + } +<KEYSTATE,SECONDPART>{litstring} { kvlval.s.string = calloc(strlen(kvtext) - 1, + sizeof(char)); + if (kvlval.s.string == (char *) NULL) + return ERROR_MEMORY; + mystrncpy(kvlval.s.string, kvtext + 1, + strlen(kvtext) - 2); + BEGIN(FIRSTPART); + return STRING; + } +<FIRSTPART,KEYSTATE>{comment} ; +[ \t\n] ; +. return ERROR_SYNTAX; + +%% + +/* + * Return RESULT_TRUE if character is octal digit, RESULT_FALSE otherwise. + */ +static int +is_octal(char c) +{ + switch (c) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + return RESULT_TRUE; + + default: + return RESULT_FALSE; + } +} + +/* + * Return octal value (non-zero) if argument starts with such a + * representation, otherwise 0. + */ +static unsigned char +get_octal(char *s, int len, int *adv) +{ + unsigned char res = 0; + + if (*s == '0') + { + if (len > 0) + { + if (is_octal(*(s + 1))) + { + res = *(s + 1) - '0'; + *adv = 2; + + if (is_octal(*(s + 2)) && (len - 1 > 0)) + { + res = res * 8 + (*(s + 2) - '0'); + *adv = 3; + } + } + } + } + else + if (is_octal(*s) && (len - 1 > 0)) /* Non-zero leading */ + { + if (is_octal(*(s + 1)) && + is_octal(*(s + 2))) + { + *adv = 3; + res = (((*s) - '0') * 64) + + (((*(s + 1)) - '0') * 8) + + ((*(s + 2)) - '0'); + } + } + + return res; +} + +/* + * Copy at most len characters to string s1 from string s2, taking + * care of escaped characters in the process. String s1 is assumed + * to have enough space, and be zero'ed. + */ +static void +mystrncpy(char *s1, char *s2, int len) +{ + unsigned char c; + int advance; + + if (len == 0) + return; + + while (len-- > 0) + { + if (*s2 == '\\') + { + s2++; + + if (len-- <= 0) + break; + + if (*s2 == '\n') + { + while (isspace(*(++s2)) && (len-- > 0)) + ; + } + else + if ((c = get_octal(s2, len, &advance)) != 0) + { + len -= advance - 1; + s2 += advance; + *s1++ = c; + } + else + if (*s2 == 'n') /* Newline */ + { + *s1++ = '\n'; + s2++; + } + else + if (*s2 == 't') /* Tab */ + { + *s1++ = '\t'; + s2++; + } + else + if (*s2 == 'r') /* Linefeed */ + { + *s1++ = '\r'; + s2++; + } + else + if (*s2 == 'f') /* Formfeed */ + { + *s1++ = '\f'; + s2++; + } + else + if ((*s1++ = *s2++) == 0) + break; + + continue; + } + + if ((*s1++ = *s2++) == 0) + break; + } +} + +/* + * Parse a file that contains environment variable/value pairs. + * Return -1 on failure. + */ +int +read_environment(char *filename) +{ + YY_BUFFER_STATE kvfoo; + FILE *fp; + int i; + + fp = fopen(filename, "r"); + if (fp == (FILE *) NULL) + { + perror(filename); + return -1; + } + + kvfoo = kv_create_buffer(fp, YY_BUF_SIZE); + kv_switch_to_buffer(kvfoo); + BEGIN(FIRSTPART); + i = kvparse(); + kv_delete_buffer(kvfoo); + fclose(fp); + switch (i) + { + case 0: + return 0; + + case ERROR_MEMORY: + fprintf(stderr, + "Memory error while processing environment file <%s>\n", + filename); + return -1; + break; + + case 1: + case ERROR_SYNTAX: + default: + fprintf(stderr, "Syntax error in environment file <%s>, line %d\n", + filename, kvlineno); + return -1; + } +} + +/* + * Parse a key. + */ +void +parse_key(char *buf) +{ + YY_BUFFER_STATE key_state; + int err; + + key_state = kv_scan_string(buf); + BEGIN(KEYSTATE); + err = kvparse(); + kv_delete_buffer(key_state); + + if (err != 0) + if (keynote_errno == 0) + keynote_errno = ERROR_SYNTAX; +} diff --git a/lib/libkeynote/keynote-ver.y b/lib/libkeynote/keynote-ver.y new file mode 100644 index 00000000000..204a5b1dbea --- /dev/null +++ b/lib/libkeynote/keynote-ver.y @@ -0,0 +1,70 @@ +/* $OpenBSD: keynote-ver.y,v 1.1 1999/05/23 22:11:04 angelos 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 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. + */ +%union { + struct s { + char *string; + } s; +}; +%type <s.string> STRING VSTRING +%token STRING VSTRING EQ +%nonassoc EQ +%start program +%{ +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "keynote.h" + +void kverror(char *); + +extern int kvlex(); + +extern int sessid; +%} +%% + +program: /* Nothing */ + | expr + | STRING { if (kn_add_authorizer(sessid, $1) != 0) + return keynote_errno; + free($1); + } + +expr: VSTRING EQ STRING { int i = kn_add_action(sessid, $1, $3, 0); + + if (i != 0) + return i; + free($1); + free($3); + } + | expr VSTRING EQ STRING { int i = kn_add_action(sessid, $2, $4, 0); + + if (i != 0) + return i; + free($2); + free($4); + } +%% +void +kverror(char *s) +{} diff --git a/lib/libkeynote/keynote-verify.c b/lib/libkeynote/keynote-verify.c new file mode 100644 index 00000000000..fde510c8667 --- /dev/null +++ b/lib/libkeynote/keynote-verify.c @@ -0,0 +1,447 @@ +/* $OpenBSD: keynote-verify.c,v 1.1 1999/05/23 22:11:04 angelos 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 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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <ctype.h> + +#ifdef WIN32 +#include <io.h> +#include "getopt.h" +#else +#include <unistd.h> +#ifdef NEED_GETOPT +#include "getopt.h" +#endif +#endif + +#include "keynote.h" + +extern int read_environment(char *); +extern void parse_key(char *); + +int sessid; + +void +usage(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"); +} + +#ifdef WIN32 +void +#else +int +#endif +main(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) + { + usage(); + 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("open()"); + 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': + usage(); + 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 = index(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); + } + + bcopy(retv, foov, 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 */ + bcopy(optarg, retv[numret], 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("open()"); + 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: + usage(); + 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("open()"); + 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); +} diff --git a/lib/libkeynote/keynote.h b/lib/libkeynote/keynote.h new file mode 100644 index 00000000000..2ae97f85919 --- /dev/null +++ b/lib/libkeynote/keynote.h @@ -0,0 +1,195 @@ +/* $OpenBSD: keynote.h,v 1.1 1999/05/23 22:11:04 angelos 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 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. + */ + +#ifndef _KEYNOTE_H_ +#define _KEYNOTE_H_ + +#include <regex.h> + +#ifdef CRYPTO +#include "ssl/crypto.h" +#include "ssl/dsa.h" +#include "ssl/rsa.h" +#include "ssl/sha.h" +#include "ssl/md5.h" +#include "ssl/err.h" +#include "ssl/rand.h" +#include "ssl/x509.h" +#endif /* CRYPTO */ + +#ifdef WIN32 +#define u_int unsigned int +#define u_char unsigned char +#define strcasecmp stricmp +#define strncasecmp strnicmp +#define open _open +#define read _read +#define close _close +#endif + +#if defined(__OpenBSD__) || defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__) +#define KEYNOTERNDFILENAME "/dev/urandom" +#endif /* __OpenBSD__ || linux || __FreeBSD__ || __NetBSD__ */ + +struct environment +{ + char *env_name; + char *env_value; + int env_flags; + regex_t env_regex; + struct environment *env_next; +}; + +struct keynote_deckey +{ + int dec_algorithm; + void *dec_key; +}; + +struct keynote_binary +{ + int bn_len; + char *bn_key; +}; + +#define SIG_DSA_SHA1_HEX "sig-dsa-sha1-hex:" +#define SIG_DSA_SHA1_HEX_LEN strlen(SIG_DSA_SHA1_HEX) +#define SIG_DSA_SHA1_BASE64 "sig-dsa-sha1-base64:" +#define SIG_DSA_SHA1_BASE64_LEN strlen(SIG_DSA_SHA1_BASE64) +#define SIG_RSA_SHA1_PKCS1_HEX "sig-rsa-sha1-hex:" +#define SIG_RSA_SHA1_PKCS1_HEX_LEN strlen(SIG_RSA_SHA1_PKCS1_HEX) +#define SIG_RSA_SHA1_PKCS1_BASE64 "sig-rsa-sha1-base64:" +#define SIG_RSA_SHA1_PKCS1_BASE64_LEN strlen(SIG_RSA_SHA1_PKCS1_BASE64) +#define SIG_RSA_MD5_PKCS1_HEX "sig-rsa-md5-hex:" +#define SIG_RSA_MD5_PKCS1_HEX_LEN strlen(SIG_RSA_MD5_PKCS1_HEX) +#define SIG_RSA_MD5_PKCS1_BASE64 "sig-rsa-md5-base64:" +#define SIG_RSA_MD5_PKCS1_BASE64_LEN strlen(SIG_RSA_MD5_PKCS1_BASE64) +#define SIG_ELGAMAL_SHA1_HEX "sig-elgamal-sha1-hex:" +#define SIG_ELGAMAL_SHA1_HEX_LEN strlen(SIG_ELGAMAL_SHA1_HEX) +#define SIG_ELGAMAL_SHA1_BASE64 "sig-elgamal-sha1-base64:" +#define SIG_ELGAMAL_SHA1_BASE64_LEN strlen(SIG_ELGAMAL_SHA1_BASE64) +#define SIG_PGP_NATIVE "sig-pgp:" +#define SIG_PGP_NATIVE_LEN strlen(SIG_PGP_NATIVE) +#define SIG_X509_SHA1_BASE64 "sig-x509-sha1-base64:" +#define SIG_X509_SHA1_BASE64_LEN strlen(SIG_X509_SHA1_BASE64) +#define SIG_X509_SHA1_HEX "sig-x509-sha1-hex:" +#define SIG_X509_SHA1_HEX_LEN strlen(SIG_X509_SHA1_HEX) + +#define SIGRESULT_UNTOUCHED 0 +#define SIGRESULT_FALSE 1 +#define SIGRESULT_TRUE 2 + +#define ENVIRONMENT_FLAG_FUNC 0x0001 /* This is a callback function */ +#define ENVIRONMENT_FLAG_REGEX 0x0002 /* Regular expression for name */ + +#define ASSERT_FLAG_LOCAL 0x0001 /* + * Trusted assertion -- means + * signature is not verified, and + * authorizer field can + * include symbolic names. + */ +#define ASSERT_FLAG_SIGGEN 0x0002 /* + * Be a bit more lax with the + * contents of the Signature: + * field; to be used in + * assertion signing only. + */ +#define ASSERT_FLAG_SIGVER 0x0004 /* + * To be used in signature verification + * only. + */ +#define RESULT_FALSE 0 +#define RESULT_TRUE 1 + +#define KEYNOTE_CALLBACK_INITIALIZE "_KEYNOTE_CALLBACK_INITIALIZE" +#define KEYNOTE_CALLBACK_CLEANUP "_KEYNOTE_CALLBACK_CLEANUP" + +#define KEYNOTE_VERSION_STRING "2" + +#define ERROR_MEMORY -1 +#define ERROR_SYNTAX -2 +#define ERROR_NOTFOUND -3 +#define ERROR_SIGN_FAILURE -4 + +#define KEYNOTE_ALGORITHM_UNSPEC -1 +#define KEYNOTE_ALGORITHM_NONE 0 +#define KEYNOTE_ALGORITHM_DSA 1 +#define KEYNOTE_ALGORITHM_ELGAMAL 2 +#define KEYNOTE_ALGORITHM_PGP 3 +#define KEYNOTE_ALGORITHM_BINARY 4 +#define KEYNOTE_ALGORITHM_X509 5 +#define KEYNOTE_ALGORITHM_RSA 6 + +#define KEYNOTE_ERROR_ANY 0 +#define KEYNOTE_ERROR_SYNTAX 1 +#define KEYNOTE_ERROR_MEMORY 2 +#define KEYNOTE_ERROR_SIGNATURE 3 + +#define ENCODING_NONE 0 +#define ENCODING_HEX 1 +#define ENCODING_BASE64 2 +#define ENCODING_NATIVE 3 /* For things like PGP */ + +#define INTERNAL_ENC_NONE 0 +#define INTERNAL_ENC_PKCS1 1 +#define INTERNAL_ENC_ASN1 2 +#define INTERNAL_ENC_NATIVE 3 /* For things like PGP */ + +#define KEYNOTE_PUBLIC_KEY 0 +#define KEYNOTE_PRIVATE_KEY 1 + +extern int keynote_errno; + +/* Session API */ +int kn_init(void); +int kn_add_assertion(int, char *, int, int); +int kn_remove_assertion(int, int); +int kn_add_action(int, char *, char *, int); +int kn_remove_action(int, char *); +int kn_add_authorizer(int, char *); +int kn_remove_authorizer(int, char *); +int kn_do_query(int, char **, int); +int kn_get_failed(int, int, int); +int kn_close(int); + +/* Simple API */ +int kn_query(struct environment *, char **, int, char **, int *, int, + char **, int *, int, char **, int); + +/* Aux. routines */ +char **kn_read_asserts(char *, int, int *); + +/* ASCII-encoding API */ +int kn_encode_base64(unsigned char const *, unsigned int, char *, + unsigned int); +int kn_decode_base64(char const *, unsigned char *, unsigned int); +int kn_encode_hex(unsigned char *, char **, int); +int kn_decode_hex(char *, char **); + +/* Key-encoding API */ +int kn_decode_key(struct keynote_deckey *, char *, int); +char *kn_encode_key(struct keynote_deckey *, int, int, int); + +/* Crypto API */ +char *kn_sign_assertion(char *, int, char *, char *, int); +int kn_verify_assertion(char *, int); +#endif /* _KEYNOTE_H_ */ diff --git a/lib/libkeynote/keynote.l b/lib/libkeynote/keynote.l new file mode 100644 index 00000000000..d1e4eb6235d --- /dev/null +++ b/lib/libkeynote/keynote.l @@ -0,0 +1,749 @@ +%{ +/* $OpenBSD: keynote.l,v 1.1 1999/05/23 22:11:05 angelos 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 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 <unistd.h> +#include <ctype.h> +#ifndef PILOT +#include <time.h> +#endif /* PILOT */ +#include <string.h> +#include "k.tab.h" +#include "environment.h" +#include "assertion.h" +#include "signature.h" + +static void mystrncpy(char *, char *, int); +extern int knparse(); + +struct lex_list +{ + int lex_type; + void *lex_s; +}; + +static struct lex_list *keynote_lex_list = (struct lex_list *) NULL; +static int keynote_max_lex_list = 32; +static int keynote_lex_counter = 0; +static int first_tok = 0; + +int keynote_used_variable = 0; +int keynote_exceptionflag = 0; +int keynote_donteval = 0; + +char *keynote_privkey = (char *) NULL; + +struct environment *keynote_init_list = (struct environment *) NULL; +struct environment *keynote_temp_list = (struct environment *) NULL; +struct keylist *keynote_keypred_keylist = (struct keylist *) NULL; +%} +digit [0-9] +specnumber [1-9][0-9]* +number ({digit}+) +flt ({digit}+"."{digit}+) +vstring [a-zA-Z_][a-zA-Z0-9_]* +white ((\\[\n][[:space:]]*)) +litstring \"(([^\"\n])|(\\[\"\n\f\r\t.]))*\" +variable {vstring} +comment "#"[^\n]* +%s ACTIONSTRING LOCALINIT KEYPREDICATE SIGNERINIT KEYNOTEVERSION +%pointer +%option noyywrap never-interactive yylineno +%% + +%{ + /* + * Return a preset token, so we can have more than one grammars + * in yacc. + */ + extern int first_tok; + + if (first_tok) + { + int holdtok = first_tok; + + first_tok = 0; + return holdtok; + } +%} + +<KEYPREDICATE>{specnumber}"-of" { + knlval.intval = atoi(kntext); + return KOF; + } +<ACTIONSTRING,KEYPREDICATE>"(" return OPENPAREN; +<ACTIONSTRING,KEYPREDICATE>")" return CLOSEPAREN; +<ACTIONSTRING,KEYPREDICATE>"&&" return AND; +<ACTIONSTRING,KEYPREDICATE>"||" return OR; +<ACTIONSTRING>"+" return PLUS; +<ACTIONSTRING>"->" return HINT; +<ACTIONSTRING>"{" return OPENBLOCK; +<ACTIONSTRING>"}" return CLOSEBLOCK; +<ACTIONSTRING>";" return SEMICOLON; +<ACTIONSTRING>"!" return NOT; +<ACTIONSTRING>"~=" return REGEXP; +<ACTIONSTRING>"==" return EQ; +<ACTIONSTRING>"!=" return NE; +<ACTIONSTRING>"<" return LT; +<ACTIONSTRING>">" return GT; +<ACTIONSTRING>"<=" return LE; +<ACTIONSTRING>">=" return GE; +<ACTIONSTRING>"-" return MINUS; +<ACTIONSTRING>"*" return MULT; +<ACTIONSTRING>"/" return DIV; +<ACTIONSTRING>"%" return MOD; +<ACTIONSTRING>"^" return EXP; +"." return DOTT; +<ACTIONSTRING>"true" return TRUE; +<ACTIONSTRING>"false" return FALSE; +{comment} /* eat up comments */ +<LOCALINIT>"=" return EQQ; +<KEYPREDICATE>"," return COMMA; +<ACTIONSTRING,KEYPREDICATE,SIGNERINIT,LOCALINIT>{variable} { + if (keynote_exceptionflag || + keynote_donteval) + { + knlval.string = (char *) NULL; + return VARIABLE; + } + + knlval.string = calloc(strlen(kntext) + + 1, + sizeof(char)); + if (knlval.string == (char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + strcpy(knlval.string, kntext); + if (keynote_lex_add(knlval.string, + LEXTYPE_CHAR) == + -1) + return -1; + return VARIABLE; + } +"$" return DEREF; +<ACTIONSTRING>"@" return OPENNUM; +<ACTIONSTRING>"&" return OPENFLT; +<ACTIONSTRING>{flt} { + knlval.doubval = atof(kntext); + return FLOAT; + } +<KEYNOTEVERSION>{number} { + if (keynote_exceptionflag || + keynote_donteval) + { + knlval.string = (char *) NULL; + return STRING; + } + + knlval.string = calloc(strlen(kntext) + 1, + sizeof(char)); + if (knlval.string == (char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + strcpy(knlval.string, kntext); + if (keynote_lex_add(knlval.string, + LEXTYPE_CHAR) == -1) + return -1; + return STRING; + } +<ACTIONSTRING>{number} { + knlval.intval = atoi(kntext); + return NUM; + } +{litstring} { + if (keynote_exceptionflag || + keynote_donteval) + { + knlval.string = (char *) NULL; + return STRING; + } + + knlval.string = calloc(strlen(kntext) - 1, + sizeof(char)); + if (knlval.string == (char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + mystrncpy(knlval.string, kntext + 1, + strlen(kntext) - 2); + + if (keynote_lex_add(knlval.string, + LEXTYPE_CHAR) == -1) + return -1; + return STRING; + } +[ \t\n] +. { keynote_errno = ERROR_SYNTAX; + return -1; + } +%% + +/* + * Zap everything. + */ +static void +keynote_lex_zap(void) +{ + int i; + + if (keynote_lex_counter == 0) + return; + + for (i = 0; i < keynote_max_lex_list; i++) + if (keynote_lex_list[i].lex_s != (void *) NULL) + { + switch (keynote_lex_list[i].lex_type) + { + case LEXTYPE_CHAR: + free(keynote_lex_list[i].lex_s); + break; + } + + keynote_lex_counter--; + keynote_lex_list[i].lex_s = (void *) NULL; + keynote_lex_list[i].lex_type = 0; + } +} + +/* + * Initialize. + */ +static int +keynote_lex_init(void) +{ + if (keynote_lex_list != (struct lex_list *) NULL) + memset(keynote_lex_list, 0, + keynote_max_lex_list * sizeof(struct lex_list)); + else + { + keynote_lex_list = (struct lex_list *) calloc(keynote_max_lex_list, + sizeof(struct lex_list)); + if (keynote_lex_list == (struct lex_list *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + } + + return RESULT_TRUE; +} + +/* + * Add the string in a list of allocated but "dangling" memory references. + * If out of memory, free the string and return -1 (and set keynote_errno). + */ +int +keynote_lex_add(void *s, int type) +{ + struct lex_list *p; + int i; + + if (s == (void *) NULL) + return RESULT_TRUE; + + for (i = 0; i < keynote_max_lex_list; i++) + if (keynote_lex_list[i].lex_s == (void *) NULL) + { + keynote_lex_list[i].lex_s = (void *) s; + keynote_lex_list[i].lex_type = type; + keynote_lex_counter++; + return RESULT_TRUE; + } + + /* Not enough space, increase the size of the array */ + keynote_max_lex_list *= 2; + + p = (struct lex_list *) realloc(keynote_lex_list, + keynote_max_lex_list * + sizeof(struct lex_list)); + if (p == (struct lex_list *) NULL) + { + switch (type) + { + case LEXTYPE_CHAR: + free(s); + break; + } + + keynote_errno = ERROR_MEMORY; + return -1; + } + + if (p != keynote_lex_list) + { + free(keynote_lex_list); + keynote_lex_list = p; + } + + keynote_lex_list[i].lex_s = s; + keynote_lex_list[i++].lex_type = type; + keynote_lex_counter++; + + /* Zero out the rest */ + memset(&(keynote_lex_list[i]), 0, + (keynote_max_lex_list - i) * sizeof(struct lex_list)); + + return RESULT_TRUE; +} + +/* + * Remove string. + */ +void +keynote_lex_remove(void *s) +{ + int i; + + for (i = 0; i < keynote_max_lex_list; i++) + if (keynote_lex_list[i].lex_s == s) + { + memset(&(keynote_lex_list[i]), 0, sizeof(struct lex_list)); + keynote_lex_counter--; + return; + } +} + +/* + * Return RESULT_TRUE if character is octal digit, RESULT_FALSE otherwise. + */ +static int +is_octal(char c) +{ + switch (c) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + return RESULT_TRUE; + + default: + return RESULT_FALSE; + } +} + +/* + * Return octal value (non-zero) if argument starts with such a + * representation, otherwise 0. + */ +static unsigned char +get_octal(char *s, int len, int *adv) +{ + unsigned char res = 0; + + if (*s == '0') + { + if (len > 0) + { + if (is_octal(*(s + 1))) + { + res = *(s + 1) - '0'; + *adv = 2; + + if (is_octal(*(s + 2)) && (len - 1 > 0)) + { + res = res * 8 + (*(s + 2) - '0'); + *adv = 3; + } + } + } + } + else + if (is_octal(*s) && (len - 1 > 0)) /* Non-zero leading */ + { + if (is_octal(*(s + 1)) && + is_octal(*(s + 2))) + { + *adv = 3; + res = (((*s) - '0') * 64) + + (((*(s + 1)) - '0') * 8) + + ((*(s + 2)) - '0'); + } + } + + return res; +} + +/* + * Copy at most len characters to string s1 from string s2, taking + * care of escaped characters in the process. String s1 is assumed + * to have enough space, and be zero'ed. + */ +static void +mystrncpy(char *s1, char *s2, int len) +{ + unsigned char c; + int advance; + + if (len == 0) + return; + + while (len-- > 0) + { + if (*s2 == '\\') + { + s2++; + + if (len-- <= 0) + break; + + if (*s2 == '\n') + { + while (isspace(*(++s2)) && (len-- > 0)) + ; + } + else + if ((c = get_octal(s2, len, &advance)) != 0) + { + len -= advance - 1; + s2 += advance; + *s1++ = c; + } + else + if (*s2 == 'n') /* Newline */ + { + *s1++ = '\n'; + s2++; + } + else + if (*s2 == 't') /* Tab */ + { + *s1++ = '\t'; + s2++; + } + else + if (*s2 == 'r') /* Linefeed */ + { + *s1++ = '\r'; + s2++; + } + else + if (*s2 == 'f') /* Formfeed */ + { + *s1++ = '\f'; + s2++; + } + else + if ((*s1++ = *s2++) == 0) + break; + + continue; + } + + if ((*s1++ = *s2++) == 0) + break; + } +} + +/* + * Evaluate an assertion, with as->as_result holding the result. + * Return RESULT_TRUE if all ok. Also return the result. + */ +int +keynote_evaluate_assertion(struct assertion *as) +{ + YY_BUFFER_STATE keynote_bs; + + /* Non-existant Conditions field means highest return value */ + if (as->as_conditions_s == (char *) NULL) + { + as->as_result = keynote_current_session->ks_values_num - 1; + return RESULT_TRUE; + } + + if (keynote_lex_init() != RESULT_TRUE) + return -1; + + keynote_used_variable = 0; + keynote_init_list = as->as_env; /* Setup the local-init var list */ + + keynote_bs = kn_scan_bytes(as->as_conditions_s, + as->as_conditions_e - as->as_conditions_s); + BEGIN(ACTIONSTRING); /* We're doing conditions-string parsing */ + first_tok = ACTSTR; + as->as_result = 0; + keynote_returnvalue = 0; + + switch (knparse()) + { + case 1: /* Fall through */ + keynote_errno = ERROR_SYNTAX; + case -1: + as->as_result = 0; + break; + + case 0: + as->as_result = keynote_returnvalue; + break; + } + + keynote_env_cleanup(&keynote_temp_list, 1); + keynote_lex_zap(); + kn_delete_buffer(keynote_bs); /* Free the space */ + + keynote_used_variable = 0; + keynote_returnvalue = 0; + keynote_temp_list = (struct environment *) NULL; + keynote_init_list = (struct environment *) NULL; + + if (keynote_errno != 0) + return -1; + else + return RESULT_TRUE; +} + +/* + * Parse/evaluate a key predicate field. + * Store keys in key predicate as keylist in as->as_keylist, if third + * argument is true. + */ +int +keynote_parse_keypred(struct assertion *as, int record) +{ + YY_BUFFER_STATE keypred_state; + int p = 0, err; + + if (as->as_keypred_s == (char *) NULL) + return keynote_current_session->ks_values_num - 1; + + if (keynote_lex_init() != RESULT_TRUE) + return -1; + + keynote_used_variable = 0; + keynote_returnvalue = 0; + keynote_justrecord = record; /* Just want the list of keys in predicate */ + keynote_init_list = as->as_env; + + keypred_state = kn_scan_bytes(as->as_keypred_s, + as->as_keypred_e - as->as_keypred_s); + BEGIN(KEYPREDICATE); + first_tok = KEYPRE; + + err = knparse(); + if (err != 0) + if (keynote_errno == 0) + keynote_errno = ERROR_SYNTAX; + + kn_delete_buffer(keypred_state); /* Free memory */ + keynote_lex_zap(); + keynote_cleanup_kth(); + + keynote_init_list = (struct environment *) NULL; + keynote_justrecord = 0; + p = keynote_returnvalue; + keynote_returnvalue = 0; + + if (record) + { + if (keynote_errno != 0) + { + keynote_keylist_free(keynote_keypred_keylist); + keynote_keypred_keylist = (struct keylist *) NULL; + return -1; + } + else + { + /* Mark for re-processing if/when environment changes */ + if (keynote_used_variable) + { + keynote_used_variable = 0; + as->as_internalflags |= ASSERT_IFLAG_WEIRDLICS; + } + + as->as_keylist = keynote_keypred_keylist; + keynote_keypred_keylist = (struct keylist *) NULL; + return RESULT_TRUE; + } + } + else + return p; +} + +/* Evaluate an authorizer or signature field. Return RESULT_TRUE on success. + * Store key in as->as_authorizer. Second argument is set only for Authorizer + * field parsing. + */ +int +keynote_evaluate_authorizer(struct assertion *as, int flag) +{ + YY_BUFFER_STATE authorizer_state; + int err; + + if (keynote_lex_init() != RESULT_TRUE) + return -1; + + keynote_init_list = as->as_env; + keynote_justrecord = 1; + keynote_used_variable = 0; + + if ((flag) && (as->as_authorizer != (void *) NULL)) + { + keynote_free_key(as->as_authorizer, as->as_signeralgorithm); + as->as_authorizer = (void *) NULL; + } + + if (flag) + authorizer_state = kn_scan_bytes(as->as_authorizer_string_s, + as->as_authorizer_string_e - + as->as_authorizer_string_s); + else + authorizer_state = kn_scan_bytes(as->as_signature_string_s, + as->as_signature_string_e - + as->as_signature_string_s); + + BEGIN(SIGNERINIT); + if (flag) + first_tok = SIGNERKEY; + else + first_tok = SIGNATUREENTRY; + + err = knparse(); + if ((err != 0) && (keynote_errno == 0)) + keynote_errno = ERROR_SYNTAX; + + kn_delete_buffer(authorizer_state); /* Free memory */ + keynote_lex_zap(); + + keynote_justrecord = 0; + keynote_init_list = (struct environment *) NULL; + keynote_returnvalue = 0; + + if (keynote_keypred_keylist != (struct keylist *) NULL) + { + if (flag) + { + if (keynote_used_variable) + as->as_internalflags |= ASSERT_IFLAG_WEIRDAUTH; + + as->as_authorizer = keynote_keypred_keylist->key_key; + as->as_signeralgorithm = keynote_keypred_keylist->key_alg; + } + else + { + if (keynote_used_variable) + as->as_internalflags |= ASSERT_IFLAG_WEIRDSIG; + + as->as_signature = keynote_keypred_keylist->key_key; + } + + keynote_keypred_keylist->key_key = (char *) NULL; + keynote_keylist_free(keynote_keypred_keylist); + keynote_keypred_keylist = (struct keylist *) NULL; + } + + keynote_used_variable = 0; + + if (keynote_errno != 0) + return -1; + else + return RESULT_TRUE; +} + +/* + * Parse a private key. + */ +char * +keynote_get_private_key(char *buf) +{ + YY_BUFFER_STATE pkey; + char *s; + int err; + + if (keynote_lex_init() != RESULT_TRUE) + return (char *) NULL; + + keynote_privkey = (char *) NULL; + pkey = kn_scan_bytes(buf, strlen(buf)); + first_tok = PRIVATEKEY; + err = knparse(); + kn_delete_buffer(pkey); + keynote_lex_zap(); + + if (err != 0) + { + if (keynote_privkey != (char *) NULL) + { + free(keynote_privkey); + keynote_privkey = (char *) NULL; + } + + if (keynote_errno == 0) + keynote_errno = ERROR_SYNTAX; + + return (char *) NULL; + } + + s = keynote_privkey; + keynote_privkey = (char *) NULL; + return s; +} + +/* + * Parse Local-Constants and KeyNote-Version fields. + */ +struct environment * +keynote_get_envlist(char *buf, char *bufend, int whichfield) +{ + struct environment *en = (struct environment *) NULL; + YY_BUFFER_STATE localinit_state; + int err; + + if (keynote_lex_init() != RESULT_TRUE) + return (struct environment *) NULL; + + localinit_state = kn_scan_bytes(buf, bufend - buf); + if (whichfield == 0) + { + BEGIN(LOCALINIT); /* We're doing Local-Constants parsing */ + first_tok = LOCINI; + } + else + { + BEGIN(KEYNOTEVERSION); /* KeyNote-Version parsing */ + first_tok = KNVERSION; + } + + err = knparse(); + if (err != 0) + if (keynote_errno == 0) + keynote_errno = ERROR_SYNTAX; + + kn_delete_buffer(localinit_state); + keynote_lex_zap(); + + if (!whichfield) + { + if (keynote_errno != 0) + keynote_env_cleanup(&keynote_init_list, 1); + else + en = keynote_init_list; + + keynote_init_list = (struct environment *) NULL; + } + + return en; +} diff --git a/lib/libkeynote/keynote.y b/lib/libkeynote/keynote.y new file mode 100644 index 00000000000..ca6c3fbf066 --- /dev/null +++ b/lib/libkeynote/keynote.y @@ -0,0 +1,899 @@ +/* $OpenBSD: keynote.y,v 1.1 1999/05/23 22:11:05 angelos 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 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. + */ +%union { + char *string; + double doubval; + int intval; + int bool; +}; +%type <bool> stringexp numexp expr floatexp +%type <intval> NUM KOF numex afterhint notemptyprog +%type <intval> notemptykeypredicate prog key keyexp keylist +%type <doubval> FLOAT floatex +%type <string> STRING VARIABLE str strnotconcat +%token TRUE FALSE NUM FLOAT STRING VARIABLE +%token OPENPAREN CLOSEPAREN EQQ COMMA ACTSTR LOCINI KOF KEYPRE KNVERSION +%token DOTT SIGNERKEY HINT OPENBLOCK CLOSEBLOCK SIGNATUREENTRY PRIVATEKEY +%token SEMICOLON TRUE FALSE +%nonassoc EQ NE LT GT LE GE REGEXP +%left OR +%left AND +%right NOT +%left PLUS MINUS DOTT +%left MULT DIV MOD +%left EXP +%nonassoc UNARYMINUS DEREF OPENNUM OPENFLT +%start grammarswitch +%{ +#include <sys/types.h> +#include <stdio.h> +#include <math.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <regex.h> +#include "environment.h" +#include "signature.h" + +static int *keynote_kth_array = (int *) NULL; +static int keylistcount = 0; + +static int resolve_assertion(char *); +static int keynote_init_kth(void); +static int isfloatstring(char *); +static int checkexception(int); +static char *my_lookup(char *); +static int intpow(int, int); +static int get_kth(int); + +extern int keynote_in_action_authorizers(void *, int); +extern int knlex(); + +extern int keynote_exceptionflag, keynote_donteval; +extern char **keynote_values, *keynote_privkey; +extern struct keylist *keynote_keypred_keylist; +extern struct environment *keynote_temp_list; +extern struct environment *keynote_init_list; +extern int knlineno, keynote_used_variable; + +void knerror(char *); +%} +%% + +grammarswitch: LOCINI { keynote_exceptionflag = keynote_donteval = 0; } + localinit + | ACTSTR { keynote_exceptionflag = keynote_donteval = 0; } program + | KEYPRE { keynote_exceptionflag = keynote_donteval = 0; } + keypredicate + | SIGNERKEY { keynote_exceptionflag = keynote_donteval = 0; } key + | SIGNATUREENTRY { keynote_exceptionflag = keynote_donteval = 0; } + key + | KNVERSION { keynote_exceptionflag = keynote_donteval = 0; } + STRING { keynote_lex_remove($3); + if (strcmp($3, KEYNOTE_VERSION_STRING)) + keynote_errno = ERROR_SYNTAX; + free($3); + } + | PRIVATEKEY { keynote_exceptionflag = keynote_donteval = 0; } + STRING { keynote_lex_remove($3); + keynote_privkey = $3; + } + +keypredicate: /* Nothing */ { keynote_returnvalue = 0; + return 0; + } + | notemptykeypredicate { keynote_returnvalue = $1; + return 0; + } + +notemptykeypredicate: key { $$ = $1; } + | keyexp { $$ = $1; } + +keyexp: notemptykeypredicate AND { if (($1 == 0) && !keynote_justrecord) + keynote_donteval = 1; + } notemptykeypredicate + { if ($1 > $4) + $$ = $4; + else + $$ = $1; + keynote_donteval = 0; + } /* Min */ + | notemptykeypredicate OR { if (($1 == (keynote_current_session->ks_values_num - 1)) && !keynote_justrecord) + keynote_donteval = 1; + } notemptykeypredicate + { if ($1 >= $4) + $$ = $1; + else + $$ = $4; + keynote_donteval = 0; + } /* Max */ + | OPENPAREN keyexp CLOSEPAREN { $$ = $2; } + | KOF { keylistcount = 0; } OPENPAREN { + if (!keynote_justrecord && !keynote_donteval) + if (keynote_init_kth() == -1) + return -1; + } keylist CLOSEPAREN + { + if (keylistcount < $1) + { + keynote_errno = ERROR_SYNTAX; + return -1; + } + + if (!keynote_justrecord && !keynote_donteval) + $$ = get_kth($1); + else + $$ = 0; + } /* K-th */ + +keylist: key + { /* Don't do anything if we're just recording */ + if (!keynote_justrecord && !keynote_donteval) + if (($1 < keynote_current_session->ks_values_num) && ($1 >= 0)) + keynote_kth_array[$1]++; + + keylistcount++; + } + | key COMMA keylist + { /* Don't do anything if we're just recording */ + if (!keynote_justrecord && !keynote_donteval) + if (($1 < keynote_current_session->ks_values_num) && ($1 >= 0)) + keynote_kth_array[$1]++; + + keylistcount++; + } + +key: str { + if (keynote_donteval) + $$ = 0; + else + { + keynote_lex_remove($1); + if (keynote_justrecord) + { + if (keynote_keylist_add(&keynote_keypred_keylist, + $1) == -1) + { + free($1); + return -1; + } + + $$ = 0; + } + else + switch (keynote_in_action_authorizers($1, KEYNOTE_ALGORITHM_UNSPEC)) + { + case -1: + free($1); + return -1; + + case RESULT_TRUE: + free($1); + $$ = keynote_current_session->ks_values_num - + 1; + break; + + default: + $$ = resolve_assertion($1); + free($1); + break; + } + } + } + +localinit: /* Nothing */ + | localconstants + +localconstants: VARIABLE EQQ STRING + { + int i; + + keynote_lex_remove($1); + keynote_lex_remove($3); + + /* + * Variable names starting with underscores are illegal here. + */ + if ($1[0] == '_') + { + free($1); + free($3); + keynote_errno = ERROR_SYNTAX; + return -1; + } + + /* If the identifier already exists, report error. */ + if (keynote_env_lookup($1, &keynote_init_list, 1) != (char *) NULL) + { + free($1); + free($3); + keynote_errno = ERROR_SYNTAX; + return -1; + } + + i = keynote_env_add($1, $3, &keynote_init_list, 1, 0); + free($1); + free($3); + + if (i != RESULT_TRUE) + return -1; + } + | VARIABLE EQQ STRING + { + int i; + + keynote_lex_remove($1); + keynote_lex_remove($3); + + /* + * Variable names starting with underscores are illegal here. + */ + if ($1[0] == '_') + { + free($1); + free($3); + keynote_errno = ERROR_SYNTAX; + return -1; + } + + /* If the identifier already exists, report error. */ + if (keynote_env_lookup($1, &keynote_init_list, 1) != (char *) NULL) + { + free($1); + free($3); + keynote_errno = ERROR_SYNTAX; + return -1; + } + + i = keynote_env_add($1, $3, &keynote_init_list, 1, 0); + free($1); + free($3); + + if (i != RESULT_TRUE) + return -1; + } localconstants + +program: prog { + keynote_returnvalue = $1; + return 0; + } + +prog: /* Nada */ { $$ = 0; } + | notemptyprog { + /* + * Cleanup envlist of additions such as + * regexp results + */ + keynote_env_cleanup(&keynote_temp_list, 1); + } SEMICOLON prog + { + if ($1 > $4) + $$ = $1; + else + $$ = $4; + } + +notemptyprog: expr HINT afterhint + { + if (checkexception($1)) + $$ = $3; + else + $$ = 0; + } + | expr + { + if (checkexception($1)) + $$ = keynote_current_session->ks_values_num - 1; + else + $$ = 0; + } + +afterhint: str { if (keynote_exceptionflag || keynote_donteval) + $$ = 0; + else + { + keynote_lex_remove($1); + + $$ = keynote_retindex($1); + if ($$ == -1) /* Invalid return value */ + $$ = 0; + + free($1); + } + } + | OPENBLOCK prog CLOSEBLOCK { $$ = $2; } + + +expr: OPENPAREN expr CLOSEPAREN { $$ = $2; } + | expr AND { if ($1 == 0) + keynote_donteval = 1; + } expr { $$ = ($1 && $4); + keynote_donteval = 0; + } + | expr OR { if ($1) + keynote_donteval = 1; + } expr { $$ = ($1 || $4); + keynote_donteval = 0; + } + | NOT expr { $$ = !($2); } + | numexp { $$ = $1; } + | floatexp { $$ = $1; } + | stringexp { $$ = $1; } + | TRUE { $$ = 1; } + | FALSE { $$ = 0; } + +numexp: numex LT numex { $$ = $1 < $3; } + | numex GT numex { $$ = $1 > $3; } + | numex EQ numex { $$ = $1 == $3; } + | numex LE numex { $$ = $1 <= $3; } + | numex GE numex { $$ = $1 >= $3; } + | numex NE numex { $$ = $1 != $3; } + +floatexp: floatex LT floatex { $$ = $1 < $3; } + | floatex GT floatex { $$ = $1 > $3; } + | floatex LE floatex { $$ = $1 <= $3; } + | floatex GE floatex { $$ = $1 >= $3; } + +numex: numex PLUS numex { $$ = $1 + $3; } + | numex MINUS numex { $$ = $1 - $3; } + | numex MULT numex { $$ = $1 * $3; } + | numex DIV numex { if ($3 == 0) + { + if (!keynote_donteval) + keynote_exceptionflag = 1; + } + else + $$ = ($1 / $3); + } + | numex MOD numex { if ($3 == 0) + { + if (!keynote_donteval) + keynote_exceptionflag = 1; + } + else + $$ = $1 % $3; + } + | numex EXP numex { $$ = intpow($1, $3); } + | MINUS numex %prec UNARYMINUS { $$ = -($2); } + | OPENPAREN numex CLOSEPAREN { $$ = $2; } + | NUM { $$ = $1; } + | OPENNUM strnotconcat { if (keynote_exceptionflag || + keynote_donteval) + $$ = 0; + else + { + keynote_lex_remove($2); + + if (!isfloatstring($2)) + $$ = 0; + else + $$ = (int) floor(atof($2)); + free($2); + } + } + +floatex: floatex PLUS floatex { $$ = ($1 + $3); } + | floatex MINUS floatex { $$ = ($1 - $3); } + | floatex MULT floatex { $$ = ($1 * $3); } + | floatex DIV floatex { if ($3 == 0) + { + if (!keynote_donteval) + keynote_exceptionflag = 1; + } + else + $$ = ($1 / $3); + } + | floatex EXP floatex { if (!keynote_exceptionflag && + !keynote_donteval) + $$ = pow($1, $3); + } + | MINUS floatex %prec UNARYMINUS { $$ = -($2); } + | OPENPAREN floatex CLOSEPAREN { $$ = $2; } + | FLOAT { $$ = $1; } + | OPENFLT strnotconcat { + if (keynote_exceptionflag || + keynote_donteval) + $$ = 0.0; + else + { + keynote_lex_remove($2); + + if (!isfloatstring($2)) + $$ = 0.0; + else + $$ = atof($2); + free($2); + } + } + +stringexp: str EQ str { + if (keynote_exceptionflag || keynote_donteval) + $$ = 0; + else + { + $$ = strcmp($1, $3) == 0 ? 1 : 0; + keynote_lex_remove($1); + keynote_lex_remove($3); + free($1); + free($3); + } + } + | str NE str { + if (keynote_exceptionflag || keynote_donteval) + $$ = 0; + else + { + $$ = strcmp($1, $3) != 0 ? 1 : 0; + keynote_lex_remove($1); + keynote_lex_remove($3); + free($1); + free($3); + } + } + | str LT str { + if (keynote_exceptionflag || keynote_donteval) + $$ = 0; + else + { + $$ = strcmp($1, $3) < 0 ? 1 : 0; + keynote_lex_remove($1); + keynote_lex_remove($3); + free($1); + free($3); + } + } + | str GT str { + if (keynote_exceptionflag || keynote_donteval) + $$ = 0; + else + { + $$ = strcmp($1, $3) > 0 ? 1 : 0; + keynote_lex_remove($1); + keynote_lex_remove($3); + free($1); + free($3); + } + } + | str LE str { + if (keynote_exceptionflag || keynote_donteval) + $$ = 0; + else + { + $$ = strcmp($1, $3) <= 0 ? 1 : 0; + keynote_lex_remove($1); + keynote_lex_remove($3); + free($1); + free($3); + } + } + | str GE str { + if (keynote_exceptionflag || keynote_donteval) + $$ = 0; + else + { + $$ = strcmp($1, $3) >= 0 ? 1 : 0; + keynote_lex_remove($1); + keynote_lex_remove($3); + free($1); + free($3); + } + } + | str REGEXP str + { + regmatch_t pmatch[32]; + char grp[4], *gr; + regex_t preg; + int i; + + if (keynote_exceptionflag || keynote_donteval) + $$ = 0; + else + { + keynote_lex_remove($1); + keynote_lex_remove($3); + + memset(pmatch, 0, sizeof(pmatch)); + memset(grp, 0, sizeof(grp)); + + if (regcomp(&preg, $3, REG_EXTENDED)) + { + free($1); + free($3); + keynote_exceptionflag = 1; + } + else + { + /* Clean-up residuals from previous regexps */ + keynote_env_cleanup(&keynote_temp_list, 1); + + free($3); + i = regexec(&preg, $1, 32, pmatch, 0); + $$ = (i == 0 ? 1 : 0); + if (i == 0) + { +#ifdef NO_SNPRINTF + sprintf(grp, "%d", preg.re_nsub); +#else /* NO_SNPRINTF */ + snprintf(grp, 3, "%d", preg.re_nsub); +#endif /* NO_SNPRINTF */ + if (keynote_env_add("_0", grp, &keynote_temp_list, + 1, 0) != RESULT_TRUE) + { + free($1); + regfree(&preg); + return -1; + } + + for (i = 1; i < 32 && pmatch[i].rm_so != -1; i++) + { + gr = calloc(pmatch[i].rm_eo - pmatch[i].rm_so + + 1, sizeof(char)); + if (gr == (char *) NULL) + { + free($1); + regfree(&preg); + keynote_errno = ERROR_MEMORY; + return -1; + } + + strncpy(gr, $1 + pmatch[i].rm_so, + pmatch[i].rm_eo - pmatch[i].rm_so); + gr[pmatch[i].rm_eo - pmatch[i].rm_so] = '\0'; +#ifdef NO_SNPRINTF + sprintf(grp, "_%d", i); +#else /* NO_SNPRINTF */ + snprintf(grp, 3, "_%d", i); +#endif /* NO_SNPRINTF */ + if (keynote_env_add(grp, gr, &keynote_temp_list, + 1, 0) == -1) + { + free($1); + regfree(&preg); + free(gr); + return -1; + } + else + free(gr); + } + } + + regfree(&preg); + free($1); + } + } + } + +str: str DOTT str { if (keynote_exceptionflag || keynote_donteval) + $$ = (char *) NULL; + else + { + $$ = calloc(strlen($1) + strlen($3) + 1, + sizeof(char)); + keynote_lex_remove($1); + keynote_lex_remove($3); + if ($$ == (char *) NULL) + { + free($1); + free($3); + keynote_errno = ERROR_MEMORY; + return -1; + } + + strcpy($$, $1); + strcpy($$ + strlen($1), $3); + free($1); + free($3); + if (keynote_lex_add($$, LEXTYPE_CHAR) == -1) + return -1; + } + } + | strnotconcat { $$ = $1; } + +strnotconcat: STRING { $$ = $1; } + | OPENPAREN str CLOSEPAREN { $$ = $2; } + | VARIABLE { if (keynote_exceptionflag || keynote_donteval) + $$ = (char *) NULL; + else + { + $$ = my_lookup($1); + keynote_lex_remove($1); + free($1); + if ($$ == (char *) NULL) + { + if (keynote_errno) + return -1; + $$ = strdup(""); + } + else + $$ = strdup($$); + + if ($$ == (char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + if (keynote_lex_add($$, LEXTYPE_CHAR) == -1) + return -1; + } + } + | DEREF str { if (keynote_exceptionflag || keynote_donteval) + $$ = (char *) NULL; + else + { + $$ = my_lookup($2); + keynote_lex_remove($2); + free($2); + if ($$ == (char *) NULL) + { + if (keynote_errno) + return -1; + $$ = strdup(""); + } + else + $$ = strdup($$); + + if ($$ == (char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + if (keynote_lex_add($$, LEXTYPE_CHAR) == -1) + return -1; + } + } +%% + +/* + * Find all assertions signed by s and give us the one with the highest + * return value. + */ +static int +resolve_assertion(char *s) +{ + int i, alg = KEYNOTE_ALGORITHM_NONE, p = 0; + void *key = (void *) s; + struct assertion *as; + struct keylist *kl; + + kl = keynote_keylist_find(keynote_current_assertion->as_keylist, s); + if (kl != (struct keylist *) NULL) + { + alg = kl->key_alg; + key = kl->key_key; + } + + for (i = 0;; i++) + { + as = keynote_find_assertion(key, i, alg); + if (as == (struct assertion *) NULL) /* Gone through all of them */ + return p; + + if (as->as_kresult == KRESULT_DONE) + if (p < as->as_result) + p = as->as_result; + + /* Short circuit if we find an assertion with maximum return value */ + if (p == (keynote_current_session->ks_values_num - 1)) + return p; + } + + return 0; +} + +/* + * Environment variable lookup. + */ +static char * +my_lookup(char *s) +{ + struct keynote_session *ks = keynote_current_session; + char *ret; + + if (!strcmp(s, "_MIN_TRUST")) + { + keynote_used_variable = 1; + return ks->ks_values[0]; + } + else + { + if (!strcmp(s, "_MAX_TRUST")) + { + keynote_used_variable = 1; + return ks->ks_values[ks->ks_values_num - 1]; + } + else + { + if (!strcmp(s, "_VALUES")) + { + keynote_used_variable = 1; + return keynote_env_lookup("_VALUES", ks->ks_env_table, + HASHTABLESIZE); + } + else + { + if (!strcmp(s, "_ACTION_AUTHORIZERS")) + { + keynote_used_variable = 1; + return keynote_env_lookup("_ACTION_AUTHORIZERS", + ks->ks_env_table, HASHTABLESIZE); + } + } + } + } + + /* Temporary list (regexp results) */ + ret = keynote_env_lookup(s, &keynote_temp_list, 1); + if (ret != (char *) NULL) + return ret; + else + if (keynote_errno != 0) + return (char *) NULL; + + /* Local-Constants */ + ret = keynote_env_lookup(s, &keynote_init_list, 1); + if (ret != (char *) NULL) + return ret; + else + if (keynote_errno != 0) + return (char *) NULL; + + keynote_used_variable = 1; + + /* Action environment */ + ret = keynote_env_lookup(s, ks->ks_env_table, HASHTABLESIZE); + if (ret != (char *) NULL) + return ret; + else + if (keynote_errno != 0) + return (char *) NULL; + + /* Regex table */ + return keynote_env_lookup(s, &(ks->ks_env_regex), 1); +} + +/* + * If we had an exception, the boolean expression should return false. + * Otherwise, return the result of the expression (the argument). + */ +static int +checkexception(int i) +{ + if (keynote_exceptionflag) + { + keynote_exceptionflag = 0; + return 0; + } + else + return i; +} + + +/* + * Integer exponentation -- copied from Schneier's AC2, page 244. + */ +static int +intpow(int x, int y) +{ + int s = 1; + + /* + * x^y with y < 0 is equivalent to 1/(x^y), which for + * integer arithmetic is 0. + */ + if (y < 0) + return 0; + + while (y) + { + if (y & 1) + s *= x; + + y >>= 1; + x *= x; + } + + return s; +} + +/* + * Check whether the string is a floating point number. + */ +static int +isfloatstring(char *s) +{ + int i, point = 0; + + for (i = strlen(s) - 1; i >= 0; i--) + if (!isdigit(s[i])) + { + if (s[i] == '.') + { + if (point == 1) + return 0; + else + point = 1; + } + else + return 0; + } + + return 1; +} + +/* + * Initialize array for threshold search. + */ +static int +keynote_init_kth(void) +{ + int i = keynote_current_session->ks_values_num; + + if (i == -1) + return -1; + + keynote_kth_array = (int *) calloc(i, sizeof(int)); + if (keynote_kth_array == (int *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + return RESULT_TRUE; +} + +/* + * Get the k-th best return value. + */ +static int +get_kth(int k) +{ + int i; + + for (i = keynote_current_session->ks_values_num - 1; i >= 0; i--) + { + k -= keynote_kth_array[i]; + + if (k <= 0) + return i; + } + + return 0; +} + +/* + * Cleanup array. + */ +void +keynote_cleanup_kth(void) +{ + if (keynote_kth_array != (int *) NULL) + { + free(keynote_kth_array); + keynote_kth_array = (int *) NULL; + } +} + +void +knerror(char *s) +{} diff --git a/lib/libkeynote/man/keynote-keygen.1 b/lib/libkeynote/man/keynote-keygen.1 new file mode 100644 index 00000000000..845baa984e3 --- /dev/null +++ b/lib/libkeynote/man/keynote-keygen.1 @@ -0,0 +1,113 @@ +.\" $OpenBSD: keynote-keygen.1,v 1.1 1999/05/23 22:11:07 angelos 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 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. +.\" You may use this code under the GNU public license if you so wish. Please +.\" contribute changes back to the author. +.\" +.\" 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. +.\" +.Dd April 29, 1999 +.Dt keynote-keygen 1 +.Os +.\" .TH keynote-keygen 1 local +.Sh NAME +.Nm keynote-keygen +.Nd command line tool for generating public/private keys +.Sh SYNOPSIS +.Nm keynote-keygen +.Ar AlgorithmName +.Ar KeySize +.Ar PublicKeyFile +.Ar PrivateKeyFile +.Op print-offset +.Op print-length +.Sh DESCRIPTION +For details on +.Nm KeyNote , +see the web page +.Bd -literal -offset indent + http://www.cis.upenn.edu/~keynote +.Ed +.Pp +.Nm keynote-keygen +creates a public/private key of size +.Fa KeySize , +for the algorithm specified by +.Fa AlgorithmName . +Typical keysizes are 512, 1024, or 2048 (bits). The minimum key size +for DSA keys is 512 (bits). Supported +.Fa AlgorithmName +identifiers are: +.Bl -tag -width indent +.It ``dsa-hex:'' +.It ``dsa-base64:'' +.It ``rsa-hex:'' +.It ``rsa-base64:'' +.El +.Pp +Notice that the trailing colon is required. +The resulting public key is stored in file +.Fa PublicKeyFile . +Similarly, the resulting private key is stored in file +.Fa PrivateKeyFile . +Either of the filenames can be specified to be ``-'', in which +case the corresponding key(s) will be printed in standard output. +.Pp +The optional parameters +.Fa print-offset +and +.Fa print-length +specify the offset from the begining of the line where the key +will be printed, and the number of characters of the key that will +be printed per line. +.Fa print-length +includes +.Fa AlgorithmName +for the first line and has to be longer (by at least 2) than +.Fa AlgorithmName . +.Fa print-length +also accounts for the line-continuation character (backslash) at +the end of each line, and the doublequotes at the begining and end +of the key encoding. Default values are 12 and 50 respectively. +.Pp +.Sh SEE ALSO +.Xr keynote 3 , +.Xr keynote 4 , +.Xr keynote-sign 1 , +.Xr keynote-sigver 1 , +.Xr keynote-verify 1 +.Bl -tag -width "AAAAAAA" +.It ``The KeyNote Trust-Management System'' +M. Blaze, J. Feigenbaum, A. D. Keromytis, +Internet Drafts, draft-ietf-trustmgt-keynote-00.txt +.It ``Decentralized Trust Management'' +M. Blaze, J. Feigenbaum, J. Lacy, +1996 IEEE Conference on Privacy and Security +.It ``Compliance-Checking in the PolicyMaker Trust Management System'' +M. Blaze, J. Feigenbaum, M. Strauss, +1998 Financial Crypto Conference +.El +.Sh AUTHOR +Angelos D. Keromytis (angelos@dsl.cis.upenn.edu) +.Sh WEB PAGE +http://www.cis.upenn.edu/~keynote +.Sh BUGS +None that we know of. +If you find any, please report them at +.Bd -literal -offset indent -compact +keynote@research.att.com +.Ed diff --git a/lib/libkeynote/man/keynote-sign.1 b/lib/libkeynote/man/keynote-sign.1 new file mode 100644 index 00000000000..0ae1bd045e6 --- /dev/null +++ b/lib/libkeynote/man/keynote-sign.1 @@ -0,0 +1,129 @@ +.\" $OpenBSD: keynote-sign.1,v 1.1 1999/05/23 22:11:07 angelos 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 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. +.\" You may use this code under the GNU public license if you so wish. Please +.\" contribute changes back to the author. +.\" +.\" 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. +.\" +.Dd April 29, 1999 +.Dt keynote-sign 1 +.Os +.\" .TH keynote-sign 1 local +.Sh NAME +.Nm keynote-sign +.Nd command line tool for signing +.Xr KeyNote 3 +assertions +.Sh SYNOPSIS +.Nm keynote-sign +.Op Fl v +.Ar AlgorithmName +.Ar AssertionFile +.Ar PrivateKeyFile +.Sh DESCRIPTION +For details on +.Nm KeyNote , +see the web page +.Bd -literal -offset indent + http://www.cis.upenn.edu/~keynote +.Ed +.Pp +.Nm keynote-sign +reads the assertion contained in +.Fa AssertionFile +and generates a signature specified by +.Fa AlgorithmName +using the private key stored in +.Fa PrivateKeyFile . +The private key is expected to be of the form output by +.Xr keynote-keygen 1 . +The private key algorithm and the +.Fa AlgorithmName +specified as an argument are expected to match. There is no requirement +for the internal or ASCII encodings to match. +Valid +.Fa AlgorithmName +identifiers are: +.Bl -tag -width indent +.It ``sig-dsa-sha1-hex:'' +.It ``sig-dsa-sha1-base64:'' +.It ``sig-rsa-sha1-hex:'' +.It ``sig-rsa-sha1-base64:'' +.It ``sig-rsa-md5-hex:'' +.It ``sig-rsa-md5-base64:'' +.El +.Pp +Notice that the trailing colon is required. +The resulting signature is printed in standard output. This can then +be added (via cut-and-paste or some script) at the end of the +assertion, in the +.Fa Signature +field. +.Pp +The public key corresponding to the private key in +.Fa PrivateKeyFile +is expected to already be included in the +.Fa Authorizer +field of the assertion, either directly or indirectly (i.e., through +use of a +.Fa Local-Init +attribute). Furthermore, the assertion must have a +.Fa Signature +field (even if it is empty), as the signature is computed on +everything between the +.Fa KeyNote-Version +and +.Fa Signature +keywords (inclusive), and the +.Fa AlgorithmName +string. +.Pp +If the +.Op Fl v +flag is provided, +.Nm keynote-sign +will also verify the newly-created signature using the +.Fa Authorizer +field key. +.Sh SEE ALSO +.Xr keynote 3 , +.Xr keynote 4 , +.Xr keynote-keygen 1 , +.Xr keynote-sigver 1 , +.Xr keynote-verify 1 +.Bl -tag -width "AAAAAAA" +.It ``The KeyNote Trust-Management System'' +M. Blaze, J. Feigenbaum, A. D. Keromytis, +Internet Drafts, draft-ietf-trustmgt-keynote-00.txt +.It ``Decentralized Trust Management'' +M. Blaze, J. Feigenbaum, J. Lacy, +1996 IEEE Conference on Privacy and Security +.It ``Compliance-Checking in the PolicyMaker Trust Management System'' +M. Blaze, J. Feigenbaum, M. Strauss, +1998 Financial Crypto Conference +.El +.Sh AUTHOR +Angelos D. Keromytis (angelos@dsl.cis.upenn.edu) +.Sh WEB PAGE +http://www.cis.upenn.edu/~keynote +.Sh BUGS +None that we know of. +If you find any, please report them at +.Bd -literal -offset indent -compact +keynote@research.att.com +.Ed diff --git a/lib/libkeynote/man/keynote-sigver.1 b/lib/libkeynote/man/keynote-sigver.1 new file mode 100644 index 00000000000..cdbc72b5b8e --- /dev/null +++ b/lib/libkeynote/man/keynote-sigver.1 @@ -0,0 +1,73 @@ +.\" $OpenBSD: keynote-sigver.1,v 1.1 1999/05/23 22:11:07 angelos 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 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. +.\" You may use this code under the GNU public license if you so wish. Please +.\" contribute changes back to the author. +.\" +.\" 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. +.\" +.Dd April 29, 1999 +.Dt keynote-sigver 1 +.Os +.\" .TH keynote-sigver 1 local +.Sh NAME +.Nm keynote-sigver +.Nd command line tool for verifying signed +.Xr KeyNote 3 +assertions +.Sh SYNOPSIS +.Nm keynote-sigver +.Op AssertionFile +.Sh DESCRIPTION +For details on +.Nm KeyNote , +see the web page +.Bd -literal -offset indent + http://www.cis.upenn.edu/~keynote +.Ed +.Pp +.Nm keynote-sigver +reads the assertion contained in +.Fa AssertionFile +and verifies the public key signature on it. +.Sh SEE ALSO +.Xr keynote 3 , +.Xr keynote 4 , +.Xr keynote-keygen 1 , +.Xr keynote-sign 1 , +.Xr keynote-verify 1 +.Bl -tag -width "AAAAAAA" +.It ``The KeyNote Trust-Management System'' +M. Blaze, J. Feigenbaum, A. D. Keromytis, +Internet Drafts, draft-ietf-trustmgt-keynote-00.txt +.It ``Decentralized Trust Management'' +M. Blaze, J. Feigenbaum, J. Lacy, +1996 IEEE Conference on Privacy and Security +.It ``Compliance-Checking in the PolicyMaker Trust Management System'' +M. Blaze, J. Feigenbaum, M. Strauss, +1998 Financial Crypto Conference +.El +.Sh AUTHOR +Angelos D. Keromytis (angelos@dsl.cis.upenn.edu) +.Sh WEB PAGE +http://www.cis.upenn.edu/~keynote +.Sh BUGS +None that we know of. +If you find any, please report them at +.Bd -literal -offset indent -compact +keynote@research.att.com +.Ed diff --git a/lib/libkeynote/man/keynote-verify.1 b/lib/libkeynote/man/keynote-verify.1 new file mode 100644 index 00000000000..2e390500fba --- /dev/null +++ b/lib/libkeynote/man/keynote-verify.1 @@ -0,0 +1,138 @@ +.\" $OpenBSD: keynote-verify.1,v 1.1 1999/05/23 22:11:07 angelos 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 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. +.\" You may use this code under the GNU public license if you so wish. Please +.\" contribute changes back to the author. +.\" +.\" 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. +.\" +.Dd April 29, 1999 +.Dt keynote-verify 1 +.Os +.\" .TH keynote-verify 1 local +.Sh NAME +.Nm keynote-verify +.Nd command line tool for evaluating +.Xr KeyNote 3 +assertions +.Sh SYNOPSIS +.Nm keynote-verify +.Op Fl h +.Op Fl e Ar file +.Fl l Ar file +.Fl r Ar retlist +.Op Fl k Ar file +.Op Fl l Ar file +.Op Ar file ... +.Sh DESCRIPTION +For details on +.Nm KeyNote , +see the web page +.Bd -literal -offset indent + http://www.cis.upenn.edu/~keynote +.Ed +.Pp +For each operand that names a +.A file , +.Nm keynote-verify +reads the file and parses the assertions contained therein (one +assertion per file). +.Pp +Files given with the +.Fl l +flag are assumed to contain trusted assertions (no signature +verification is performed, and the +.Fa Authorizer +field can contain non-key principals. +There should be at least one assertion with the +.Fa POLICY +keyword in the +.Fa Authorizer +field. +.Pp +The +.Fl r +flag is used to provide a comma-separated list of return values, in +increasing order of compliance from left to right. +.Pp +Files given with the +.Fl e +flag are assumed to contain environment variables and their values, +in the format: +.Bd -literal -offset indent + varname = \"value\" +.Ed +.Pp +.Fa varname +can begin with any letter (upper or lower case) or number, +and can contain underscores. +.Fa value +is a quoted string, and can contain any character, and escape +(backslash) processing is performed, as specified in the KeyNote +draft. +.Pp +The remaining options are: +.Bl -tag -width indent +.It Fl h +Print a usage message and exit. +.It Fl k Ar file +Add a key from +.Fa file +in the action authorizers. +.El +.Pp +Exactly one +.Fl r +and least one of each +.Fl e , +.Fl l , +and +.Fl k +flags should be given per invocation. If no flags are given, +.Nm keynote-verify +prints the usage message and exits with error code -1. +.Pp +The +.Nm keynote-verify +exits with code -1 if there was an error, and 0 on success. +.Sh SEE ALSO +.Xr keynote 3 , +.Xr keynote 4 , +.Xr keynote-keygen 1 , +.Xr keynote-sign 1 , +.Xr keynote-sigver 1 +.Bl -tag -width "AAAAAAA" +.It ``The KeyNote Trust-Management System'' +M. Blaze, J. Feigenbaum, A. D. Keromytis, +Internet Drafts, draft-ietf-trustmgt-keynote-00.txt +.It ``Decentralized Trust Management'' +M. Blaze, J. Feigenbaum, J. Lacy, +1996 IEEE Conference on Privacy and Security +.It ``Compliance-Checking in the PolicyMaker Trust Management System'' +M. Blaze, J. Feigenbaum, M. Strauss, +1998 Financial Crypto Conference +.El +.Sh AUTHOR +Angelos D. Keromytis (angelos@dsl.cis.upenn.edu) +.Sh WEB PAGE +http://www.cis.upenn.edu/~keynote +.Sh BUGS +None that we know of. +If you find any, please report them at +.Bd -literal -offset indent -compact +keynote@research.att.com +.Ed diff --git a/lib/libkeynote/man/keynote.3 b/lib/libkeynote/man/keynote.3 new file mode 100644 index 00000000000..3575ffc2fe1 --- /dev/null +++ b/lib/libkeynote/man/keynote.3 @@ -0,0 +1,770 @@ +.\" $OpenBSD: keynote.3,v 1.1 1999/05/23 22:11:07 angelos 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 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. +.\" You may use this code under the GNU public license if you so wish. Please +.\" contribute changes back to the author. +.\" +.\" 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. +.\" +.Dd April 29, 1999 +.Dt KeyNote 3 +.Os +.\" .TH KeyNote 3 local +.Sh NAME +.Nm KeyNote +.Nd A Trust-Management System Library +.Sh SYNOPSIS +.Fd #include <keynote.h> +.Bd -literal + +struct environment +{ + char *env_name; + char *env_value; + int env_flags; + regex_t env_regex; + struct environment *env_next; +}; + +struct keynote_deckey +{ + int dec_algorithm; + void *dec_key; +}; + +struct keynote_binary +{ + int bn_len; + char *bn_key; +}; +.Ed +.Ft int +.Fd keynote_errno; +.Ft int +.Fn kn_init "void" +.Ft int +.Fn kn_add_assertion "int sessid" "char *assertion" "int len" "int flags" +.Ft int +.Fn kn_remove_assertion "int sessid" "int assertid" +.Ft int +.Fn kn_add_action "int sessid" "char *name" "char *value" "int flags" +.Ft int +.Fn kn_remove_action "int sessid" "char *name" +.Ft int +.Fn kn_add_authorizer "int sessid" "char *principal" +.Ft int +.Fn kn_remove_authorizer "int sessid" "char *principal" +.Ft int +.Fn kn_do_query "int sessid" "char **returnvalues" "int numvalues" +.Ft int +.Fn kn_get_failed "int sessid" "int type" "int seq" +.Ft int +.Fn kn_close "int sessid" +.Ft int +.Fn kn_query "struct environment *env" "char **returnvalues, int numvalues" "char **trusted, int *trustedlen, int numtrusted" "char **untrusted, int *untrustedlen, int numuntrusted" "char **authorizers, int numauthauthorizers" +.Ft char ** +.Fn kn_read_asserts "char *array" "int arraylen" "int *numassertions" +.Ft int +.Fn kn_encode_base64 "unsigned char const *src" "unsigned int srclen" "char *dst" "unsigned int dstlen" +.Ft int +.Fn kn_decode_base64 "char const *src" "unsigned char *dst" "unsigned int dstlen" +.Ft int +.Fn kn_encode_hex "unsigned char *src" "char **dst" "int srclen" +.Ft int +.Fn kn_decode_hex "char *src" "char **dst" +.Ft char * +.Fn kn_encode_key "struct keynote_deckey *dc" "int iencoding" "int encoding" "int keytype" +.Ft int +.Fn kn_decode_key "struct keynote_deckey *dc" "char *key" "int keytype" +.Ft char * +.Fn kn_sign_assertion "char *assertion" "int len" "char *key" "char *algorithm" "int vflag" +.Ft int +.Fn kn_verify_assertion "char *assertion" "int len" +.Fd Link options: -lkeynote -lm -lcrypto +.Sh DESCRIPTION +For details on +.Nm KeyNote , +see the web page +.Bd -literal -offset indent + http://www.cis.upenn.edu/~keynote +.Ed +.Pp +.Va keynote_errno +contains an error code if some library call failed. Failed calls +return -1 (if their return value is integer), or +.Dv NULL +(if their return value is a pointer) and set +.Va keynote_errno . +The defined error codes are: +.Bl -tag -width "ERROR_NOTFOUND" -offset indent +.It Li ERROR_MEMORY +Some memory allocation or usage error was encountered. +.It Li ERROR_SYNTAX +Some syntactic or logical error was encountered. +.It Li ERROR_NOTFOUND +One of the arguments referred to a nonexistent structure or entry. +.El +.Pp +If no errors were encountered, +.Va keynote_errno +will be set to 0. This variable should be reset to 0 if an error was +encountered, prior to calling other library routines. +.Pp +The main interface to +.Nm KeyNote +is centered around the concept of a session. A session describes a +collection of policies, assertions, action authorizers, return values, +and action attributes that the +.Nm KeyNote +system uses to evaluate a query. Information is not shared between +sessions. Policies, credentials, action authorizers, and action +attributes can be added or deleted at any point during the lifetime of +a session. Furthermore, an application can discover which assertions +failed to be evaluated, and in what way, during a query. +.Pp +For those applications that only need to do a simple query, there +exists a single call that takes as arguments all the necessary +information and performs all the necessary steps. This is essentially +a wrapper that calls the session API functions as necessary. +.Pp +Finally, there exist functions for doing ASCII to hexadecimal and +Base64 encoding (and vice versa), for encoding/decoding keys between +ASCII and binary formats, and for signing and verifying assertions. +.Pp +The description of all +.Nm KeyNote +library functions follows. +.Pp +.Fn kn_init +creates a new +.Nm KeyNote +session, and performs any necessary initializations. On success, this +function returns the new session ID, which is used by all subsequent +calls with a +.Fa sessid +argument. +On failure, it returns -1 and sets +.Va keynote_errno +to +.Er ERROR_MEMORY . +.Pp +.Fn kn_add_assertion +adds the assertion pointed to by the array +.Fa assertion , +of length +.Fa len +in the session identified by +.Fa sessid . +The first argument can be discarded after the call to this function. +The following flags are defined: +.Bl -tag -width ASSERT_FLAG_LOCAL -offset indent +.It ASSERT_FLAG_LOCAL +Mark this assertion as ultimately trusted. +Trusted assertions need not be signed, and the +.Fa Authorizer +and +.Fa Licensees +fields can have non-key entries. +.El +.Pp +At least one (trusted) assertion should have +.Dv POLICY +as the +.Fa Authorizer . +On success, this function will return an assertion ID which can be +used to remove the assertion from the session, by using +.Xr kn_remove_assertion 3 . +On failure, -1 is returned, and +.Va keynote_errno +is set to +.Er ERROR_NOTFOUND +if the session was not found, +.Er ERROR_SYNTAX +if the assertion was syntactically incorrect, or +.Er ERROR_MEMORY +if necessary memory could not be allocated. +.Pp +.Fn kn_remove_assertion +removes the assertion identified by +.Fa assertid +from the session identified by +.Fa sessid . +On success, this function returns 0. On failure, it returns -1 and +sets +.Va keynote_errno +to +.Er ERROR_NOTFOUND . +.Pp +.Fn kn_add_action +inserts the variable +.Fa name +in the action environment of session +.Fa sessid , +with the value +.Fa value . +The same attribute may be added more than once, but only the last +instance will be used (memory resources are consumed however). +.Pp +The +.Fa flags +specified are formed by or'ing the following values: +.Bl -tag -width ENVIRONMENT_FLAG_REGEX -offset indent +.It ENVIRONMENT_FLAG_FUNC +In this case, +.Fa value +is a pointer to a function that takes as argument a string and returns +a string. This is used to implement callbacks for getting action +attribute values. The argument passed to such a callback function is a +string identifying the action attribute whose value is requested, and +should return a pointer to string containing that value (this pointer +will not be freed by the library), the empty string if the value was +not found, or a +.Dv NULL +to indicate an error (and may set +.Va keynote_errno +appropriately). Prior to first use (currently, at the time the +attribute is added to the session environment), such functions are +called with +.Dv KEYNOTE_CALLBACK_INITIALIZE +as the argument (defined in keynote.h) so that they can +perform any special initializations. Furthermore, when the +session is deleted, all such functions will be called with +.Dv KEYNOTE_CALLBACK_CLEANUP +to perform any special cleanup (such as free any allocated memory). A +function may be called with either of these arguments more than once, +if it has been defined as the callback function for more than one +attribute. +.It ENVIRONMENT_FLAG_REGEX +In this case, +.Fa name +is a regular expression that may match more than one attribute. +In case of conflict between a regular expression and a ``simple'' +attribute, the latter will be given priority. In case of conflict +between two regular expression attributes, the one added later will be +given priority. A callback function should never change the current +.Nm KeyNote +session, start/invoke/operate on another session, or call one of the +session-API functions. +.El +.Pp +The combination of the two flags may be used to specify callback +functions that handle large sets of attributes (even to the extent of +having one callback function handling all attribute references). This +is particularly useful when the action attribute set is particularly +large. +.Pp +On success, +.Xr keynote_add_action 3 +returns 0. On failure, it returns -1 and sets +.Va keynote_errno to +.Er ERROR_NOTFOUND +if the session was not found, +.Er ERROR_SYNTAX +if the +.Fa name +was invalid (e.g., started with an underscore character) or was +.Dv NULL , +or +.Er ERROR_MEMORY +if necessary memory could not be allocated. +.Pp +.Fn kn_remove_action +removes action attribute +.Fa name +from the environment of session +.Fa sessid . +Notice that if more than one instances of +.Fa name +exist, only the one added last will be deleted. +On success, this function returns 0. On failure, it returns -1 and +.Va keynote_errno +is set to +.Er ERROR_NOTFOUND +if the session or the attribute were not found, or +.Er ERROR_SYNTAX +if the name was invalid. If the attribute value was a callback, that +function will be called with the define +.Dv KEYNOTE_CALLBACK_CLEANUP +as the argument. +.Pp +.Fn kn_add_authorizer +adds the principal pointed to by +.Fa principal +to the action authorizers list of session +.Fa sessid . +The principal is typically an ASCII-encoded key. On success, this +function will return 0. On failure, it returns -1 and sets +.Va keynote_errno +to +.Er ERROR_NOTFOUND +if the session was not found, +.Er ERROR_SYNTAX +if the encoding was invalid, or +.Er ERROR_MEMORY +if necessary memory could not be allocated. +.Pp +.Fn kn_remove_authorizer +removes +.Fa principal +from the action authorizer list of session +.Fa sessid . +On success, this function returns 0. On failure, it returns -1 and sets +.Va keynote_errno +to +.Er ERROR_NOTFOUND +if the session was not found. +.Pp +.Fn kn_do_query +evaluates the request based on the assertions, action attributes, and +action authorizers added to session +.Fa sessid . +.Fa returnvalues +is an ordered array of strings that contain the return values. The +lowest-ordered return value is contained in +.Fa returnvalues[0] , +and the highest-ordered value is +.Fa returnvalues[numvalues - 1] . +If +.Fa returnvalues +is +.Dv NULL , +the +.Fa returnvalues +from the previous call to +.Xr kn_do_query 3 +will be used. The programmer SHOULD NOT free +.Fa returnvalues +after the call to +.Xr kn_do_query 3 +if this feature is used, as the array is not replicated internally. +On success, this function returns an index into the +.Fa returnvalues +array. On failure, it returns -1 and sets +.Va keynote_errno +to +.Er ERROR_NOTFOUND +if the session was not found or the authorizers list was empty, +.Er ERROR_SYNTAX +if no +.Fa returnvalues +have been specified, or +.Er ERROR_MEMORY +if necessary memory could not be allocated. +.Pp +.Fn kn_get_failed +returns the assertion ID of the +.Fa num'th +assertion (starting from zero) in session +.Fa sessid +that was somehow invalid during evaluation. This function is typically +called after +.Xr kn_do_query 3 +is used to evaluate a request. +.Fa type +specifies the type of failure the application is interested in. It can +be set to: +.Bl -tag -width KEYNOTE_ERROR_SIGNATURE -offset indent +.It KEYNOTE_ERROR_ANY +to indicate interest in any error. +.It KEYNOTE_ERROR_SYNTAX +for syntactic or semantic errors. +.It KEYNOTE_ERROR_MEMORY +for memory-related problems. +.It KEYNOTE_ERROR_SIGNATURE +if the assertion could be be cryptographically verified. +.El +.Pp +These values are defined in keynote.h. An application can then delete +the offending assertion using +.Xr kn_remove_assertion 3 . +For example, to remove all assertion whose signature failed, an +application could do something like: +.Bd -literal + while ((assertid = kn_get_failed(sessid, KEYNOTE_ERROR_SIGNATURE, 0) + != -1) + kn_remove_assertion(sessid, assertid); +.Ed +.Pp +On success, +.Xr kn_get_failed 3 +returns an assertion ID. On failure, or when no assertion matching the +given criteria is found, it returns -1 and set +.Va keynote_errno +to +.Er ERROR_NOTFOUND . +.Pp +.Fn kn_close +closes session +.Fa sessid +and frees all related resources, deleting action attributes, action +authorizers, and assertions. On success, this function returns 0. On +failure, it returns -1 and sets +.Va keynote_errno +to +.Er ERROR_NOTFOUND +if the session was not found. +.Pp +.Fn kn_read_asserts +parses the string +.Fa array +of length +.Fa arraylen +and returns an array of strings containing the assertions found in +.Fa array . +.Fa numassertions +contains the number of assertions (and thus strings in the returned +array) found in +.Fa array . +On failure, this function returns -1 and sets +.Va keynote_errno +to +.Er ERROR_MEMORY +if necessary memory could not be allocated, or +.Er ERROR_SYNTAX +if +.Fa array +was +.Dv NULL . +.Pp +.Fn kn_query +takes as arguments a list of action attributes in +.Fa env , +a list of return values in +.Fa returnvalues +(the number of returnvalues in indicated by +.Fa numvalues ), +a number ( +.Fa numtrusted ) +of locally-trusted assertions in +.Fa trusted +(the length of each assertion is given by the respective element of +.Fa trustedlen ), +a number ( +.Fa numuntrusted ) +of assertions that need to be cryptographically verified in +.Fa untrusted +(the length of each assertion is given by the respective element of +.Fa untrustedlen ), +and a number ( +.Fa numauthorizers ) +of action authorizers in +.Fa authorizers . +.Fa env +is a linked list of +.Fa struct environment +structures. The +.Fa env_name , +.Fa env_value , +and +.Fa env_flags +fields correspond to the +.Fa name , +.Fa value , +and +.Fa flags +arguments to +.Xr kn_add_assertion 3 +respectively. +.Fa env_regex +is not used. On success, this function returns an index in +.Fa returnvalues +indicating the returned value to the query. On failure, it returns -1 +and sets +.Va keynote_errno +to the same values as +.Xr kn_do_query 3 . +.Pp +.Fn kn_encode_base64 +converts the data of length +.Fa srclen +contained in +.Fa src +in Base64 encoding and stores them in +.Fa dst +which is of length +.Fa dstlen . +The actual length of the encoding stored in +.Fa dst +is returned. +.Fa dst should be long enough to also contain the trailing +string terminator. If +.Fa srclen +is not a multiple of 4, or +.Fa dst +is not long enough to contain the encoded data, this function returns +-1 and sets +.Va keynote_errno +to +.Er ERROR_SYNTAX . +.Pp +.Fn kn_decode_base64 +decodes the Base64-encoded data stored in +.Fa src +and stores the result in +.Fa dst , +which is of length +.Fa dstlen . +The actual length of the decoded data is returned on success. On +failure, this function returns -1 and sets +.Va keynote_errno +to +.Er ERROR_SYNTAX , +denoting either an invalid Base64 encoding or insufficient space in +.Fa dst . +.Pp +.Fn kn_encode_hex +encodes in ASCII-hexadecimal format the data of length +.Fa srclen +contained in +.Fa src . +This function allocates a chunk of memory to store the result, which +is returned in +.Fa dst . +Thus, this function should be used as follows: +.Bd -literal + char *dst; + + kn_encode_hex(src, &dst, srclen); +.Ed +.Pp +The length of the allocated buffer will be (2 * srclen + 1). On +success, this function returns 0. On failure, it returns -1 and sets +.Va keynote_errno +to +.Er ERROR_MEMORY +if it failed to allocate enough memory, +.Er ERROR_SYNTAX +if +.Fa dst +was +.Dv NULL . +.Pp +.Fn kn_decode_hex +decodes the ASCII hex-encoded string in +.Fa src +and stores the result in a memory chunk allocated by the function. A +pointer to that memory is stored in +.Fa dst . +The length of the allocated memory will be (strlen(src) / 2). On +success, this function returns 0. On failure, it returns -1 and sets +.Va keynote_errno +to +.Er ERROR_MEMORY +if it could not allocate enough memory, or +.Er ERROR_SYNTAX +if +.Fa dst +was +.Dv NULL , +or the length of +.Fa src +is not even. +.Pp +.Fn kn_encode_key +ASCII-encodes a cryptographic key. The binary representation of the +key is contained in +.Fa dc . +The field +.Fa dec_key +in that structure is a pointer to some cryptographic algorithm +dependent information describing the key. In this implementation, this +pointer should be a +.Fa DSA * +or +.Fa RSA * +for DSA or RSA keys respectively, as used in the SSL library, or a +.Fa keynote_binary * +for cryptographic keys whose algorithm +.Nm KeyNote +does not know about but the application wishes to include in the +action authorizers (and thus need to be canonicalized). The field +.Fa dec_algorithm +describes the cryptographic algorithm, and may be one of +.Dv KEYNOTE_ALGORITHM_DSA , +.Dv KEYNOTE_ALGORITHM_RSA , +or +.Dv KEYNOTE_ALGORITHM_BINARY +in this implementation. +.Pp +.Fa iencoding +describes how the key should be binary-encoded. This implementation +supports +.DV INTERNAL_ENC_PKCS1 +for RSA keys, +.Dv INTERNAL_ENC_ASN1 +for DSA keys, and +.Dv INTERNAL_ENC_NONE +for BINARY keys. +.Fa encoding +describes what ASCII encoding should be applied to the key. Valid +values are +.Dv ENCODING_HEX +and +.Dv ENCODING_BASE64 , +for hexadecimal and Base64 encoding respectively. +.Fa keytype +is one of +.Dv KEYNOTE_PUBLIC_KEY +or +.Dv KEYNOTE_PRIVATE_KEY +to indicate whether the key is public or private. Private keys have +the string +.Dv KEYNOTE_PRIVATE_KEY_PREFIX +(defined in keynote.h) prefixed to the algorithm name. On success, +this function returns a string containing the encoded key. On failure, +it returns +.Dv NULL +and sets +.Va keynote_errno +to +.Er ERROR_NOTFOUND +if the +.Fa dc +argument was invalid, +.Er ERROR_MEMORY +if it failed to allocate the necessary memory, or +.Er ERROR_SYNTAX +if the key to be converted was invalid. +.Pp +.Fn kn_decode_key +decodes the ASCII-encoded string contained in +.Fa key . +The result is placed in +.Fa dc , +with +.Fa dec_algorithm +describing the algorithm (see +.Xr kn_encode_key 3 ), +and +.Fa dec_key +pointing to an algorithm-dependent structure. In this implementation, +this is an SSLeay/OpenSSL-defined +.Fa DSA * +for DSA keys, +.Fa RSA * +for RSA keys, and a +.Fa keynote_binary * +for BINARY keys. +.Fa keytype +takes the values +.Dv KEYNOTE_PUBLIC_KEY +or +.Dv KEYNOTE_PRIVATE_KEY +to specify a public or private key, where applicable. On success, this +function returns 0. On failure, it returns -1 and sets +.Va keynote_errno +to +.Er ERROR_MEMORY +if necessary memory could not be allocated, or +.Er ERROR_SYNTAX +if the key or the ASCII encoding was malformed. +.Pp +.Fn kn_sign_assertion +produces the cryptographic signature for the assertion of length +.Fa len +stored in +.Fa assertion , +using the ASCII-encoded cryptographic key contained in +.Fa key . +The type of signature to be produced is described by the string +.Fa algorithm . +Possible values for this string are +.Dv SIG_RSA_SHA1_HEX +.Dv SIG_RSA_SHA1_BASE64 , +.Dv SIG_RSA_MD5_HEX , +and +.Dv SIG_RSA_MD5_HEX +for RSA keys, +.Dv SIG_DSA_SHA1_HEX +and +.Dv SIG_DSA_SHA1_BASE64 +for DSA keys. No other cryptographic signatures are currently +supported by this implementation. If +.Fa vflag +is set to 1, then the generated signature will also be verified. On +success, this function returns a string containing the ASCII-encoded +signature, without modifying the +.Fa assertion . +On failure, it returns +.Dv NULL +and sets +.Va keynote_errno +to +.Er ERROR_NOTFOUND +if one of the arguments was +.Dv NULL, +.Er ERROR_MEMORY +if necessary memory could not be allocated, or +.Er ERROR_SYNTAX +if the +.Fa algorithm , +the +.Fa key , +or the +.Fa assertion +(if signature verification was requested) was invalid. +.Pp +.Fn kn_verify_assertion +verifies the cryptographic signature on the assertion of length +.Fa len +contained in string +.Fa assertion . +On success, this function returns +.Dv SIGRESULT_TRUE +if the signature could be verified, or +.Dv SIGRESULT_FALSE +otherwise. On failure, this function returns -1 and sets +.Va keynote_errno +to +.Er ERROR_MEMORY +if necessary memory could not be allocated, or +.Er ERROR_SYNTAX +if the assertion contained a syntactic error, or the cryptographic +algorithm was not supported. +.Pp +.Sh FILES +.Fd keynote.h +.Fd libkeynote.a +.Sh SEE ALSO +.Xr keynote-keygen 1 , +.Xr keynote-sign 1 , +.Xr keynote-sigver 1 , +.Xr keynote-verify 1 +.Bl -tag -width "AAAAAAA" +.It ``The KeyNote Trust-Management System'' +M. Blaze, J. Feigenbaum, A. D. Keromytis, +Internet Drafts, draft-ietf-trustmgt-keynote-00.txt +.It ``Decentralized Trust Management'' +M. Blaze, J. Feigenbaum, J. Lacy, +1996 IEEE Conference on Privacy and Security +.It ``Compliance-Checking in the PolicyMaker Trust Management System'' +M. Blaze, J. Feigenbaum, M. Strauss, +1998 Financial Crypto Conference +.El +.Sh AUTHOR +Angelos D. Keromytis (angelos@dsl.cis.upenn.edu) +.Sh WEB PAGE +http://www.cis.upenn.edu/~keynote +.Sh DIAGNOSTICS +The return values of all the functions have been given along with the +function description above. +.Sh BUGS +None that we know of. +If you find any, please report them at +.Bd -literal -offset indent -compact +keynote@research.att.com +.Ed diff --git a/lib/libkeynote/man/keynote.4 b/lib/libkeynote/man/keynote.4 new file mode 100644 index 00000000000..892585dc3af --- /dev/null +++ b/lib/libkeynote/man/keynote.4 @@ -0,0 +1,266 @@ +.\" $OpenBSD: keynote.4,v 1.1 1999/05/23 22:11:08 angelos 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 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. +.\" You may use this code under the GNU public license if you so wish. Please +.\" contribute changes back to the author. +.\" +.\" 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. +.\" +.Dd May 22, 1999 +.Dt KeyNote 3 +.Os +.\" .TH KeyNote 4 local +.Sh NAME +.Nm KeyNote +.Nd A Trust-Management System +.Sh SYNOPSIS +.Fd #include <keynote.h> +.Fd Link options: -lkeynote -lm -lcrypto +.Sh DESCRIPTION +For more details on +.Nm KeyNote , +see the web page +.Bd -literal -offset indent + http://www.cis.upenn.edu/~keynote +.Ed +.Pp +Additional details on the API and the various tools are given in the +man pages listed at the end of this manual. +.Pp +Trust management, introduced in the PolicyMaker system, is a unified +approach to specifying and interpreting security policies, +credentials, and relationships; it allows direct authorization of +security-critical actions. A trust-management system provides +standard, general-purpose mechanisms for specifying application +security policies and credentials. Trust-management credentials +describe a specific delegation of trust and subsume the role of public +key certificates; unlike traditional certificates, which bind keys to +names, credentials can bind keys directly to the authorization to +perform specific tasks. + +A trust-management system has five basic components: + +.nf +* A language for describing `actions,' which are operations with + security consequences that are to be controlled by the system. + +* A mechanism for identifying `principals,' which are entities that + can be authorized to perform actions. + +* A language for specifying application `policies,' which govern the + actions that principals are authorized to perform. + +* A language for specifying `credentials,' which allow principals + to delegate authorization to other principals. + +* A `compliance checker,' which provides a service to applications + for determining how an action requested by principals should be + handled, given a policy and a set of credentials. +.fi + +The trust-management approach has a number of advantages over other +mechanisms for specifying and controlling authorization, especially +when security policy is distributed over a network or is otherwise +decentralized. + +Trust management unifies the notions of security policy, credentials, +access control, and authorization. An application that uses a +trust-management system can simply ask the compliance checker whether +a requested action should be allowed. Furthermore, policies and +credentials are written in standard languages that are shared by all +trust-managed applications; the security configuration mechanism for +one application carries exactly the same syntactic and semantic +structure as that of another, even when the semantics of the +applications themselves are quite different. + +Trust-management policies are easy to distribute across networks, +helping to avoid the need for application-specific distributed policy +configuration mechanisms, access control lists, and certificate +parsers and interpreters. + +For a general discussion of the use of trust management in distributed +system security, see the papers listed at the end of this manual. + +KeyNote is a simple and flexible trust-management system designed to +work well for a variety of large- and small- scale Internet-based +applications. It provides a single, unified language for both local +policies and credentials. KeyNote policies and credentials, called +`assertions,' contain predicates that describe the trusted actions +permitted by the holders of specific public keys. KeyNote assertions +are essentially small, highly-structured programs. A signed assertion, +which can be sent over an untrusted network, is also called a +`credential assertion.' Credential assertions, which also serve the +role of certificates, have the same syntax as policy assertions but +are also signed by the principal delegating the trust. + +In KeyNote: + +.nf +* Actions are specified as a collection of name-value pairs. + +* Principal names can be any convenient string and can directly + represent cryptographic public keys. + +* The same language is used for both policies and credentials. + +* The policy and credential language is concise, highly expressive, + human readable and writable, and compatible with a variety of + storage and transmission media, including electronic mail. + +* The compliance checker returns an application-configured `policy + compliance value' that describes how a request should be handled + by the application. Policy compliance values are always + positively derived from policy and credentials, facilitating + analysis of KeyNote-based systems. + +* Compliance checking is efficient enough for high-performance and + real-time applications. +.fi + +In KeyNote, the authority to perform trusted actions is associated +with one or more `principals.' A principal may be a physical entity, a +process in an operating system, a public key, or any other convenient +abstraction. KeyNote principals are identified by a string called a +`Principal Identifier.' In some cases, a Principal Identifier will +contain a cryptographic key interpreted by the KeyNote system (e.g., +for credential signature verification). In other cases, Principal +Identifiers may have a structure that is opaque to KeyNote. + +Principals perform two functions of concern to KeyNote: They request +`actions' and they issue `assertions.' Actions are any trusted +operations that an application places under KeyNote control. +Assertions delegate the authorization to perform actions to other +principals. + +Actions are described to the KeyNote compliance checker in terms of a +collection of name-value pairs called an `action attribute set.' The +action attribute set is created by the invoking application. Its +structure and format are described in detail in Section 3 of this +document. + +KeyNote provides advice to applications on the interpretation of +policy with regard to specific requested actions. Applications invoke +the KeyNote compliance checker by issuing a `query' containing a +proposed action attribute set and identifying the principal(s) +requesting it. The KeyNote system determines and returns an +appropriate `policy compliance value' from an ordered set of possible +responses. + +The policy compliance value returned from a KeyNote query advises the +application how to process the requested action. In the simplest case, +the compliance value is Boolean (e.g., "reject" or "approve"). +Assertions can also be written to select from a range of possible +compliance values, when appropriate for the application (e.g., "no +access", "restricted access", "full access"). Applications can +configure the relative ordering (from `weakest' to `strongest') of +compliance values at query time. + +Assertions are the basic programming unit for specifying policy and +delegating authority. Assertions describe the conditions under which a +principal authorizes actions requested by other principals. An +assertion identifies the principal that made it, which other +principals are being authorized, and the conditions under which the +authorization applies. The syntax of assertions is given in Section 4. + +A special principal, whose identifier is "POLICY", provides the root +of trust in KeyNote. "POLICY" is therefore considered to be authorized +to perform any action. + +Assertions issued by the "POLICY" principal are called `policy +assertions' and are used to delegate authority to otherwise untrusted +principals. The KeyNote security policy of an application consists of +a collection of policy assertions. + +When a principal is identified by a public key, it can digitally sign +assertions and distribute them over untrusted networks for use by +other KeyNote compliance checkers. These signed assertions are also +called `credentials,' and serve a role similar to that of traditional +public key certificates. Policies and credentials share the same +syntax and are evaluated according to the same semantics. A principal +can therefore convert its policy assertions into credentials simply by +digitally signing them. + +KeyNote is designed to encourage the creation of human-readable +policies and credentials that are amenable to transmission and storage +over a variety of media. Its assertion syntax is inspired by the +format of RFC822-style message headers. A KeyNote assertion contains a +sequence of sections, called `fields,' each of which specifying one +aspect of the assertion's semantics. Fields start with an identifier +at the beginning of a line and continue until the next field is +encountered. For example: + +.nf + KeyNote-Version: 2 + Comment: A simple, if contrived, email certificate for user mab + Local-Constants: ATT_CA_key = "RSA:acdfa1df1011bbac" + mab_key = "DSA:deadbeefcafe001a" + Authorizer: ATT_CA_key + Licensees: mab_key + Conditions: ((app_domain == "email") # valid for email only + && (address == "mab@research.att.com")); + Signature: "RSA-SHA1:f00f2244" +.fi + +For the exact meanings of all the fields, see the RFC reference at the +end of this manual. + +KeyNote semantics resolve the relationship between an application's +policy and actions requested by other principals, as supported by +credentials. The KeyNote compliance checker processes the assertions +against the action attribute set to determine the policy compliance +value of a requested action. These semantics are defined in Section 5. + +An important principle in KeyNote's design is `assertion +monotonicity'; the policy compliance value of an action is always +positively derived from assertions made by trusted principals. +Removing an assertion never results in increasing the compliance value +returned by KeyNote for a given query. The monotonicity property can +simplify the design and analysis of complex network-based security +protocols; network failures that prevent the transmission of +credentials can never result in spurious authorization of dangerous +actions. +.Pp +.Sh FILES +.Fd keynote.h +.Fd libkeynote.a +.Sh SEE ALSO +.Xr keynote 3 , +.Xr keynote-keygen 1 , +.Xr keynote-sign 1 , +.Xr keynote-sigver 1 , +.Xr keynote-verify 1 +.Bl -tag -width "AAAAAAA" +.It ``The KeyNote Trust-Management System'' +M. Blaze, J. Feigenbaum, A. D. Keromytis, +Internet Drafts, draft-ietf-trustmgt-keynote-00.txt +.It ``Decentralized Trust Management'' +M. Blaze, J. Feigenbaum, J. Lacy, +1996 IEEE Conference on Privacy and Security +.It ``Compliance-Checking in the PolicyMaker Trust Management System'' +M. Blaze, J. Feigenbaum, M. Strauss, +1998 Financial Crypto Conference +.El +.Sh AUTHOR +Angelos D. Keromytis (angelos@dsl.cis.upenn.edu) +.Sh WEB PAGE +http://www.cis.upenn.edu/~keynote +.Sh BUGS +None that we know of. +If you find any, please report them at +.Bd -literal -offset indent -compact +keynote@research.att.com +.Ed diff --git a/lib/libkeynote/parse_assertion.c b/lib/libkeynote/parse_assertion.c new file mode 100644 index 00000000000..4427782d54e --- /dev/null +++ b/lib/libkeynote/parse_assertion.c @@ -0,0 +1,623 @@ +/* $OpenBSD: parse_assertion.c,v 1.1 1999/05/23 22:11:06 angelos 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 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 <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <limits.h> +#include "assertion.h" +#include "environment.h" +#include "signature.h" + +/* Globals */ +struct assertion *keynote_current_assertion = (struct assertion *) NULL; +int keynote_errno = 0; + +extern int keynote_in_action_authorizers(void *, int); + +/* + * 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 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(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; + + if (as->as_buf[i] == '#') /* Comment */ + { + seen_field = 1; + + /* Skip until the end of line */ + while ((i< j) && as->as_buf[++i] != '\n') + ; + + 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(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) + break; + } + + /* 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; +} diff --git a/lib/libkeynote/signature.c b/lib/libkeynote/signature.c new file mode 100644 index 00000000000..3533d8ee41f --- /dev/null +++ b/lib/libkeynote/signature.c @@ -0,0 +1,1420 @@ +/* $OpenBSD: signature.c,v 1.1 1999/05/23 22:11:06 angelos 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 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. + */ + +/* + * Support for X509 keys and signing added by Ben Laurie <ben@algroup.co.uk> + * 3 May 1999 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include "signature.h" + +static const char hextab[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' +}; + +/* + * Actual conversion to hex. + */ +static void +bin2hex(unsigned char *data, unsigned char *buffer, int len) +{ + int off = 0; + + while(len > 0) + { + buffer[off++] = hextab[*data >> 4]; + buffer[off++] = hextab[*data & 0xF]; + data++; + len--; + } +} + +/* + * Encode a binary string with hex encoding. Return 0 on success. + */ +int +kn_encode_hex(unsigned char *buf, char **dest, int len) +{ + keynote_errno = 0; + if (dest == (char **) NULL) + { + keynote_errno = ERROR_SYNTAX; + return -1; + } + + *dest = (char *) calloc(2 * len + 1, sizeof(char)); + if (*dest == (char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + bin2hex(buf, *dest, len); + return 0; +} + +/* + * Decode a hex encoding. Return 0 on success. The second argument + * will be half as large as the first. + */ +int +kn_decode_hex(char *hex, char **dest) +{ + int i, decodedlen; + char ptr[3]; + + keynote_errno = 0; + if (dest == (char **) NULL) + { + keynote_errno = ERROR_SYNTAX; + return -1; + } + + if (strlen(hex) % 2) /* Should be even */ + { + keynote_errno = ERROR_SYNTAX; + return -1; + } + + decodedlen = strlen(hex) / 2; + *dest = (char *) calloc(decodedlen, sizeof(char)); + if (*dest == (char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + ptr[2] = '\0'; + for (i = 0; i < decodedlen; i++) + { + ptr[0] = hex[2 * i]; + ptr[1] = hex[(2 * i) + 1]; + (*dest)[i] = (unsigned char) strtoul(ptr, (char **) NULL, 16); + } + + return 0; +} + +void +keynote_free_key(void *key, int type) +{ + if (key == (void *) NULL) + return; + +#ifdef CRYPTO + /* DSA keys */ + if (type == KEYNOTE_ALGORITHM_DSA) + { + DSA_free(key); + return; + } + + /* RSA keys */ + if (type == KEYNOTE_ALGORITHM_RSA) + { + RSA_free(key); + return; + } + + /* X509 keys */ + if (type == KEYNOTE_ALGORITHM_X509) + { + RSA_free(key); /* RSA-specific */ + return; + } +#endif /* CRYPTO */ + +#ifdef PGPLIB + /* PGP keys */ + if (type == KEYNOTE_ALGORITHM_PGP) + { + /* Unsupported yet */ + return; + } +#endif /* PGPLIB */ + + /* BINARY keys */ + if (type == KEYNOTE_ALGORITHM_BINARY) + { + free(((struct keynote_binary *) key)->bn_key); + free(key); + return; + } + + /* Catch-all case */ + if (type == KEYNOTE_ALGORITHM_NONE) + free(key); +} + +#if defined(CRYPTO) || defined(PGPLIB) + +/* + * Map a signature to an algorithm. Return algorithm number (defined in + * keynote.h), or KEYNOTE_ALGORITHM_NONE if unknown. + * Also return in the second, third and fourth arguments the digest + * algorithm, ASCII and internal encodings respectively. + */ +static int +keynote_get_sig_algorithm(char *sig, int *hash, int *enc, int *internal) +{ + if (sig == (char *) NULL) + return KEYNOTE_ALGORITHM_NONE; + + if (!strncasecmp(SIG_DSA_SHA1_HEX, sig, SIG_DSA_SHA1_HEX_LEN)) + { + *hash = KEYNOTE_HASH_SHA1; + *enc = ENCODING_HEX; + *internal = INTERNAL_ENC_ASN1; + return KEYNOTE_ALGORITHM_DSA; + } + + if (!strncasecmp(SIG_DSA_SHA1_BASE64, sig, SIG_DSA_SHA1_BASE64_LEN)) + { + *hash = KEYNOTE_HASH_SHA1; + *enc = ENCODING_BASE64; + *internal = INTERNAL_ENC_ASN1; + return KEYNOTE_ALGORITHM_DSA; + } + + if (!strncasecmp(SIG_RSA_MD5_PKCS1_HEX, sig, SIG_RSA_MD5_PKCS1_HEX_LEN)) + { + *hash = KEYNOTE_HASH_MD5; + *enc = ENCODING_HEX; + *internal = INTERNAL_ENC_PKCS1; + return KEYNOTE_ALGORITHM_RSA; + } + + if (!strncasecmp(SIG_RSA_SHA1_PKCS1_HEX, sig, SIG_RSA_SHA1_PKCS1_HEX_LEN)) + { + *hash = KEYNOTE_HASH_SHA1; + *enc = ENCODING_HEX; + *internal = INTERNAL_ENC_PKCS1; + return KEYNOTE_ALGORITHM_RSA; + } + + if (!strncasecmp(SIG_RSA_MD5_PKCS1_BASE64, sig, + SIG_RSA_MD5_PKCS1_BASE64_LEN)) + { + *hash = KEYNOTE_HASH_MD5; + *enc = ENCODING_BASE64; + *internal = INTERNAL_ENC_PKCS1; + return KEYNOTE_ALGORITHM_RSA; + } + + if (!strncasecmp(SIG_RSA_SHA1_PKCS1_BASE64, sig, + SIG_RSA_SHA1_PKCS1_BASE64_LEN)) + { + *hash = KEYNOTE_HASH_SHA1; + *enc = ENCODING_BASE64; + *internal = INTERNAL_ENC_PKCS1; + return KEYNOTE_ALGORITHM_RSA; + } + + if (!strncasecmp(SIG_X509_SHA1_BASE64, sig, SIG_X509_SHA1_BASE64_LEN)) + { + *hash = KEYNOTE_HASH_SHA1; + *enc = ENCODING_BASE64; + *internal = INTERNAL_ENC_ASN1; + return KEYNOTE_ALGORITHM_X509; + } + + if (!strncasecmp(SIG_X509_SHA1_HEX, sig, SIG_X509_SHA1_HEX_LEN)) + { + *hash = KEYNOTE_HASH_SHA1; + *enc = ENCODING_HEX; + *internal = INTERNAL_ENC_ASN1; + return KEYNOTE_ALGORITHM_X509; + } + +#if 0 /* Not supported yet */ + if (!strncasecmp(SIG_ELGAMAL_SHA1_HEX, sig, SIG_ELGAMAL_SHA1_HEX_LEN)) + { + *hash = KEYNOTE_HASH_SHA1; + *enc = ENCODING_HEX; + *internal = INTERNAL_ENC_ASN1; + return KEYNOTE_ALGORITHM_ELGAMAL; + } + + if (!strncasecmp(SIG_ELGAMAL_SHA1_BASE64, sig, + SIG_ELGAMAL_SHA1_BASE64_LEN)) + { + *hash = KEYNOTE_HASH_SHA1; + *enc = ENCODING_BASE64; + *internal = INTERNAL_ENC_ASN1; + return KEYNOTE_ALGORITHM_ELGAMAL; + } +#endif /* 0 */ + +#ifdef PGPLIB + if (!strncasecmp(SIG_PGP_NATIVE, sig, SIG_PGP_NATIVE_LEN)) + { + *hash = KEYNOTE_HASH_NONE; + *enc = ENCODING_NATIVE; + *internal = INTERNAL_ENC_NATIVE; + return KEYNOTE_ALGORITHM_PGP; + } +#endif /* PGPLIB */ + + *hash = KEYNOTE_HASH_NONE; + *enc = ENCODING_NONE; + *internal = INTERNAL_ENC_NONE; + return KEYNOTE_ALGORITHM_NONE; +} +#endif /* CRYPTO || PGPLIB */ + +/* + * Map a key to an algorithm. Return algorithm number (defined in + * keynote.h), or KEYNOTE_ALGORITHM_NONE if unknown. + * This latter is also a valid algorithm (for logical tags). Also return + * in the second and third arguments the ASCII and internal encodings. + */ +int +keynote_get_key_algorithm(char *key, int *encoding, int *internalencoding) +{ +#ifdef CRYPTO + if (!strncasecmp(DSA_HEX, key, DSA_HEX_LEN)) + { + *internalencoding = INTERNAL_ENC_ASN1; + *encoding = ENCODING_HEX; + return KEYNOTE_ALGORITHM_DSA; + } + + if (!strncasecmp(DSA_BASE64, key, DSA_BASE64_LEN)) + { + *internalencoding = INTERNAL_ENC_ASN1; + *encoding = ENCODING_BASE64; + return KEYNOTE_ALGORITHM_DSA; + } + + if (!strncasecmp(RSA_PKCS1_HEX, key, RSA_PKCS1_HEX_LEN)) + { + *internalencoding = INTERNAL_ENC_PKCS1; + *encoding = ENCODING_HEX; + return KEYNOTE_ALGORITHM_RSA; + } + + if (!strncasecmp(RSA_PKCS1_BASE64, key, RSA_PKCS1_BASE64_LEN)) + { + *internalencoding = INTERNAL_ENC_PKCS1; + *encoding = ENCODING_BASE64; + return KEYNOTE_ALGORITHM_RSA; + } + + if (!strncasecmp(X509_BASE64, key, X509_BASE64_LEN)) + { + *internalencoding=INTERNAL_ENC_ASN1; + *encoding=ENCODING_BASE64; + return KEYNOTE_ALGORITHM_X509; + } + + if (!strncasecmp(X509_HEX, key, X509_HEX_LEN)) + { + *internalencoding=INTERNAL_ENC_ASN1; + *encoding=ENCODING_HEX; + return KEYNOTE_ALGORITHM_X509; + } + +#if 0 /* Not supported yet */ + if (!strncasecmp(ELGAMAL_HEX, key, ELGAMAL_HEX_LEN)) + { + *internalencoding = INTERNAL_ENC_ASN1; + *encoding = ENCODING_HEX; + return KEYNOTE_ALGORITHM_ELGAMAL; + } + + if (!strncasecmp(ELGAMAL_BASE64, key, ELGAMAL_BASE64_LEN)) + { + *internalencoding = INTERNAL_ENC_ASN1; + *encoding = ENCODING_BASE64; + return KEYNOTE_ALGORITHM_ELGAMAL; + } +#endif /* 0 */ +#endif /* CRYPTO */ + +#ifdef PGPLIB + if (!strncasecmp(PGP_NATIVE, key, PGP_NATIVE_LEN)) + { + *internalencoding = INTERNAL_ENC_NATIVE; + *encoding = ENCODING_NATIVE; + return KEYNOTE_ALGORITHM_PGP; + } +#endif /* PGPLIB */ + + if (!strncasecmp(BINARY_HEX, key, BINARY_HEX_LEN)) + { + *internalencoding = INTERNAL_ENC_NONE; + *encoding = ENCODING_HEX; + return KEYNOTE_ALGORITHM_BINARY; + } + + if (!strncasecmp(BINARY_BASE64, key, BINARY_BASE64_LEN)) + { + *internalencoding = INTERNAL_ENC_NONE; + *encoding = ENCODING_BASE64; + return KEYNOTE_ALGORITHM_BINARY; + } + + *internalencoding = INTERNAL_ENC_NONE; + *encoding = ENCODING_NONE; + return KEYNOTE_ALGORITHM_NONE; +} + +/* + * Same as keynote_get_key_algorithm(), only verify that this is + * a private key (just look at the prefix). + */ +static int +keynote_get_private_key_algorithm(char *key, int *encoding, + int *internalencoding) +{ + if (strncasecmp(KEYNOTE_PRIVATE_KEY_PREFIX, key, + KEYNOTE_PRIVATE_KEY_PREFIX_LEN)) + { + *internalencoding = INTERNAL_ENC_NONE; + *encoding = ENCODING_NONE; + return KEYNOTE_ALGORITHM_NONE; + } + + return keynote_get_key_algorithm(key + KEYNOTE_PRIVATE_KEY_PREFIX_LEN, + encoding, internalencoding); +} + +/* + * Decode a string to a key. Return 0 on success. + */ +int +kn_decode_key(struct keynote_deckey *dc, char *key, int keytype) +{ +#ifdef CRYPTO + void *kk = (void *) NULL; + X509 *px509Cert; + EVP_PKEY *pPublicKey; +#endif /* CRYPTO */ + unsigned char *ptr = (char *) NULL, *decoded = (char *) NULL; + int encoding, internalencoding, len; + + keynote_errno = 0; + if (keytype == KEYNOTE_PRIVATE_KEY) + dc->dec_algorithm = keynote_get_private_key_algorithm(key, &encoding, + &internalencoding); + else + dc->dec_algorithm = keynote_get_key_algorithm(key, &encoding, + &internalencoding); + if (dc->dec_algorithm == KEYNOTE_ALGORITHM_NONE) + { + dc->dec_key = (void *) strdup(key); + if (dc->dec_key == (void *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + return 0; + } + + key = index(key, ':'); /* Move forward, to the Encoding. We're guaranteed + * to have a ':' character, since this is a key */ + key++; + + /* Remove ASCII encoding */ + switch (encoding) + { + case ENCODING_NONE: + break; + + case ENCODING_HEX: + len = strlen(key) / 2; + if (kn_decode_hex(key, (char **) &decoded) != 0) + return -1; + ptr = decoded; + break; + + case ENCODING_BASE64: + len = strlen(key); + if (len % 4) /* Base64 encoding must be a multiple of 4 */ + { + keynote_errno = ERROR_SYNTAX; + return -1; + } + + len = 3 * (len / 4); + decoded = (unsigned char *) calloc(len, sizeof(unsigned char)); + ptr = decoded; + if (decoded == (unsigned char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + if ((len = kn_decode_base64(key, decoded, len)) == -1) + return -1; + break; + + case ENCODING_NATIVE: + decoded = strdup(key); + len = strlen(key); + if (decoded == (unsigned char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + ptr = decoded; + break; + + default: + keynote_errno = ERROR_SYNTAX; + return -1; + } + +#ifdef CRYPTO + /* DSA-HEX */ + if ((dc->dec_algorithm == KEYNOTE_ALGORITHM_DSA) && + (internalencoding == INTERNAL_ENC_ASN1)) + { + dc->dec_key = DSA_new(); + if (dc->dec_key == (DSA *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + kk = dc->dec_key; + if (keytype == KEYNOTE_PRIVATE_KEY) + { + if (d2i_DSAPrivateKey((DSA **) &kk, &decoded, len) == (DSA *) NULL) + { + if (ptr != (unsigned char *) NULL) + free(ptr); + DSA_free(kk); + keynote_errno = ERROR_SYNTAX; /* Could be a memory error */ + return -1; + } + } + else + { + if (d2i_DSAPublicKey((DSA **) &kk, &decoded, len) == (DSA *) NULL) + { + if (ptr != (unsigned char *) NULL) + free(ptr); + DSA_free(kk); + keynote_errno = ERROR_SYNTAX; /* Could be a memory error */ + return -1; + } + } + + if (ptr != (unsigned char *) NULL) + free(ptr); + + return 0; + } + + /* RSA-PKCS1-HEX */ + if ((dc->dec_algorithm == KEYNOTE_ALGORITHM_RSA) && + (internalencoding == INTERNAL_ENC_PKCS1)) + { + dc->dec_key = RSA_new(); + if (dc->dec_key == (RSA *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + kk = dc->dec_key; + if (keytype == KEYNOTE_PRIVATE_KEY) + { + if (d2i_RSAPrivateKey((RSA **) &kk, &decoded, len) == (RSA *) NULL) + { + if (ptr != (unsigned char *) NULL) + free(ptr); + RSA_free(kk); + keynote_errno = ERROR_SYNTAX; /* Could be a memory error */ + return -1; + } + } + else + { + if (d2i_RSAPublicKey((RSA **) &kk, &decoded, len) == (RSA *) NULL) + { + if (ptr != (unsigned char *) NULL) + free(ptr); + RSA_free(kk); + keynote_errno = ERROR_SYNTAX; /* Could be a memory error */ + return -1; + } + } + + if (ptr != (unsigned char *) NULL) + free(ptr); + + return 0; + } + + /* X509 Cert */ + if ((dc->dec_algorithm == KEYNOTE_ALGORITHM_X509) && + (internalencoding == INTERNAL_ENC_ASN1) && + (keytype == KEYNOTE_PUBLIC_KEY)) + { + if ((px509Cert = X509_new()) == (X509 *) NULL) + { + if (ptr) + free(ptr); + keynote_errno = ERROR_MEMORY; + return -1; + } + + if(d2i_X509(&px509Cert, &decoded, len) == NULL) + { + if (ptr) + free(ptr); + X509_free(px509Cert); + keynote_errno = ERROR_SYNTAX; + return -1; + } + + if ((pPublicKey = X509_get_pubkey(px509Cert)) == (EVP_PKEY *) NULL) + { + if (ptr) + free(ptr); + X509_free(px509Cert); + keynote_errno = ERROR_SYNTAX; + return -1; + } + + /* RSA-specific */ + dc->dec_key = pPublicKey->pkey.rsa; + + if(ptr) + free(ptr); + return 0; + } +#endif /* CRYPTO */ + + /* BINARY keys */ + if ((dc->dec_algorithm == KEYNOTE_ALGORITHM_BINARY) && + (internalencoding == INTERNAL_ENC_NONE)) + { + dc->dec_key = (void *) calloc(1, sizeof(struct keynote_binary)); + if (dc->dec_key == (struct keynote_binary *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + ((struct keynote_binary *) dc->dec_key)->bn_key = decoded; + ((struct keynote_binary *) dc->dec_key)->bn_len = len; + return RESULT_TRUE; + } + + /* Add support for more algorithms here */ + + if (ptr != (unsigned char *) NULL) + free(ptr); + + /* This shouldn't ever be reached really */ + keynote_errno = ERROR_SYNTAX; + return -1; +} + +/* + * Compare two keys for equality. Return RESULT_TRUE if equal, + * RESULT_FALSE otherwise. + */ +int +keynote_keycompare(void *key1, void *key2, int algorithm) +{ +#ifdef CRYPTO + DSA *p1, *p2; + RSA *p3, *p4; +#endif /* CRYPTO */ + struct keynote_binary *bn1, *bn2; + + if ((key1 == (void *) NULL) || + (key2 == (void *) NULL)) + return RESULT_FALSE; + + switch (algorithm) + { + case KEYNOTE_ALGORITHM_NONE: + if (!strcmp((char *) key1, (char *) key2)) + return RESULT_TRUE; + else + return RESULT_FALSE; + + case KEYNOTE_ALGORITHM_DSA: +#ifdef CRYPTO + p1 = (DSA *) key1; + p2 = (DSA *) key2; + if (!BN_cmp(p1->p, p2->p) && + !BN_cmp(p1->q, p2->q) && + !BN_cmp(p1->g, p2->g) && + !BN_cmp(p1->pub_key, p2->pub_key)) + return RESULT_TRUE; + else + return RESULT_FALSE; +#else /* CRYPTO */ + return RESULT_FALSE; +#endif /* CRYPTO */ + + case KEYNOTE_ALGORITHM_RSA: +#ifdef CRYPTO + p3 = (RSA *) key1; + p4 = (RSA *) key2; + if (!BN_cmp(p3->n, p4->n) && + !BN_cmp(p3->e, p4->e)) + return RESULT_TRUE; + else + return RESULT_FALSE; +#else /* CRYPTO */ + return RETURN_FALSE; +#endif /* CRYPTO */ + + case KEYNOTE_ALGORITHM_ELGAMAL: + /* Not supported yet */ + return RESULT_FALSE; + + case KEYNOTE_ALGORITHM_PGP: + /* Not supported yet */ + return RESULT_FALSE; + + case KEYNOTE_ALGORITHM_BINARY: + bn1 = (struct keynote_binary *) key1; + bn2 = (struct keynote_binary *) key2; + if ((bn1->bn_len == bn2->bn_len) && + !memcmp(bn1->bn_key, bn2->bn_key, bn1->bn_len)) + return RESULT_TRUE; + else + return RESULT_FALSE; + + default: + return RESULT_FALSE; + } +} + +/* + * Verify the signature on an assertion; return SIGRESULT_TRUE is + * success, SIGRESULT_FALSE otherwise. + */ +int +keynote_sigverify_assertion(struct assertion *as) +{ +#if defined(CRYPTO) || defined(PGPLIB) + int hashtype, enc, intenc, alg = KEYNOTE_ALGORITHM_NONE, hashlen = 0; + unsigned char *sig, *decoded = (char *) NULL, *ptr; +#ifdef CRYPTO + unsigned char res2[20]; + SHA_CTX shscontext; + MD5_CTX md5context; + DSA *dsa; + RSA *rsa; + int len; +#endif /* CRYPTO */ + if ((as->as_signature == (char *) NULL) || + (as->as_startofsignature == (char *) NULL) || + (as->as_allbutsignature == (char *) NULL) || + (as->as_allbutsignature - as->as_startofsignature <= 0)) + return SIGRESULT_FALSE; + + alg = keynote_get_sig_algorithm(as->as_signature, &hashtype, &enc, + &intenc); + if (alg == KEYNOTE_ALGORITHM_NONE) + return SIGRESULT_FALSE; + + /* Check for matching algorithms */ + if (alg != as->as_signeralgorithm) + return SIGRESULT_FALSE; + + sig = index(as->as_signature, ':'); /* Move forward to the Encoding. We + * are guaranteed to have a ':' + * character, since this is a valid + * signature */ + sig++; + + switch (hashtype) + { + case KEYNOTE_HASH_SHA1: +#ifdef CRYPTO + hashlen = 20; + memset(res2, 0, hashlen); + SHA1_Init(&shscontext); + SHA1_Update(&shscontext, as->as_startofsignature, + as->as_allbutsignature - as->as_startofsignature); + SHA1_Update(&shscontext, as->as_signature, + (char *) sig - as->as_signature); + SHA1_Final(res2, &shscontext); +#endif /* CRYPTO */ + break; + + case KEYNOTE_HASH_MD5: +#ifdef CRYPTO + hashlen = 16; + memset(res2, 0, hashlen); + MD5_Init(&md5context); + MD5_Update(&md5context, as->as_startofsignature, + as->as_allbutsignature - as->as_startofsignature); + MD5_Update(&md5context, as->as_signature, + (char *) sig - as->as_signature); + MD5_Final(res2, &md5context); +#endif /* CRYPTO */ + break; + + case KEYNOTE_HASH_NONE: + break; + } + + /* Remove ASCII encoding */ + switch (enc) + { + case ENCODING_NONE: + ptr = (char *) NULL; + break; + + case ENCODING_HEX: + len = strlen(sig) / 2; + if (kn_decode_hex(sig, (char **) &decoded) != 0) + return -1; + ptr = decoded; + break; + + case ENCODING_BASE64: + len = strlen(sig); + if (len % 4) /* Base64 encoding must be a multiple of 4 */ + { + keynote_errno = ERROR_SYNTAX; + return -1; + } + + len = 3 * (len / 4); + decoded = (unsigned char *) calloc(len, sizeof(unsigned char)); + ptr = decoded; + if (decoded == (unsigned char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + + len = kn_decode_base64(sig, decoded, len); + if ((len == -1) || (len == 0) || (len == 1)) + return -1; + break; + + case ENCODING_NATIVE: + decoded = (unsigned char *) strdup(sig); + if (decoded == (unsigned char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return -1; + } + ptr = decoded; + break; + + default: + keynote_errno = ERROR_SYNTAX; + return -1; + } + + /* DSA */ + if ((alg == KEYNOTE_ALGORITHM_DSA) && (intenc == INTERNAL_ENC_ASN1)) + { + dsa = (DSA *) as->as_authorizer; + if (DSA_verify(0, res2, hashlen, decoded, len, dsa) == 1) + { + if (ptr != (unsigned char *) NULL) + free(ptr); + return SIGRESULT_TRUE; + } + } + else /* RSA */ + if ((alg == KEYNOTE_ALGORITHM_RSA) && (intenc == INTERNAL_ENC_PKCS1)) + { + rsa = (RSA *) as->as_authorizer; + if (RSA_verify_ASN1_OCTET_STRING(RSA_PKCS1_PADDING, res2, hashlen, + decoded, len, rsa) == 1) + { + if (ptr != (unsigned char *) NULL) + free(ptr); + return SIGRESULT_TRUE; + } + } + else + if ((alg == KEYNOTE_ALGORITHM_X509) && (intenc == INTERNAL_ENC_ASN1)) + { + /* RSA-specific */ + rsa = (RSA *) as->as_authorizer; + if (RSA_verify(NID_shaWithRSAEncryption, res2, hashlen, decoded, + len, rsa) == 1) + { + if (ptr != (unsigned char *) NULL) + free(ptr); + return SIGRESULT_TRUE; + } + } + + /* Handle more algorithms here */ + + if (ptr != (unsigned char *) NULL) + free(ptr); +#endif /* CRYPTO || PGPLIB */ + + return SIGRESULT_FALSE; +} + +/* + * Sign an assertion. + */ +static char * +keynote_sign_assertion(struct assertion *as, char *sigalg, void *key, + int keyalg, int verifyflag) +{ +#if defined(CRYPTO) || defined(PGPLIB) +#ifdef CRYPTO + int slen, i, hashlen = 0, hashtype, alg, encoding, internalenc; + unsigned char *sig = (char *) NULL, *finalbuf = (char *) NULL; + unsigned char res2[LARGEST_HASH_SIZE], *sbuf = (char *) NULL; + BIO *biokey = (BIO *) NULL; + DSA *dsa = (DSA *) NULL; + RSA *rsa = (RSA *) NULL; + SHA_CTX shscontext; + MD5_CTX md5context; +#endif /* CRYPTO */ + + if ((as->as_signature_string_s == (char *) NULL) || + (as->as_startofsignature == (char *) NULL) || + (as->as_allbutsignature == (char *) NULL) || + (as->as_allbutsignature - as->as_startofsignature <= 0) || + (as->as_authorizer == (void *) NULL) || + (key == (void *) NULL) || + (as->as_signeralgorithm == KEYNOTE_ALGORITHM_NONE)) + { + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + + alg = keynote_get_sig_algorithm(sigalg, &hashtype, &encoding, + &internalenc); + if ((alg != as->as_signeralgorithm) || (alg != keyalg)) + { + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + + sig = index(sigalg, ':'); + if (sig == (unsigned char *) NULL) + { + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + + sig++; + + switch (hashtype) + { + case KEYNOTE_HASH_SHA1: +#ifdef CRYPTO + hashlen = 20; + memset(res2, 0, hashlen); + SHA1_Init(&shscontext); + SHA1_Update(&shscontext, as->as_startofsignature, + as->as_allbutsignature - as->as_startofsignature); + SHA1_Update(&shscontext, sigalg, (char *) sig - sigalg); + SHA1_Final(res2, &shscontext); +#endif /* CRYPTO */ + break; + + case KEYNOTE_HASH_MD5: +#ifdef CRYPTO + hashlen = 16; + memset(res2, 0, hashlen); + MD5_Init(&md5context); + MD5_Update(&md5context, as->as_startofsignature, + as->as_allbutsignature - as->as_startofsignature); + MD5_Update(&md5context, sigalg, (char *) sig - sigalg); + MD5_Final(res2, &md5context); +#endif /* CRYPTO */ + break; + + case KEYNOTE_HASH_NONE: + break; + } + +#ifdef CRYPTO + if ((alg == KEYNOTE_ALGORITHM_DSA) && + (hashtype == KEYNOTE_HASH_SHA1) && + (internalenc == INTERNAL_ENC_ASN1) && + ((encoding == ENCODING_HEX) || (encoding == ENCODING_BASE64))) + { + dsa = (DSA *) key; + sbuf = (unsigned char *) calloc(DSA_size(dsa), sizeof(unsigned char)); + if (sbuf == (unsigned char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return (char *) NULL; + } + + if (DSA_sign(0, res2, hashlen, sbuf, &slen, dsa) <= 0) + { + free(sbuf); + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + } + else + if ((alg == KEYNOTE_ALGORITHM_RSA) && + ((hashtype == KEYNOTE_HASH_SHA1) || + (hashtype == KEYNOTE_HASH_MD5)) && + (internalenc == INTERNAL_ENC_PKCS1) && + ((encoding == ENCODING_HEX) || (encoding == ENCODING_BASE64))) + { + rsa = (RSA *) key; + sbuf = (unsigned char *) calloc(RSA_size(rsa), + sizeof(unsigned char)); + if (sbuf == (unsigned char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return (char *) NULL; + } + + if (RSA_sign_ASN1_OCTET_STRING(RSA_PKCS1_PADDING, res2, hashlen, + sbuf, &slen, rsa) <= 0) + { + free(sbuf); + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + } + else + if ((alg == KEYNOTE_ALGORITHM_X509) && + (hashtype == KEYNOTE_HASH_SHA1) && + (internalenc == INTERNAL_ENC_ASN1)) + { + if ((biokey = BIO_new(BIO_s_mem())) == (BIO *) NULL) + { + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + + if (BIO_write(biokey, key, strlen(key) + 1) <= 0) + { + BIO_free(biokey); + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + + /* RSA-specific */ + rsa = (RSA *) PEM_read_bio_RSAPrivateKey(biokey, NULL, NULL); + if (rsa == (RSA *) NULL) + { + BIO_free(biokey); + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + + sbuf = calloc(RSA_size(rsa), sizeof(char)); + if (sbuf == (unsigned char *) NULL) + { + BIO_free(biokey); + RSA_free(rsa); + keynote_errno = ERROR_MEMORY; + return (char *) NULL; + } + + if (RSA_sign(NID_shaWithRSAEncryption, res2, hashlen, sbuf, &slen, + rsa) <= 0) + { + BIO_free(biokey); + RSA_free(rsa); + free(sbuf); + keynote_errno = ERROR_SIGN_FAILURE; + return NULL; + } + + BIO_free(biokey); + RSA_free(rsa); + } + else /* Other algorithms here */ + { + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + + /* ASCII encoding */ + switch (encoding) + { + case ENCODING_HEX: + i = kn_encode_hex(sbuf, (char **) &finalbuf, slen); + free(sbuf); + if (i != 0) + return (char *) NULL; + break; + + case ENCODING_BASE64: + finalbuf = (unsigned char *) calloc(2 * slen, + sizeof(unsigned char)); + if (finalbuf == (unsigned char *) NULL) + { + keynote_errno = ERROR_MEMORY; + free(sbuf); + return (char *) NULL; + } + + if ((slen = kn_encode_base64(sbuf, slen, finalbuf, + 2 * slen)) == -1) + { + free(sbuf); + return (char *) NULL; + } + break; + + default: + free(sbuf); + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + + /* Replace as->as_signature */ + as->as_signature = (char *) calloc(strlen(sigalg) + + strlen(finalbuf) + 1, sizeof(char)); + if (as->as_signature == (char *) NULL) + { + free(finalbuf); + keynote_errno = ERROR_MEMORY; + return (char *) NULL; + } + + /* Concatenate algorithm name and signature value */ + sprintf(as->as_signature, "%s%s", sigalg, finalbuf); + free(finalbuf); + finalbuf = as->as_signature; + + /* Verify the newly-created signature if requested */ + if (verifyflag) + { + /* Do the signature verification */ + if (keynote_sigverify_assertion(as) != SIGRESULT_TRUE) + { + as->as_signature = (char *) NULL; + free(finalbuf); + if (keynote_errno == 0) + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + + as->as_signature = (char *) NULL; + } + else + as->as_signature = (char *) NULL; + + /* Everything ok */ + return (char *) finalbuf; +#endif /* CRYPTO */ +#else /* CRYPTO || PGPLIB */ + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; +#endif /* CRYPTO || PGPLIB */ +} + +/* + * Verify the signature on an assertion. + */ +int +kn_verify_assertion(char *buf, int len) +{ + struct assertion *as; + int res; + + keynote_errno = 0; + as = keynote_parse_assertion(buf, len, ASSERT_FLAG_SIGVER); + if (as == (struct assertion *) NULL) + return -1; + + res = keynote_sigverify_assertion(as); + keynote_free_assertion(as); + return res; +} + +/* + * Produce the signature for an assertion. + */ +char * +kn_sign_assertion(char *buf, int buflen, char *key, char *sigalg, int vflag) +{ + int i, alg, hashtype, encoding, internalenc; + struct keynote_deckey dc; + struct assertion *as; + char *s, *sig; + + keynote_errno = 0; + s = (char *) NULL; + + if ((sigalg == (char *) NULL) || (buf == (char *) NULL) || + (key == (char *) NULL)) + { + keynote_errno = ERROR_NOTFOUND; + return (char *) NULL; + } + + if (sigalg[strlen(sigalg) - 1] != ':') + { + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + + /* We're using a different format for X509 private keys, so... */ + alg = keynote_get_sig_algorithm(sigalg, &hashtype, &encoding, + &internalenc); + if (alg != KEYNOTE_ALGORITHM_X509) + { + /* Parse the private key */ + s = keynote_get_private_key(key); + if (s == (char *) NULL) + return (char *) NULL; + + /* Decode private key */ + i = kn_decode_key(&dc, s, KEYNOTE_PRIVATE_KEY); + if (i == -1) + { + free(s); + return (char *) NULL; + } + } + else /* X509 private key */ + { + dc.dec_key = key; + dc.dec_algorithm = alg; + } + + as = keynote_parse_assertion(buf, buflen, ASSERT_FLAG_SIGGEN); + if (as == (struct assertion *) NULL) + { + if (alg != KEYNOTE_ALGORITHM_X509) + { + keynote_free_key(dc.dec_key, dc.dec_algorithm); + free(s); + } + return (char *) NULL; + } + + sig = keynote_sign_assertion(as, sigalg, dc.dec_key, dc.dec_algorithm, + vflag); + if (alg != KEYNOTE_ALGORITHM_X509) + keynote_free_key(dc.dec_key, dc.dec_algorithm); + keynote_free_assertion(as); + if (s != (char *) NULL) + free(s); + return sig; +} + +/* + * ASCII-encode a key. + */ +char * +kn_encode_key(struct keynote_deckey *dc, int iencoding, + int encoding, int keytype) +{ +#ifdef CRYPTO + char *foo, *ptr; + DSA *dsa; + RSA *rsa; + int i; +#endif /* CRYPTO */ + struct keynote_binary *bn; + char *s; + + keynote_errno = 0; + if ((dc == (struct keynote_deckey *) NULL) || + (dc->dec_key == (void *) NULL)) + { + keynote_errno = ERROR_NOTFOUND; + return (char *) NULL; + } + +#ifdef CRYPTO + /* DSA keys */ + if ((dc->dec_algorithm == KEYNOTE_ALGORITHM_DSA) && + (iencoding == INTERNAL_ENC_ASN1) && + ((encoding == ENCODING_HEX) || (encoding == ENCODING_BASE64))) + { + dsa = (DSA *) dc->dec_key; + if (keytype == KEYNOTE_PUBLIC_KEY) + i = i2d_DSAPublicKey(dsa, NULL); + else + i = i2d_DSAPrivateKey(dsa, NULL); + + if (i <= 0) + { + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + + ptr = foo = (char *) calloc(i, sizeof(char)); + if (foo == (char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return (char *) NULL; + } + + dsa->write_params = 1; + if (keytype == KEYNOTE_PUBLIC_KEY) + i2d_DSAPublicKey(dsa, (unsigned char **) &foo); + else + i2d_DSAPrivateKey(dsa, (unsigned char **) &foo); + + if (encoding == ENCODING_HEX) + { + if (kn_encode_hex(ptr, &s, i) != 0) + { + free(ptr); + return (char *) NULL; + } + + free(ptr); + return s; + } + else + if (encoding == ENCODING_BASE64) + { + s = (char *) calloc(2 * i, sizeof(char)); + if (s == (char *) NULL) + { + free(ptr); + keynote_errno = ERROR_MEMORY; + return (char *) NULL; + } + + if (kn_encode_base64(ptr, i, s, 2 * i) == -1) + { + free(s); + free(ptr); + return (char *) NULL; + } + + free(ptr); + return s; + } + } + + /* RSA keys */ + if ((dc->dec_algorithm == KEYNOTE_ALGORITHM_RSA) && + (iencoding == INTERNAL_ENC_PKCS1) && + ((encoding == ENCODING_HEX) || (encoding == ENCODING_BASE64))) + { + rsa = (RSA *) dc->dec_key; + if (keytype == KEYNOTE_PUBLIC_KEY) + i = i2d_RSAPublicKey(rsa, NULL); + else + i = i2d_RSAPrivateKey(rsa, NULL); + + if (i <= 0) + { + keynote_errno = ERROR_SYNTAX; + return (char *) NULL; + } + + ptr = foo = (char *) calloc(i, sizeof(char)); + if (foo == (char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return (char *) NULL; + } + + if (keytype == KEYNOTE_PUBLIC_KEY) + i2d_RSAPublicKey(rsa, (unsigned char **) &foo); + else + i2d_RSAPrivateKey(rsa, (unsigned char **) &foo); + + if (encoding == ENCODING_HEX) + { + if (kn_encode_hex(ptr, &s, i) != 0) + { + free(ptr); + return (char *) NULL; + } + + free(ptr); + return s; + } + else + if (encoding == ENCODING_BASE64) + { + s = (char *) calloc(2 * i, sizeof(char)); + if (s == (char *) NULL) + { + free(ptr); + keynote_errno = ERROR_MEMORY; + return (char *) NULL; + } + + if (kn_encode_base64(ptr, i, s, 2 * i) == -1) + { + free(s); + free(ptr); + return (char *) NULL; + } + + free(ptr); + return s; + } + } +#endif /* CRYPTO */ + + /* BINARY keys */ + if ((dc->dec_algorithm == KEYNOTE_ALGORITHM_BINARY) && + (iencoding == INTERNAL_ENC_NONE) && + ((encoding == ENCODING_HEX) || (encoding == ENCODING_BASE64))) + { + bn = (struct keynote_binary *) dc->dec_key; + + if (encoding == ENCODING_HEX) + { + if (kn_encode_hex(bn->bn_key, &s, bn->bn_len) != 0) + return (char *) NULL; + + return s; + } + else + if (encoding == ENCODING_BASE64) + { + s = (char *) calloc(2 * bn->bn_len, sizeof(char)); + if (s == (char *) NULL) + { + keynote_errno = ERROR_MEMORY; + return (char *) NULL; + } + + if (kn_encode_base64(bn->bn_key, bn->bn_len, s, + 2 * bn->bn_len) == -1) + { + free(s); + return (char *) NULL; + } + + return s; + } + } + + keynote_errno = ERROR_NOTFOUND; + return (char *) NULL; +} diff --git a/lib/libkeynote/signature.h b/lib/libkeynote/signature.h new file mode 100644 index 00000000000..d2f8faae092 --- /dev/null +++ b/lib/libkeynote/signature.h @@ -0,0 +1,64 @@ +/* $OpenBSD: signature.h,v 1.1 1999/05/23 22:11:06 angelos 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 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. + */ + +#ifndef __SIGNATURE_H__ +#define __SIGNATURE_H__ + +#define KEYNOTE_HASH_NONE 0 +#define KEYNOTE_HASH_SHA1 1 +#define KEYNOTE_HASH_MD5 2 + +#define DSA_HEX "dsa-hex:" +#define DSA_HEX_LEN strlen(DSA_HEX) +#define DSA_BASE64 "dsa-base64:" +#define DSA_BASE64_LEN strlen(DSA_BASE64) +#define RSA_PKCS1_HEX "rsa-hex:" +#define RSA_PKCS1_HEX_LEN strlen(RSA_PKCS1_HEX) +#define RSA_PKCS1_BASE64 "rsa-base64:" +#define RSA_PKCS1_BASE64_LEN strlen(RSA_PKCS1_BASE64) +#define ELGAMAL_HEX "elgamal-hex:" +#define ELGAMAL_HEX_LEN strlen(ELGAMAL_HEX) +#define ELGAMAL_BASE64 "elgamal-base64:" +#define ELGAMAL_BASE64_LEN strlen(ELGAMAL_BASE64) +#define PGP_NATIVE "pgp:" +#define PGP_NATIVE_LEN strlen(PGP_NATIVE) +#define BINARY_BASE64 "binary-base64:" +#define BINARY_BASE64_LEN strlen(BINARY_BASE64) +#define BINARY_HEX "binary-hex:" +#define BINARY_HEX_LEN strlen(BINARY_HEX) +#define X509_BASE64 "x509-base64:" +#define X509_BASE64_LEN strlen(X509_BASE64) +#define X509_HEX "x509-hex:" +#define X509_HEX_LEN strlen(X509_HEX) + +#define KEYNOTE_PRIVATE_KEY_PREFIX "private-" +#define KEYNOTE_PRIVATE_KEY_PREFIX_LEN strlen(KEYNOTE_PRIVATE_KEY_PREFIX) + +#define LARGEST_HASH_SIZE 20 /* In bytes, length of SHA1 hash */ + +#include "assertion.h" + +int keynote_get_key_algorithm(char *, int *, int *); +int keynote_sigverify_assertion(struct assertion *); +int keynote_keycompare(void *, void *, int); +void keynote_free_key(void *, int); +#endif /* __SIGNATURE_H__ */ diff --git a/lib/libkeynote/testsuite/auth1 b/lib/libkeynote/testsuite/auth1 new file mode 100644 index 00000000000..b9383732201 --- /dev/null +++ b/lib/libkeynote/testsuite/auth1 @@ -0,0 +1 @@ +KeyC # Some key diff --git a/lib/libkeynote/testsuite/auth2 b/lib/libkeynote/testsuite/auth2 new file mode 100644 index 00000000000..bb700d8069e --- /dev/null +++ b/lib/libkeynote/testsuite/auth2 @@ -0,0 +1 @@ +KeyE diff --git a/lib/libkeynote/testsuite/auth3 b/lib/libkeynote/testsuite/auth3 new file mode 100644 index 00000000000..e79e63561ec --- /dev/null +++ b/lib/libkeynote/testsuite/auth3 @@ -0,0 +1 @@ +Key12 diff --git a/lib/libkeynote/testsuite/auth4 b/lib/libkeynote/testsuite/auth4 new file mode 100644 index 00000000000..776d0fb609d --- /dev/null +++ b/lib/libkeynote/testsuite/auth4 @@ -0,0 +1,11 @@ + # Comments are allowed + "dsa-hex:3081df02410082b9dc3a9671aa7f7595f15987a9\ + f9a9d0fbe23540d193c342469fc7076fb71a5ef0abdd67e5f\ + ad884862ae62b1fcf5a89c99e060b16b80b2c0b07ffb9dc66\ + 79024100c9d63ddc7384f4c2ed0f0c8c40464faded1ae5636\ + 4e48c7c199ab063e29834b54a9d2f6f37b17b2e62f54ce034\ + 659c80968a46a22ca0414d417b5991e4b74727021500e3ec7\ + 2171b4c535b0d967b0ce3eb5e7dbfd8d96302403097f8ce26\ + 22ff8a4bcb8f74df518a92aa22efd6439415ed657253f5748\ + 2eda35835b8976720b60690f7a6206529c69bf89af4acd0a9\ + 017e541a12a5c366619f" diff --git a/lib/libkeynote/testsuite/test-assertion1 b/lib/libkeynote/testsuite/test-assertion1 new file mode 100644 index 00000000000..2e9b9c167a1 --- /dev/null +++ b/lib/libkeynote/testsuite/test-assertion1 @@ -0,0 +1,45 @@ +keynote-version: 2 # some comment +comment: The weird looking string test in the conditions field is for + verifying correctness of string grammar +# comment inside comment field, no problem +#authorizer: $$$$foo # Don't try this at home +authorizer: "POLICY" +licensees: (MYKEY) && ((("Key3") || "Key4") && (MYKEY)) || TWOKEY +local-constants: MYKEY = "Key3" +# we can put a comment here + TWOKEY = "dsa-hex:3081de02402ae5e2d8c12fbaec4934dd5a98cbe39159\ + f1b8d02143a5e3d07c96c0acedef73d508a54286bb19b53cd2b7\ + bd0beca47b12ec75ddd7a7aeece8b724fbf940ca220241008cfe\ + 2799793dc5eef44cc78228d2a42e76246326e6f442d7c14eb705\ + 3e48d49a001350177e7d320d762d87f10ecbeceffb12b359e4c0\ + f827e05b34ef336823710215008773db9f8a9d42e7ad53c023d1\ + 61dda43ae081a9024069f506a956d69c8a0a2ab6d6a888f57dd0\ + 6593f537135b6d3c2bc928634f7e5e03b12c9fbac7ce4a6ce708\ + b63bdcda576e5eeecfb68930a5c3ca8df71d84fd0e" +conditions: app_domain == "testing" -> +# gratuitous comment + { + 1 / 0 == 1 -> "true"; # runtime exception + true -> "false"; + request == "whatever" -> "false"; + TWOKEY == "dsa-hex:3081de02402ae5e2d8c12fbaec4934dd5a98cbe39159\ + f1b8d02143a5e3d07c96c0acedef73d508a54286bb19b53cd2b7\ + bd0beca47b12ec75ddd7a7aeece8b724fbf940ca220241008cfe\ + 2799793dc5eef44cc78228d2a42e76246326e6f442d7c14eb705\ + 3e48d49a001350177e7d320d762d87f10ecbeceffb12b359e4c0\ + f827e05b34ef336823710215008773db9f8a9d42e7ad53c023d1\ + 61dda43ae081a9024069f506a956d69c8a0a2ab6d6a888f57dd0\ + 6593f537135b6d3c2bc928634f7e5e03b12c9fbac7ce4a6ce708\ + b63bdcda576e5eeecfb68930a5c3ca8df71d84fd0e" && + @(foo) == @foo && + "this string contains a newline\n\ + \ followed by one space." == + "this\ string\ contains\ a\ newline\n\ foll\ + owed\ by\ one\ space\." && + "this string contains a newline\n\ \ + followed by one space." == + "this string contains a newline\012\040followed by one space." && + request == "test" -> "true"; # this is another comment + request == "whatever3" -> "true"; + request == "test" -> "fa" . "lse"; + }; diff --git a/lib/libkeynote/testsuite/test-assertion2 b/lib/libkeynote/testsuite/test-assertion2 new file mode 100644 index 00000000000..66e6d8714ab --- /dev/null +++ b/lib/libkeynote/testsuite/test-assertion2 @@ -0,0 +1,7 @@ +keynote-version: 2 +Comment: this policy will be false +authorizer: "POLICY" +licensees: "Key10" +conditions: app_domain == "testing" && request == "test" && + variable == "no" -> "true"; + true -> "false"; diff --git a/lib/libkeynote/testsuite/test-assertion3 b/lib/libkeynote/testsuite/test-assertion3 new file mode 100644 index 00000000000..ac7a200e9a0 --- /dev/null +++ b/lib/libkeynote/testsuite/test-assertion3 @@ -0,0 +1,8 @@ +keynote-version: 2 +authorizer: "Key3" +# This assertion will return "true" +licensees: 2-of("Key5", "Key6", "Key7", "KeyE") +conditions: app_domain == "testing" && request == "test" && + foo == "bar" && $("foo") == "bar" && + $(foo) == "xyz" && $foo == "xyz" && $$foo == "qua" && + variable == "yes" -> "true"; diff --git a/lib/libkeynote/testsuite/test-assertion4 b/lib/libkeynote/testsuite/test-assertion4 new file mode 100644 index 00000000000..b379203d5ca --- /dev/null +++ b/lib/libkeynote/testsuite/test-assertion4 @@ -0,0 +1,5 @@ +keynote-version: 2 +authorizer: "Key4" +licensees: "Key8" +conditions: app_domain == "testing" && request == "test" && + variable == "yes" -> "true"; diff --git a/lib/libkeynote/testsuite/test-assertion5 b/lib/libkeynote/testsuite/test-assertion5 new file mode 100644 index 00000000000..7ca4cad5b8c --- /dev/null +++ b/lib/libkeynote/testsuite/test-assertion5 @@ -0,0 +1,12 @@ +keynote-version: 2 +authorizer: "Key5" +licensees: "Key6" +# this assertion will evaluate to "false" because of the conditions field value +conditions: app_domain == "testing" && request == "test" && + variable == "no" -> "true"; # this clause won't match + app_domain == "testing" -> { + app_domain == "testing" -> "false"; # this will match + var == "no" -> { foo == "bar" -> + "false"; # this won't match + }; + }; diff --git a/lib/libkeynote/testsuite/test-assertion6 b/lib/libkeynote/testsuite/test-assertion6 new file mode 100644 index 00000000000..a1b8a59ebc4 --- /dev/null +++ b/lib/libkeynote/testsuite/test-assertion6 @@ -0,0 +1,7 @@ +keynote-version: 2 +authorizer: "Key6" +licensees: "KeyB" # this assertion will return "false" because +# of the licensees field +conditions: app_domain == "testing" && request == "test" && + (variable == "no" || variable == "yes") -> "true"; +# however, the conditions field will return "true" diff --git a/lib/libkeynote/testsuite/test-assertion7 b/lib/libkeynote/testsuite/test-assertion7 new file mode 100644 index 00000000000..1860d9117dc --- /dev/null +++ b/lib/libkeynote/testsuite/test-assertion7 @@ -0,0 +1,24 @@ +comment: this assertion will return "true", since "KeyC" is among the + action authorizers and the conditions field will return "true" +authorizer: "Key6" +licensees: "KeyC" +conditions: app_domain == "testing" && request == "test" && + _VALUES == "false,maybe,probably,true" && _VALUES != "foo" && + _ACTION_AUTHORIZERS ~= "(.*,)KeyE,.*" && + @ _0 == 2^2/4 && # minor regexp testing here + _ACTION_AUTHORIZERS != "foo" && + # now let's test precedence rules + $foo . bar == ($foo) . bar && + $foo . bar != $(foo . bar) && + $foo == $(foo) && + $ ("fo" . "o") == foo && + $ "fo" . "o" != $"foo" && + -1 * 12 == -12 && + 12 * 1 - 1 != 0 && + @(foo . bar) == 0 && + $((("foo"))) == foo && + @foo + 1 == @bar + 1 && # 1 == 1 + @foo == 0 && + variable == "yes" -> "true"; + + @foo / @foo == @foo -> "maybe"; diff --git a/lib/libkeynote/testsuite/test-env b/lib/libkeynote/testsuite/test-env new file mode 100644 index 00000000000..9151c36da66 --- /dev/null +++ b/lib/libkeynote/testsuite/test-env @@ -0,0 +1,10 @@ +# Comments are allowed +app_domain = "test\ + in\g" +request = test +variable = yes +weirduse = POLICY +foo = bar +bar = xyz +xyz = qua +qua = weirduse |