krb5 commit: Add ccache collection tests using API

Greg Hudson ghudson at MIT.EDU
Tue Sep 24 15:10:48 EDT 2013


https://github.com/krb5/krb5/commit/620275cd43e237ab273b726b2aee0ae729587772
commit 620275cd43e237ab273b726b2aee0ae729587772
Author: Greg Hudson <ghudson at mit.edu>
Date:   Tue Sep 24 12:20:17 2013 -0400

    Add ccache collection tests using API
    
    Create a new test program in lib/krb5/ccache named t_cccol.c which
    verifies collection semantics using the API.  Run it with an empty DIR
    collection in t_cccol.py.

 src/lib/krb5/ccache/Makefile.in |    9 +-
 src/lib/krb5/ccache/t_cccol.c   |  353 +++++++++++++++++++++++++++++++++++++++
 src/lib/krb5/ccache/t_cccol.py  |    6 +
 3 files changed, 366 insertions(+), 2 deletions(-)

diff --git a/src/lib/krb5/ccache/Makefile.in b/src/lib/krb5/ccache/Makefile.in
index 1b62e81..ad53e65 100644
--- a/src/lib/krb5/ccache/Makefile.in
+++ b/src/lib/krb5/ccache/Makefile.in
@@ -66,6 +66,7 @@ SRCS=	$(srcdir)/ccbase.c \
 
 EXTRADEPSRCS= \
 	$(srcdir)/t_cc.c \
+	$(srcdir)/t_cccol.c \
 	$(srcdir)/t_cccursor.c
 
 ##DOS##OBJS=$(OBJS) $(OUTPRE)ccfns.$(OBJEXT)
@@ -103,6 +104,10 @@ T_CC_OBJS=t_cc.o
 t_cc: $(T_CC_OBJS) $(KRB5_BASE_DEPLIBS)
 	$(CC_LINK) -o t_cc $(T_CC_OBJS) $(KRB5_BASE_LIBS)
 
+T_CCCOL_OBJS = t_cccol.o
+t_cccol: $(T_CCCOL_OBJS) $(KRB5_BASE_DEPLIBS)
+	$(CC_LINK) -o $@ $(T_CCCOL_OBJS) $(KRB5_BASE_LIBS)
+
 T_CCCURSOR_OBJS = t_cccursor.o
 t_cccursor: $(T_CCCURSOR_OBJS) $(KRB5_BASE_DEPLIBS)
 	$(CC_LINK) -o $@ $(T_CCCURSOR_OBJS) $(KRB5_BASE_LIBS)
@@ -111,11 +116,11 @@ check-unix:: t_cc
 	KRB5_CONFIG=$(srcdir)/t_krb5.conf ; export KRB5_CONFIG ;\
 	$(RUN_SETUP) $(VALGRIND) ./t_cc
 
-check-pytests:: t_cccursor
+check-pytests:: t_cccursor t_cccol
 	$(RUNPYTEST) $(srcdir)/t_cccol.py $(PYTESTFLAGS)
 
 clean-unix::
-	$(RM) t_cc t_cc.o t_cccursor t_cccursor.o
+	$(RM) t_cc t_cc.o t_cccursor t_cccursor.o t_cccol t_cccol.o
 
 ##WIN32## $(OUTPRE)cc_mslsa.$(OBJEXT): cc_mslsa.c $(top_srcdir)/include/k5-int.h $(BUILDTOP)/include/krb5/osconf.h $(BUILDTOP)/include/krb5/autoconf.h $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS)
 
diff --git a/src/lib/krb5/ccache/t_cccol.c b/src/lib/krb5/ccache/t_cccol.c
new file mode 100644
index 0000000..3b4d3b3
--- /dev/null
+++ b/src/lib/krb5/ccache/t_cccol.c
@@ -0,0 +1,353 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/ccache/t_cccol.py - Test ccache collection via API */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <krb5.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+static krb5_context ctx;
+
+/* Check that code is 0.  Display an error message first if it is not. */
+static void
+check(krb5_error_code code)
+{
+    const char *errmsg;
+
+    if (code != 0) {
+        errmsg = krb5_get_error_message(ctx, code);
+        fprintf(stderr, "%s\n", errmsg);
+        krb5_free_error_message(ctx, errmsg);
+    }
+    assert(code == 0);
+}
+
+/* Construct a list of the names of each credential cache in the collection. */
+static void
+get_collection_names(char ***list_out, size_t *count_out)
+{
+    krb5_cccol_cursor cursor;
+    krb5_ccache cache;
+    char **list = NULL;
+    size_t count = 0;
+    char *name;
+
+    check(krb5_cccol_cursor_new(ctx, &cursor));
+    while (1) {
+        check(krb5_cccol_cursor_next(ctx, cursor, &cache));
+        if (cache == NULL)
+            break;
+        check(krb5_cc_get_full_name(ctx, cache, &name));
+        krb5_cc_close(ctx, cache);
+        list = realloc(list, (count + 1) * sizeof(*list));
+        assert(list != NULL);
+        list[count++] = name;
+    }
+    krb5_cccol_cursor_free(ctx, &cursor);
+    *list_out = list;
+    *count_out = count;
+}
+
+/* Return true if list contains name. */
+static krb5_boolean
+in_list(char **list, size_t count, const char *name)
+{
+    size_t i;
+
+    for (i = 0; i < count; i++) {
+        if (strcmp(list[i], name) == 0)
+            return TRUE;
+    }
+    return FALSE;
+}
+
+/* Release the memory for a list of credential cache names. */
+static void
+free_list(char **list, size_t count)
+{
+    size_t i;
+
+    for (i = 0; i < count; i++)
+        krb5_free_string(ctx, list[i]);
+    free(list);
+}
+
+/*
+ * Check that the cache names within the current collection begin with first
+ * (unless first is NULL), that the other elements match the remaining
+ * arguments in some order.  others must be the number of additional cache
+ * names.
+ */
+static void
+check_collection(const char *first, size_t others, ...)
+{
+    va_list ap;
+    char **list;
+    size_t count, i;
+    const char *name;
+
+    get_collection_names(&list, &count);
+    if (first != NULL) {
+        assert(strcmp(first, list[0]) == 0);
+        assert(count == others + 1);
+    } else {
+        assert(count == others);
+    }
+    va_start(ap, others);
+    for (i = 0; i < others; i++) {
+        name = va_arg(ap, const char *);
+        assert(in_list(list, count, name));
+    }
+    va_end(ap);
+    free_list(list, count);
+}
+
+/* Check that the name of cache matches expected_name. */
+static void
+check_name(krb5_ccache cache, const char *expected_name)
+{
+    char *name;
+
+    check(krb5_cc_get_full_name(ctx, cache, &name));
+    assert(strcmp(name, expected_name) == 0);
+    krb5_free_string(ctx, name);
+}
+
+/* Check that when collection_name is resolved, the resulting cache's name
+ * matches expected_name. */
+static void
+check_primary_name(const char *collection_name, const char *expected_name)
+{
+    krb5_ccache cache;
+
+    check(krb5_cc_resolve(ctx, collection_name, &cache));
+    check_name(cache, expected_name);
+    krb5_cc_close(ctx, cache);
+}
+
+/* Check that when name is resolved, the resulting cache's principal matches
+ * expected_princ, or has no principal if expected_princ is NULL. */
+static void
+check_princ(const char *name, krb5_principal expected_princ)
+{
+    krb5_ccache cache;
+    krb5_principal princ;
+
+    check(krb5_cc_resolve(ctx, name, &cache));
+    if (expected_princ != NULL) {
+        check(krb5_cc_get_principal(ctx, cache, &princ));
+        assert(krb5_principal_compare(ctx, princ, expected_princ));
+        krb5_free_principal(ctx, princ);
+    } else {
+        assert(krb5_cc_get_principal(ctx, cache, &princ) != 0);
+    }
+    krb5_cc_close(ctx, cache);
+}
+
+/* Check that krb5_cc_cache_match on princ returns a cache whose name matches
+ * expected_name, or that the match fails if expected_name is NULL. */
+static void
+check_match(krb5_principal princ, const char *expected_name)
+{
+    krb5_ccache cache;
+
+    if (expected_name != NULL) {
+        check(krb5_cc_cache_match(ctx, princ, &cache));
+        check_name(cache, expected_name);
+        krb5_cc_close(ctx, cache);
+    } else {
+        assert(krb5_cc_cache_match(ctx, princ, &cache) != 0);
+    }
+}
+
+int
+main(int argc, char **argv)
+{
+    krb5_ccache ccinitial, ccu1, ccu2;
+    krb5_principal princ1, princ2, princ3;
+    const char *collection_name, *typename;
+    char *initial_primary_name, *unique1_name, *unique2_name;
+
+    /*
+     * Get the collection name from the command line.  This is a ccache name
+     * with collection semantics, like DIR:/path/to/directory.  This test
+     * program assumes that the collection is empty to start with.
+     */
+    assert(argc == 2);
+    collection_name = argv[1];
+
+    /*
+     * Set the default ccache for the context to be the collection name, so the
+     * library can find the collection.
+     */
+    check(krb5_init_context(&ctx));
+    check(krb5_cc_set_default_name(ctx, collection_name));
+
+    /*
+     * Resolve the collection name.  Since the collection is empty, this should
+     * generate a subsidiary name of an uninitialized cache.  Getting the name
+     * of the resulting cache should give us the subsidiary name, not the
+     * collection name.  This resulting subsidiary name should be consistent if
+     * we resolve the collection name again, and the collection should still be
+     * empty since we haven't initialized the cache.
+     */
+    check(krb5_cc_resolve(ctx, collection_name, &ccinitial));
+    check(krb5_cc_get_full_name(ctx, ccinitial, &initial_primary_name));
+    assert(strcmp(initial_primary_name, collection_name) != 0);
+    check_primary_name(collection_name, initial_primary_name);
+    check_collection(NULL, 0);
+    check_princ(collection_name, NULL);
+    check_princ(initial_primary_name, NULL);
+
+    /*
+     * Before initializing the primary ccache, generate and initialize two
+     * unique caches of the collection's type.  Check that the cache names
+     * resolve to the generated caches and appear in the collection.  (They
+     * might appear before being initialized; that's not currently considered
+     * important).  The primary cache for the collection should remain as the
+     * unitialized cache from the previous step.
+     */
+    typename = krb5_cc_get_type(ctx, ccinitial);
+    check(krb5_cc_new_unique(ctx, typename, NULL, &ccu1));
+    check(krb5_cc_get_full_name(ctx, ccu1, &unique1_name));
+    check(krb5_parse_name(ctx, "princ1 at X", &princ1));
+    check(krb5_cc_initialize(ctx, ccu1, princ1));
+    check_princ(unique1_name, princ1);
+    check_match(princ1, unique1_name);
+    check_collection(NULL, 1, unique1_name);
+    check(krb5_cc_new_unique(ctx, typename, NULL, &ccu2));
+    check(krb5_cc_get_full_name(ctx, ccu2, &unique2_name));
+    check(krb5_parse_name(ctx, "princ2 at X", &princ2));
+    check(krb5_cc_initialize(ctx, ccu2, princ2));
+    check_princ(unique2_name, princ2);
+    check_match(princ1, unique1_name);
+    check_match(princ2, unique2_name);
+    check_collection(NULL, 2, unique1_name, unique2_name);
+    assert(strcmp(unique1_name, initial_primary_name) != 0);
+    assert(strcmp(unique1_name, collection_name) != 0);
+    assert(strcmp(unique2_name, initial_primary_name) != 0);
+    assert(strcmp(unique2_name, collection_name) != 0);
+    assert(strcmp(unique2_name, unique1_name) != 0);
+    check_primary_name(collection_name, initial_primary_name);
+
+    /*
+     * Initialize the initial primary cache.  Make sure it didn't change names,
+     * that the previously retrieved name and the collection name both resolve
+     * to the initialized cache, and that it now appears first in the
+     * collection.
+     */
+    check(krb5_parse_name(ctx, "princ3 at X", &princ3));
+    check(krb5_cc_initialize(ctx, ccinitial, princ3));
+    check_name(ccinitial, initial_primary_name);
+    check_princ(initial_primary_name, princ3);
+    check_princ(collection_name, princ3);
+    check_match(princ3, initial_primary_name);
+    check_collection(initial_primary_name, 2, unique1_name, unique2_name);
+
+    /*
+     * Switch the primary cache to each cache we have open.  One each switch,
+     * check the primary name, check that the collection resolves to the
+     * expected cache, and check that the new primary name appears first in the
+     * collection.
+     */
+    check(krb5_cc_switch(ctx, ccu1));
+    check_primary_name(collection_name, unique1_name);
+    check_princ(collection_name, princ1);
+    check_collection(unique1_name, 2, initial_primary_name, unique2_name);
+    check(krb5_cc_switch(ctx, ccu2));
+    check_primary_name(collection_name, unique2_name);
+    check_princ(collection_name, princ2);
+    check_collection(unique2_name, 2, initial_primary_name, unique1_name);
+    check(krb5_cc_switch(ctx, ccinitial));
+    check_primary_name(collection_name, initial_primary_name);
+    check_princ(collection_name, princ3);
+    check_collection(initial_primary_name, 2, unique1_name, unique2_name);
+
+    /*
+     * Destroy the primary cache.  Make sure this causes both the initial
+     * primary name and the collection name to resolve to an uninitialized
+     * cache.  Make sure the primary name doesn't change and doesn't appear in
+     * the collection any more.
+     */
+    check(krb5_cc_destroy(ctx, ccinitial));
+    check_princ(initial_primary_name, NULL);
+    check_princ(collection_name, NULL);
+    check_primary_name(collection_name, initial_primary_name);
+    check_match(princ1, unique1_name);
+    check_match(princ2, unique2_name);
+    check_match(princ3, NULL);
+    check_collection(NULL, 2, unique1_name, unique2_name);
+
+    /*
+     * Switch to the first unique cache after destroying the primary cache.
+     * Check that the collection name resolves to this cache and that the new
+     * primary name appears first in the collection.
+     */
+    check(krb5_cc_switch(ctx, ccu1));
+    check_primary_name(collection_name, unique1_name);
+    check_princ(collection_name, princ1);
+    check_collection(unique1_name, 1, unique2_name);
+
+    /*
+     * Destroy the second unique cache (which is not the current primary),
+     * check that it is on longer initialized, and check that it no longer
+     * appears in the collection.  Check that destroying the non-primary cache
+     * doesn't affect the primary name.
+     */
+    check(krb5_cc_destroy(ctx, ccu2));
+    check_princ(unique2_name, NULL);
+    check_match(princ2, NULL);
+    check_collection(unique1_name, 0);
+    check_primary_name(collection_name, unique1_name);
+    check_match(princ1, unique1_name);
+    check_princ(collection_name, princ1);
+
+    /*
+     * Destroy the first unique cache.  Check that the collection is empty and
+     * still has the same primary name.
+     */
+    check(krb5_cc_destroy(ctx, ccu1));
+    check_princ(unique1_name, NULL);
+    check_princ(collection_name, NULL);
+    check_primary_name(collection_name, unique1_name);
+    check_match(princ1, NULL);
+    check_collection(NULL, 0);
+
+    krb5_free_string(ctx, initial_primary_name);
+    krb5_free_string(ctx, unique1_name);
+    krb5_free_string(ctx, unique2_name);
+    krb5_free_principal(ctx, princ1);
+    krb5_free_principal(ctx, princ2);
+    krb5_free_principal(ctx, princ3);
+    return 0;
+}
diff --git a/src/lib/krb5/ccache/t_cccol.py b/src/lib/krb5/ccache/t_cccol.py
index f0792e9..8b70470 100644
--- a/src/lib/krb5/ccache/t_cccol.py
+++ b/src/lib/krb5/ccache/t_cccol.py
@@ -1,6 +1,12 @@
 #!/usr/bin/python
 from k5test import *
 
+# Run the collection test program against each collection-enabled type.
+realm = K5Realm(create_kdb=False)
+realm.run(['./t_cccol', 'DIR:' + os.path.join(realm.testdir, 'cc')])
+realm.stop()
+
+# Test cursor semantics using real ccaches.
 realm = K5Realm(create_host=False)
 
 realm.addprinc('alice', password('alice'))


More information about the cvs-krb5 mailing list