diff options
-rw-r--r-- | lib/libkeynote/Makefile.in | 9 | ||||
-rw-r--r-- | lib/libkeynote/README | 5 | ||||
-rw-r--r-- | lib/libkeynote/TODO | 3 | ||||
-rw-r--r-- | lib/libkeynote/keynote-sigver.c | 18 | ||||
-rw-r--r-- | lib/libkeynote/keynote.3 | 12 | ||||
-rw-r--r-- | lib/libkeynote/sample-app.c | 544 |
6 files changed, 577 insertions, 14 deletions
diff --git a/lib/libkeynote/Makefile.in b/lib/libkeynote/Makefile.in index 14f97cbfce3..a07f101b665 100644 --- a/lib/libkeynote/Makefile.in +++ b/lib/libkeynote/Makefile.in @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile.in,v 1.7 1999/10/26 22:31:37 angelos Exp $ +# $OpenBSD: Makefile.in,v 1.8 1999/11/05 00:27:18 angelos Exp $ # # The author of this code is Angelos D. Keromytis (angelos@dsl.cis.upenn.edu) # @@ -62,11 +62,13 @@ LIBS = -L. -lkeynote @LIBS@ TARGET = libkeynote.a TARGET2 = keynote +TARGET3 = sample-app OBJS = k.tab.o lex.kn.o environment.o parse_assertion.o \ signature.o aux.o base64.o OBJS2 = z.tab.o lex.kv.o keynote-verify.o keynote-sign.o \ keynote-sigver.o keynote-keygen.o keynote-main.o @LIBOBJS@ +OBJS3 = sample-app.o crypto: all @@ -120,7 +122,7 @@ 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) man/*.0 config.cache + $(RM) $(RMFLAGS) $(TARGET) $(TARGET2) $(TARGET3) man/*.0 config.cache $(RM) $(RMFLAGS) config.log config.status config.h Makefile $(RM) $(RMFLAGS) man/*.cat? @@ -129,6 +131,9 @@ $(SSLCERT) $(SSLKEY): -new -x509 -nodes -out $(SSLCERT) \ -keyout $(SSLKEY) +test-sample: all $(OBJS3) + $(CC) $(CFLAGS) -o $(TARGET3) $(OBJS3) $(LIBS) + test-sig: all $(SSLCERT) $(SSLKEY) $(SED) -e 's/--.*//' < $(SSLCERT) > $(SSLCERT).1 $(ECHO) -n 'authorizer: "x509-base64:' > $(SSLCERT).2 diff --git a/lib/libkeynote/README b/lib/libkeynote/README index 9fca97dda7b..707bfde53fb 100644 --- a/lib/libkeynote/README +++ b/lib/libkeynote/README @@ -1,4 +1,4 @@ -# $OpenBSD: README,v 1.7 1999/11/03 19:52:21 angelos Exp $ +# $OpenBSD: README,v 1.8 1999/11/05 00:27:18 angelos Exp $ This is release 2.2 of the KeyNote trust management library reference implementation (in case you are wondering, there was never an official 1.0 @@ -14,6 +14,9 @@ crypto support, use "make nocrypto" instead (you still need to run "configure"). If you have built crypto support, "make test-sig" will run some more tests on the cryptographic algorithms. +A sample application is provided in sample-app.c. To build it, use +"make test-sample". + 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 diff --git a/lib/libkeynote/TODO b/lib/libkeynote/TODO index 888b02f3eaa..58f8298e673 100644 --- a/lib/libkeynote/TODO +++ b/lib/libkeynote/TODO @@ -1,8 +1,7 @@ -# $OpenBSD: TODO,v 1.6 1999/10/26 22:31:37 angelos Exp $ +# $OpenBSD: TODO,v 1.7 1999/11/05 00:27:18 angelos Exp $ TODOs: - Write key/signature algorithm drafts - - Sample application - More interesting/comprehensive testsuite - Make X509 support be non-RSA specific - Recursive parser diff --git a/lib/libkeynote/keynote-sigver.c b/lib/libkeynote/keynote-sigver.c index 81332a4ff10..760eb793e32 100644 --- a/lib/libkeynote/keynote-sigver.c +++ b/lib/libkeynote/keynote-sigver.c @@ -1,4 +1,4 @@ -/* $OpenBSD: keynote-sigver.c,v 1.9 1999/11/03 19:52:22 angelos Exp $ */ +/* $OpenBSD: keynote-sigver.c,v 1.10 1999/11/05 00:27:18 angelos Exp $ */ /* * The author of this code is Angelos D. Keromytis (angelos@dsl.cis.upenn.edu) * @@ -102,13 +102,17 @@ keynote_sigver(int argc, char *argv[]) close(fd); assertlist = kn_read_asserts(buf, sb.st_size, &n); - if ((assertlist == NULL) || (n == 0)) + if (assertlist == NULL) { - if (keynote_errno == ERROR_MEMORY) - fprintf(stderr, "Out of memory while allocating memory for " - "assertions.\n"); - else - fprintf(stderr, "No assertions found.\n"); + fprintf(stderr, "Out of memory while allocating memory for " + "assertions.\n"); + exit(-1); + } + + if (n == 0) + { + fprintf(stderr, "No assertions found in %s.\n", argv[1]); + free(assertlist); exit(-1); } diff --git a/lib/libkeynote/keynote.3 b/lib/libkeynote/keynote.3 index ee405941922..004db2389aa 100644 --- a/lib/libkeynote/keynote.3 +++ b/lib/libkeynote/keynote.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: keynote.3,v 1.17 1999/11/03 19:52:22 angelos Exp $ +.\" $OpenBSD: keynote.3,v 1.18 1999/11/05 00:27:18 angelos Exp $ .\" .\" The author of this code is Angelos D. Keromytis (angelos@dsl.cis.upenn.edu) .\" @@ -454,7 +454,9 @@ no longer needed. contains the number of assertions (and thus strings in the returned array) found in .Fa array . -On failure, this function returns \-1 and sets +On failure, this function returns +.Dv NULL +and sets .Va keynote_errno to .Er ERROR_MEMORY @@ -464,6 +466,12 @@ if .Fa array was .Dv NULL . +Note that if there were no assertions found in +.Fa array , +a valid pointer will be returned, but +.Fa numassertions +will contain the value zero on return. The returned pointer should be +freed by the programmer. .Pp .Fn kn_keycompare compares diff --git a/lib/libkeynote/sample-app.c b/lib/libkeynote/sample-app.c new file mode 100644 index 00000000000..59f12a706e5 --- /dev/null +++ b/lib/libkeynote/sample-app.c @@ -0,0 +1,544 @@ +/* $OpenBSD: sample-app.c,v 1.1 1999/11/05 00:27:18 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 <stdio.h> +#include <regex.h> +#include "keynote.h" + +/* These is only needed to pull in the SSL include files */ +#if HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "header.h" + +char policy_assertions[] = +"Authorizer: \"POLICY\"\n" \ +"Licensees: \"rsa-hex:3048024100d15d08ce7d2103d93ef21a87330361\\\n" \ +" ff123096b14330f9f0936e8f2064ef815ffdaabbb7d3ba47b\\\n" \ +" 49fac090cf44818af7ac7d66c2910f32d8d5eb261328558e1\\\n" \ +" 0203010001\"\n" \ +"Comment: This is our first policy assertion\n" \ +"Conditions: app_domain == \"test application\" -> \"true\";\n" \ +"\n" \ +"Authorizer: \"POLICY\"\n" \ +"Licensees: KEY1 || KEY2\n" \ +"Local-Constants: \n" \ +" KEY1 = \"rsa-base64:MEgCQQCzxWCi619s3Bqf8QOZTREBFelqWvljw\\\n" \ +" vCwktO7/5zufcz+P0UBRBFNtasWgkP6/tAIK8MnLMUnejGsye\\\n" \ +" DS2EVzAgMBAAE=\"\n" \ +" KEY2 = \"dsa-base64:MIHfAkEAhRzwrvhbRXIJH+nGfQB/tRp3ueF0j\\\n" \ +" 4OqVU4GmC6eIlrmlKxR+Me6tjqtWJr5gf/AEOnzoQAPRIlpiP\\\n" \ +" VJX1mRjwJBAKHTpHS7M938wVr+lIMjq0H0Aav5T4jlxS2rphI\\\n" \ +" 4fbc7tJm6wPW9p2KyHbe9GaZgzYK1OdnNXdanM/AkLW4OKz0C\\\n" \ +" FQDF69A/EHKoQC1H6DxCi0L3HfW9uwJANCLE6ViRxnv4Jj0gV\\\n" \ +" 8aO/b5AD+uA63+0EXUxO0Hqp91lzhDg/61BusMxFq7mQI0CLv\\\n" \ +" S+dlCGShsYyB+VjSub7Q==\"\n" \ +"Comment: A slightly more complicated policy\n" \ +"Conditions: app_domain == \"test application\" && @some_num == 1 && \n" \ +" (some_var == \"some value\" || \n" \ +" some_var == \"some other value\") -> \"true\";"; + +char credential_assertions[] = +"KeyNote-Version: 2\n"\ +"Authorizer: KEY1\n" +"Local-Constants: \n" \ +" KEY1 = \"rsa-base64:MEgCQQCzxWCi619s3Bqf8QOZTREBFelqWvljw\\\n" \ +" vCwktO7/5zufcz+P0UBRBFNtasWgkP6/tAIK8MnLMUnejGsye\\\n" \ +" DS2EVzAgMBAAE=\"\n" \ +"Licensees: \"dsa-hex:3081de02402121e160209f7ecef1b6866c907e8d\\\n" \ +" d65e9a67ef0fbd6ece7760b7c8bb0d9a0b71a0dd921b949f0\\\n" \ +" 9a16092eb3f50e33892bc3e9f1c8409f5298de40461493ef1\\\n" \ +" 024100a60b7e77f317e156566b388aaa32c3866a086831649\\\n" \ +" 1a55ab6fb8e57f7ade4a2a31e43017c383ab2a3e54f49688d\\\n" \ +" d66a326b7362beb974f2f1fb7dd573dd1bdf021500909807a\\\n" \ +" 4937f198fe893be6c63a7d627f13a385b02405811292c9949\\\n" \ +" 7aa80911c781a0ff51a5843423b9b4d03ad7e708ae2bfacaf\\\n" \ +" 11477f4f197dbba534194f8afd1e0b73261bb0a2c04af35db\\\n" \ +" 0507f5cffe74ed4f1a\"\n" \ +"Conditions: app_domain == \"test application\" && \n" \ +" another_var == \"foo\" -> \"true\";\n" \ +"Signature: \"sig-rsa-sha1-base64:E2OhrczI0LtAYAoJ6fSlqvlQDA4r\\\n" \ +" GiIX73T6p9eExpyHZbfjxPxXEIf6tbBre6x2Y26wBQCx/yCj5\\\n" \ +" 4IS3tuY2w==\"\n"; + +char action_authorizer[] = +"dsa-hex:3081de02402121e160209f7ecef1b6866c907e8d" \ +"d65e9a67ef0fbd6ece7760b7c8bb0d9a0b71a0dd921b949f0" \ +"9a16092eb3f50e33892bc3e9f1c8409f5298de40461493ef1" \ +"024100a60b7e77f317e156566b388aaa32c3866a086831649" \ +"1a55ab6fb8e57f7ade4a2a31e43017c383ab2a3e54f49688d" \ +"d66a326b7362beb974f2f1fb7dd573dd1bdf021500909807a" \ +"4937f198fe893be6c63a7d627f13a385b02405811292c9949" \ +"7aa80911c781a0ff51a5843423b9b4d03ad7e708ae2bfacaf" \ +"11477f4f197dbba534194f8afd1e0b73261bb0a2c04af35db" \ +"0507f5cffe74ed4f1a"; + +#define NUM_RETURN_VALUES 2 + +char *returnvalues[NUM_RETURN_VALUES]; + +/* + * Sample application. We do the following: + * - create a session + * - read a "file" with our KeyNote policy assertions + * - obtain a credential + * - obtain the requester's public key + * - construct an action attribute set + * - do the query + * + * Since this is a sample application, we won't be actually reading any + * real files or sockets. See the comments in the code below. + */ + +int +main(int argc, char **argv) +{ + int sessionid, num, i, j; + char **decomposed; + + /* + * We are creating a new KeyNote session here. A session may be temporary + * (we create it, add policies, credentials, authorizers, action set, do + * the query, and then we destroy it), or it may be persistent (if, for + * example, policies remain the same for a while). + * + * In this example, we'll just assume the session is temporary, but there + * will be comments as to what to do if this were a persistent session. + */ + sessionid = kn_init(); + if (sessionid == -1) + { + fprintf(stderr, "Failed to create a new session.\n"); + exit(-1); + } + + /* + * Assume we have read a file, or somehow securely acquired our policy + * assertions, and we have stored them in policy_assertions. + */ + + /* Let's find how many policies we just "read". */ + decomposed = kn_read_asserts(policy_assertions, strlen(policy_assertions), + &num); + if (decomposed == NULL) + { + fprintf(stderr, "Failed to allocate memory for policy assertions.\n"); + exit(-1); + } + + /* + * If there were no assertions in the first argument to kn_read_asserts, + * we'll get a valid pointer back, which we need to free. Note that this + * is an error; we always MUST have at least one policy assertion. + */ + if (num == 0) + { + free(decomposed); + fprintf(stderr, "No policy assertions provided.\n"); + exit(-1); + } + + /* + * We no longer need a copy of policy_assertions, so we could + * free it here. + */ + + /* + * decomposed now contains num pointers to strings, each containing a + * single assertion. We now add them all to the session. Note that + * we must provide the ASSERT_FLAG_LOCAL flag to indicate that these + * are policy assertions and thus do not have a signature field. + */ + for (i = 0; i < num; i++) + { + j = kn_add_assertion(sessionid, decomposed[i], + strlen(decomposed[i]), ASSERT_FLAG_LOCAL); + if (j == -1) + { + switch (keynote_errno) + { + case ERROR_MEMORY: + fprintf(stderr, "Out of memory, trying to add policy " + "assertion %d.\n", j); + break; + + case ERROR_SYNTAX: + fprintf(stderr, "Syntax error parsing policy " + "assertion %d.\n", j); + break; + + case ERROR_NOTFOUND: + fprintf(stderr, "Session %d not found while adding " + "policy assertion %d.\n", sessionid, j); + default: + fprintf(stderr, "Unspecified error %d (shouldn't happen) " + "while adding policy assertion %d.\n", + keynote_errno, j); + break; + } + + /* We don't need the assertion any more. */ + free(decomposed[i]); + } + } + + /* Now free decomposed itself. */ + free(decomposed); + + /* + * Now, assume we have somehow acquired (through some application-dependent + * means) one or more KeyNote credentials, and the key of the action + * authorizer. For example, if this were an HTTP authorization application, + * we would have acquired the credential(s) and the key after completing + * an SSL protocol exchange. + * + * So, we have some credentials in credential_assertions, and a key + * in action_authorizer. + */ + + /* Let's find how many credentials we just "received". */ + decomposed = kn_read_asserts(credential_assertions, + strlen(credential_assertions), &num); + if (decomposed == NULL) + { + fprintf(stderr, "Failed to allocate memory for credential " + "assertions.\n"); + exit(-1); + } + + /* + * If there were no assertions in the first argument to kn_read_asserts, + * we'll get a valid pointer back, which we need to free. Note that + * it is legal to have zero credentials. + */ + if (num == 0) + { + free(decomposed); + fprintf(stderr, "No credential assertions provided.\n"); + } + + /* + * We no longer need a copy of credential_assertions, so we could + * free it here. + */ + + /* + * decomposed now contains num pointers to strings, each containing a + * single assertion. We now add them all to the session. Note that here + * we must NOT provide the ASSERT_FLAG_LOCAL flag, since these are + * all credential assertions and need to be cryptographically verified. + */ + for (i = 0; i < num; i++) + { + /* + * The value returned by kn_add_assertion() is an ID for that + * assertion (unless it's a -1, which indicates an error). We could + * use this ID to remove the assertion from the session in the future, + * if we needed to. We would need to store the IDs somewhere of + * course. + * + * If this were a persistent session, it may make sense to delete + * the credentials we just added after we are done with the query, + * simply to conserve memory. On the other hand, we could just leave + * them in the session; this has no security implications. + * + * Also note that we could do the same with policy assertions. + * However, if we want to delete policy assertions, it usually then + * makes sense to just destroy the whole session via kn_close(), + * which frees all allocated resources. + */ + j = kn_add_assertion(sessionid, decomposed[i], + strlen(decomposed[i]), 0); + if (j == -1) + { + switch (keynote_errno) + { + case ERROR_MEMORY: + fprintf(stderr, "Out of memory, trying to add credential " + "assertion %d.\n", j); + break; + + case ERROR_SYNTAX: + fprintf(stderr, "Syntax error parsing credential " + "assertion %d.\n", j); + break; + + case ERROR_NOTFOUND: + fprintf(stderr, "Session %d not found while adding " + "credential assertion %d.\n", sessionid, j); + default: + fprintf(stderr, "Unspecified error %d (shouldn't happen) " + "while adding credential assertion %d.\n", + keynote_errno, j); + break; + } + + /* We don't need the assertion any more. */ + free(decomposed[i]); + } + } + + /* No longer needed. */ + free(decomposed); + + /* + * Now add the action authorizer. If we have more than one, just + * repeat. Note that the value returned here is just a success or + * failure indicator. If we want to later on delete an authorizer from + * the session (which we MUST do if this is a persistent session), + * we must keep a copy of the key. + */ + if (kn_add_authorizer(sessionid, action_authorizer) == -1) + { + switch (keynote_errno) + { + case ERROR_MEMORY: + fprintf(stderr, "Out of memory while adding action " + "authorizer.\n"); + break; + + case ERROR_SYNTAX: + fprintf(stderr, "Malformed action authorizer.\n"); + break; + + case ERROR_NOTFOUND: + fprintf(stderr, "Session %d not found while adding action " + "authorizer.\n", sessionid); + break; + + default: + fprintf(stderr, "Unspecified error while adding action " + "authorizer.\n"); + break; + } + } + + /* + * If we don't need action_authorizer any more (i.e., this is a temporary + * session), we could free it now. + */ + + /* + * Now we need to construct the action set. In a real application, we + * would be gathering the relevant information. Here, we just construct + * a fixed action set. + */ + + /* + * Add the relevant action attributes. Flags is zero, since we are not + * using any callback functions (ENVIRONMENT_FLAG_FUNC) or a regular + * expression for action attribute names (ENVIRONMENT_FLAG_REGEX). + */ + if (kn_add_action(sessionid, "app_domain", "test application", 0) == -1) + { + switch (keynote_errno) + { + case ERROR_SYNTAX: + fprintf(stderr, "Invalid name action attribute name " + "[app_domain]\n"); + break; + + case ERROR_MEMORY: + fprintf(stderr, "Out of memory adding action attribute " + "[app_domain = \"test application\"]\n"); + break; + + case ERROR_NOTFOUND: + fprintf(stderr, "Session %d not found while addiing action " + "attribute [app_domain = \"test application\"]\n", + sessionid); + break; + + default: + fprintf(stderr, "Unspecified error %d (shouldn't happen) " + "while adding action attribute [app_domain = " + "\"test application\"]\n", keynote_errno); + break; + } + } + + if (kn_add_action(sessionid, "some_num", "1", 0) == -1) + { + switch (keynote_errno) + { + case ERROR_SYNTAX: + fprintf(stderr, "Invalid name action attribute name " + "[some_num]\n"); + break; + + case ERROR_MEMORY: + fprintf(stderr, "Out of memory adding action attribute " + "[some_num = \"1\"]\n"); + break; + + case ERROR_NOTFOUND: + fprintf(stderr, "Session %d not found while addiing action " + "attribute [some_num = \"1\"]\n", sessionid); + break; + + default: + fprintf(stderr, "Unspecified error %d (shouldn't happen) " + "while adding action attribute [some_num = \"1\"]", + keynote_errno); + break; + } + } + + if (kn_add_action(sessionid, "some_var", "some other value", 0) == -1) + { + switch (keynote_errno) + { + case ERROR_SYNTAX: + fprintf(stderr, "Invalid name action attribute name " + "[some_var]\n"); + break; + + case ERROR_MEMORY: + fprintf(stderr, "Out of memory adding action attribute " + "[some_var = \"some other value\"]\n"); + break; + + case ERROR_NOTFOUND: + fprintf(stderr, "Session %d not found while addiing action " + "attribute [some_var = \"some other value\"]\n", + sessionid); + break; + + default: + fprintf(stderr, "Unspecified error %d (shouldn't happen) " + "while adding action attribute [some_var = " + "\"some other value\"]\n", keynote_errno); + break; + } + } + + if (kn_add_action(sessionid, "another_var", "foo", 0) == -1) + { + switch (keynote_errno) + { + case ERROR_SYNTAX: + fprintf(stderr, "Invalid name action attribute name " + "[another_var]\n"); + break; + + case ERROR_MEMORY: + fprintf(stderr, "Out of memory adding action attribute " + "[another_var = \"foo\"]\n"); + break; + + case ERROR_NOTFOUND: + fprintf(stderr, "Session %d not found while addiing action " + "attribute [another_var = \"foo\"]\n", sessionid); + break; + + default: + fprintf(stderr, "Unspecified error %d (shouldn't happen) " + "while adding action attribute [another_var = " + "\"foo\"]\n", keynote_errno); + break; + } + } + + /* Set the return values for this application -- just "false" and "true" */ + returnvalues[0] = "false"; + returnvalues[1] = "true"; + + /* Just do the query. */ + j = kn_do_query(sessionid, returnvalues, NUM_RETURN_VALUES); + if (j == -1) + { + switch (j) + { + case ERROR_MEMORY: + fprintf(stderr, "Out of memory while performing authorization " + "query.\n"); + break; + + case ERROR_NOTFOUND: + fprintf(stderr, "Session %d not found while performing " + "authorization query.\n", sessionid); + break; + + default: + fprintf(stderr, "Unspecified error %d (shouldn't happen) " + "while performing authorization query.\n", + keynote_errno); + break; + } + } + else + { + fprintf(stdout, "Return value is [%s]\n", returnvalues[j]); + } + + /* + * Once the query is done, we can find what assertions failed in what way. + * One way is just going through the list of assertions, as shown here + * for assertions that failed due to memory exhaustion. + */ + j = 0; + + do + { + i = kn_get_failed(sessionid, KEYNOTE_ERROR_MEMORY, j++); + if (i != -1) + fprintf(stderr, "Assertion %d failed due to memory exhaustion.\n", + i); + } while (i != -1); + + /* + * Another way is to go through the list of failed assertions by deleting + * the "first" one. + */ + do + { + i = kn_get_failed(sessionid, KEYNOTE_ERROR_SYNTAX, 0); + if (i != -1) + { + fprintf(stderr, "Assertion %d failed due to some syntax error.\n", + i); + kn_remove_assertion(sessionid, i); /* Delete assertion */ + } + } while (i != -1); + + /* + * Signature failures, another way. + */ + for (j = 0, i = kn_get_failed(sessionid, KEYNOTE_ERROR_SIGNATURE, 0); + i != -1; i = kn_get_failed(sessionid, KEYNOTE_ERROR_SIGNATURE, j++)) + fprintf(stderr, "Failed to verify signature on assertion %d.\n", i); + + /* + * Here's how to find all errors. + */ + for (i = kn_get_failed(sessionid, KEYNOTE_ERROR_ANY, 0); i != -1; + i = kn_get_failed(sessionid, KEYNOTE_ERROR_ANY, 0)) + { + fprintf(stderr, "Unspecified error in processing assertion %d.\n", i); + kn_remove_assertion(sessionid, i); + } + + /* Destroy the session, freeing all allocated memory. */ + kn_close(sessionid); + + exit(0); +} |