krb5 commit: Tests for unlocked iteration

Tom Yu tlyu at MIT.EDU
Sat Aug 2 14:24:28 EDT 2014


https://github.com/krb5/krb5/commit/344d6e26d9a11303a6078ae6afeb5fc8c4b42865
commit 344d6e26d9a11303a6078ae6afeb5fc8c4b42865
Author: Tom Yu <tlyu at mit.edu>
Date:   Sat Aug 2 14:20:35 2014 -0400

    Tests for unlocked iteration
    
    ticket: 7977

 src/tests/Makefile.in     |    7 +-
 src/tests/t_unlockiter.py |   18 +++
 src/tests/unlockiter.c    |  273 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 297 insertions(+), 1 deletions(-)

diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index abd431d..34816e3 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -51,6 +51,10 @@ t_init_creds: t_init_creds.o $(KRB5_BASE_DEPLIBS)
 t_localauth: t_localauth.o $(KRB5_BASE_DEPLIBS)
 	$(CC_LINK) -o $@ t_localauth.o $(KRB5_BASE_LIBS)
 
+unlockiter: unlockiter.o $(KDB5_DEPLIBS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+	$(CC_LINK) -o $@ unlockiter.o $(KDB5_LIBS) $(KADMSRV_LIBS) \
+		$(KRB5_BASE_LIBS)
+
 check-unix:: kdb_check
 
 kdc.conf: Makefile
@@ -94,7 +98,7 @@ kdb_check: kdc.conf krb5.conf
 	$(RM) $(TEST_DB)* stash_file
 
 check-pytests:: gcred hist hrealm kdbtest plugorder rdreq responder s2p
-check-pytests:: t_init_creds t_localauth
+check-pytests:: t_init_creds t_localauth unlockiter
 	$(RUNPYTEST) $(srcdir)/t_general.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_dump.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_iprop.py $(PYTESTFLAGS)
@@ -136,6 +140,7 @@ check-pytests:: t_init_creds t_localauth
 	$(RUNPYTEST) $(srcdir)/t_bogus_kdc_req.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_kdc_log.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_proxy.py $(PYTESTFLAGS)
+	$(RUNPYTEST) $(srcdir)/t_unlockiter.py $(PYTESTFLAGS)
 
 clean::
 	$(RM) gcred hist hrealm kdbtest plugorder rdreq responder s2p
diff --git a/src/tests/t_unlockiter.py b/src/tests/t_unlockiter.py
new file mode 100644
index 0000000..d4ca4c5
--- /dev/null
+++ b/src/tests/t_unlockiter.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+from k5test import *
+
+# Default KDB iteration is locked.  Expect write lock failure unless
+# unlocked iteration is explicitly requested.
+realm = K5Realm()
+realm.run(['./unlockiter'], expected_code=1)
+realm.run(['./unlockiter', '-u'])
+realm.run(['./unlockiter', '-l'], expected_code=1)
+
+# Set default to unlocked iteration.  Only explicitly requested locked
+# iteration should block the write lock.
+realm = K5Realm(krb5_conf={'dbmodules': {'db': {'unlockiter': 'true'}}})
+realm.run(['./unlockiter'])
+realm.run(['./unlockiter', '-u'])
+realm.run(['./unlockiter', '-l'], expected_code=1)
+
+success('Unlocked iteration unit tests')
diff --git a/src/tests/unlockiter.c b/src/tests/unlockiter.c
new file mode 100644
index 0000000..e2f2eb5
--- /dev/null
+++ b/src/tests/unlockiter.c
@@ -0,0 +1,273 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/unlockiter.c - test program for unlocked iteration */
+/*
+ * Copyright (C) 2014 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.
+ */
+
+/*
+ * Test unlocked KDB iteration.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <krb5.h>
+#include <kadm5/admin.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+struct cb_arg {
+    int inpipe;
+    int outpipe;
+    int timeout;
+    int done;
+};
+
+/* Helper function for cb(): read a sync byte (with possible timeout), then
+ * write a sync byte. */
+static int
+syncpair_rw(const char *name, struct cb_arg *arg, char *cp, int timeout)
+{
+    struct timeval tv;
+    fd_set rset;
+    int nfds;
+
+    FD_ZERO(&rset);
+    FD_SET(arg->inpipe, &rset);
+    tv.tv_sec = timeout;
+    tv.tv_usec = 0;
+
+    printf("cb: waiting for %s sync pair\n", name);
+    nfds = select(arg->inpipe + 1, &rset,
+                  NULL, NULL, (timeout == 0) ? NULL : &tv);
+    if (nfds < 0)
+        return -1;
+    if (nfds == 0) {
+        errno = ETIMEDOUT;
+        return -1;
+    }
+    if (read(arg->inpipe, cp, 1) < 0)
+        return -1;
+    printf("cb: writing %s sync pair\n", name);
+    if (write(arg->outpipe, cp, 1) < 0)
+        return -1;
+    return 0;
+}
+
+/* On the first iteration only, receive and send sync bytes to the locking
+ * child to drive its locking activities. */
+static krb5_error_code
+cb(void *argin, krb5_db_entry *ent)
+{
+    struct cb_arg *arg = argin;
+    char c = '\0';
+
+    if (arg->done)
+        return 0;
+
+    if (syncpair_rw("first", arg, &c, 0) < 0) {
+        com_err("cb", errno, "first sync pair");
+        return errno;
+    }
+    if (syncpair_rw("second", arg, &c, arg->timeout) < 0) {
+        com_err("cb", errno, "second sync pair");
+        return errno;
+    }
+    printf("cb: waiting for final sync byte\n");
+    if (read(arg->inpipe, &c, 1) < 0) {
+        com_err("cb", errno, "final sync byte");
+        return errno;
+    }
+    arg->done = 1;
+    return 0;
+}
+
+/* Parent process: iterate over the KDB, using a callback that synchronizes
+ * with the locking child. */
+static int
+iterator(struct cb_arg *cb_arg, char **db_args, pid_t child)
+{
+    krb5_error_code retval;
+    krb5_context ctx;
+
+    retval = krb5_init_context_profile(NULL, KRB5_INIT_CONTEXT_KDC, &ctx);
+    if (retval)
+        goto cleanup;
+
+    retval = krb5_db_open(ctx, db_args,
+                          KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
+    if (retval)
+        goto cleanup;
+
+    retval = krb5_db_iterate(ctx, NULL, cb, cb_arg, 0);
+    if (retval)
+        goto cleanup;
+
+    retval = krb5_db_fini(ctx);
+
+cleanup:
+    if (retval) {
+        com_err("iterator", retval, "");
+        kill(child, SIGTERM);
+        exit(1);
+    }
+    return retval;
+}
+
+/* Helper function for locker(): write, then receive a sync byte. */
+static int
+syncpair_wr(const char *name, int inpipe, int outpipe, unsigned char *cp)
+{
+    printf("locker: writing %s sync pair\n", name);
+    if (write(outpipe, cp, 1) < 0)
+        return -1;
+    printf("locker: waiting for %s sync pair\n", name);
+    if (read(inpipe, cp, 1) < 0)
+        return -1;
+    return 0;
+}
+
+/* Child process: acquire and release a write lock on the KDB, synchronized
+ * with parent. */
+static int
+locker(int inpipe, int outpipe, char **db_args)
+{
+    krb5_error_code retval;
+    unsigned char c = '\0';
+    krb5_context ctx;
+
+    retval = krb5_init_context_profile(NULL, KRB5_INIT_CONTEXT_KDC, &ctx);
+    if (retval)
+        goto cleanup;
+
+    retval = krb5_db_open(ctx, db_args,
+                          KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
+    if (retval)
+        goto cleanup;
+
+    if (syncpair_wr("first", inpipe, outpipe, &c) < 0) {
+        retval = errno;
+        goto cleanup;
+    }
+    printf("locker: acquiring lock...\n");
+    retval = krb5_db_lock(ctx, KRB5_DB_LOCKMODE_EXCLUSIVE);
+    if (retval)
+        goto cleanup;
+    printf("locker: acquired lock\n");
+    if (syncpair_wr("second", inpipe, outpipe, &c) < 0) {
+        retval = errno;
+        goto cleanup;
+    }
+    krb5_db_unlock(ctx);
+    printf("locker: released lock\n");
+    printf("locker: writing final sync byte\n");
+    if (write(outpipe, &c, 1) < 0) {
+        retval = errno;
+        goto cleanup;
+    }
+    retval = krb5_db_fini(ctx);
+cleanup:
+    if (retval)
+        com_err("locker", retval, "");
+
+    krb5_free_context(ctx);
+    exit(retval != 0);
+}
+
+static void
+usage(const char *prog)
+{
+    fprintf(stderr, "usage: %s [-lu] [-t timeout]\n", prog);
+    exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+    struct cb_arg cb_arg;
+    pid_t child;
+    char *db_args[2] = { NULL, NULL };
+    int c;
+    int cstatus;
+    int pipe_to_locker[2], pipe_to_iterator[2];
+
+    cb_arg.timeout = 1;
+    cb_arg.done = 0;
+    while ((c = getopt(argc, argv, "lt:u")) != -1) {
+        switch (c) {
+        case 'l':
+            db_args[0] = "lockiter";
+            break;
+        case 't':
+            cb_arg.timeout = atoi(optarg);
+            break;
+        case 'u':
+            db_args[0] = "unlockiter";
+            break;
+        default:
+            usage(argv[0]);
+        }
+    }
+    if (pipe(pipe_to_locker) < 0) {
+        com_err(argv[0], errno, "pipe(p_il)");
+        exit(1);
+    }
+    if (pipe(pipe_to_iterator) < 0) {
+        com_err(argv[0], errno, "pipe(p_li)");
+        exit(1);
+    }
+    cb_arg.inpipe = pipe_to_iterator[0];
+    cb_arg.outpipe = pipe_to_locker[1];
+    child = fork();
+    switch (child) {
+    case -1:
+        com_err(argv[0], errno, "fork");
+        exit(1);
+        break;
+    case 0:
+        locker(pipe_to_locker[0], pipe_to_iterator[1], db_args);
+        break;
+    default:
+        if (iterator(&cb_arg, db_args, child))
+            exit(1);
+        if (wait(&cstatus) < 0) {
+            com_err(argv[0], errno, "wait");
+            exit(1);
+        }
+        if (WIFSIGNALED(cstatus))
+            exit(1);
+        if (WIFEXITED(cstatus) && WEXITSTATUS(cstatus) != 0) {
+            exit(1);
+        }
+    }
+    exit(0);
+}


More information about the cvs-krb5 mailing list