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