summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2013-08-05 16:10:10 -0400
committerGreg Hudson <ghudson@mit.edu>2013-08-15 12:39:58 -0400
commit7ad5f3bfd8b57d2f4c001182792e25968309ca8a (patch)
tree56092fc925f574c72c9f0a0afae396ed5387c093
parent4f7f1fce6edca17db625d76c1f81ea098f29c313 (diff)
downloadkrb5-7ad5f3bfd8b57d2f4c001182792e25968309ca8a.tar.gz
krb5-7ad5f3bfd8b57d2f4c001182792e25968309ca8a.tar.xz
krb5-7ad5f3bfd8b57d2f4c001182792e25968309ca8a.zip
Add hostrealm interface tests
Create a test module for the hostrealm interface, a harness to call the realm mapping functions and display their results, and a Python script to exercise the functionality of the interface and each module (except the dns module, which we cannot easily test since it relies on TXT records in the public DNS). ticket: 7687
-rw-r--r--src/Makefile.in1
-rw-r--r--src/configure.in1
-rw-r--r--src/plugins/hostrealm/test/Makefile.in21
-rw-r--r--src/plugins/hostrealm/test/deps14
-rw-r--r--src/plugins/hostrealm/test/hostrealm_test.exports2
-rw-r--r--src/plugins/hostrealm/test/main.c197
-rw-r--r--src/tests/Makefile.in11
-rw-r--r--src/tests/hrealm.c99
-rw-r--r--src/tests/t_hostrealm.py128
9 files changed, 471 insertions, 3 deletions
diff --git a/src/Makefile.in b/src/Makefile.in
index 0000510d42..2f4e6fcd0b 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -10,6 +10,7 @@ mydir=.
SUBDIRS=util include lib \
@sam2_plugin@ \
plugins/kadm5_hook/test \
+ plugins/hostrealm/test \
plugins/localauth/test \
plugins/pwqual/test \
plugins/kdb/db2 \
diff --git a/src/configure.in b/src/configure.in
index b2802baee9..81c684d16d 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -1357,6 +1357,7 @@ dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
kdc slave config-files build-tools man doc include
+ plugins/hostrealm/test
plugins/locate/python
plugins/localauth/test
plugins/kadm5_hook/test
diff --git a/src/plugins/hostrealm/test/Makefile.in b/src/plugins/hostrealm/test/Makefile.in
new file mode 100644
index 0000000000..2d9c38e32a
--- /dev/null
+++ b/src/plugins/hostrealm/test/Makefile.in
@@ -0,0 +1,21 @@
+mydir=plugins$(S)hostrealm$(S)test
+BUILDTOP=$(REL)..$(S)..$(S)..
+
+LIBBASE=hostrealm_test
+LIBMAJOR=0
+LIBMINOR=0
+RELDIR=../plugins/hostrealm/test
+# Depends on libkrb5
+SHLIB_EXPDEPS= $(KRB5_DEPLIB)
+SHLIB_EXPLIBS= $(KRB5_LIB)
+
+STLIBOBJS=main.o
+
+SRCS= $(srcdir)/main.c
+
+all-unix:: all-libs
+install-unix::
+clean-unix:: clean-libs clean-libobjs
+
+@libnover_frag@
+@libobj_frag@
diff --git a/src/plugins/hostrealm/test/deps b/src/plugins/hostrealm/test/deps
new file mode 100644
index 0000000000..d91fef860d
--- /dev/null
+++ b/src/plugins/hostrealm/test/deps
@@ -0,0 +1,14 @@
+#
+# Generated makefile dependencies follow.
+#
+main.so main.po $(OUTPRE)main.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/hostrealm_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ main.c
diff --git a/src/plugins/hostrealm/test/hostrealm_test.exports b/src/plugins/hostrealm/test/hostrealm_test.exports
new file mode 100644
index 0000000000..1adb759285
--- /dev/null
+++ b/src/plugins/hostrealm/test/hostrealm_test.exports
@@ -0,0 +1,2 @@
+hostrealm_test1_initvt
+hostrealm_test2_initvt
diff --git a/src/plugins/hostrealm/test/main.c b/src/plugins/hostrealm/test/main.c
new file mode 100644
index 0000000000..3b36dce77d
--- /dev/null
+++ b/src/plugins/hostrealm/test/main.c
@@ -0,0 +1,197 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* plugins/hostrealm/test/main.c - test module for host-realm interface */
+/*
+ * Copyright (C) 2010,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.
+ */
+
+/*
+ * This file implements two hostrealm modules named "test1" and "test2".
+ *
+ * The first module returns multi-element lists. For host_realm and
+ * fallback_realm, it returns a list of the host's components in order. For
+ * default_realm, it returns a list containing "one" and "two".
+ *
+ * The second module tests error handling. For host_realm and fallback_realm,
+ * it returns a fatal error on hosts beginning with 'z', a list containing "a"
+ * for hosts beginning with 'a', and passes control to later modules otherwise.
+ * For default_realm, it returns a fatal error.
+ */
+
+#include <k5-int.h>
+#include <krb5/hostrealm_plugin.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+static krb5_error_code
+split_comps(krb5_context context, krb5_hostrealm_moddata data,
+ const char *host, char ***realms_out)
+{
+ krb5_error_code ret;
+ const char *p, *q;
+ char *word = NULL, **list = NULL, **newptr;
+ size_t count = 0;
+
+ *realms_out = NULL;
+ if (*host == '\0')
+ return KRB5_PLUGIN_NO_HANDLE;
+ p = host;
+ while (TRUE) {
+ q = strchr(p, '.');
+ word = (q == NULL) ? strdup(p) : k5memdup0(p, q - p, &ret);
+ if (word == NULL)
+ goto oom;
+ newptr = realloc(list, (count + 2) * sizeof(*list));
+ if (newptr == NULL)
+ goto oom;
+ list = newptr;
+ list[count++] = word;
+ list[count] = NULL;
+ word = NULL;
+ if (q == NULL)
+ break;
+ p = q + 1;
+ }
+ *realms_out = list;
+ return 0;
+
+oom:
+ krb5_free_host_realm(context, list);
+ free(word);
+ return ENOMEM;
+}
+
+static krb5_error_code
+multi_defrealm(krb5_context context, krb5_hostrealm_moddata data,
+ char ***realms_out)
+{
+ char **list = NULL, *one = NULL, *two = NULL;
+
+ *realms_out = NULL;
+ one = strdup("one");
+ if (one == NULL)
+ goto oom;
+ two = strdup("two");
+ if (two == NULL)
+ goto oom;
+ list = calloc(3, sizeof(*list));
+ if (list == NULL)
+ goto oom;
+ list[0] = one;
+ list[1] = two;
+ list[2] = NULL;
+ *realms_out = list;
+ return 0;
+
+oom:
+ free(one);
+ free(two);
+ free(list);
+ return ENOMEM;
+}
+
+static krb5_error_code
+maybe_realm(krb5_context context, krb5_hostrealm_moddata data,
+ const char *host, char ***realms_out)
+{
+ char **list, *a;
+
+ *realms_out = NULL;
+ if (*host == 'z')
+ return KRB5_ERR_NO_SERVICE;
+ if (*host != 'a')
+ return KRB5_PLUGIN_NO_HANDLE;
+ a = strdup("a");
+ if (a == NULL)
+ return ENOMEM;
+ list = calloc(2, sizeof(*list));
+ if (list == NULL) {
+ free(a);
+ return ENOMEM;
+ }
+ list[0] = a;
+ list[1] = NULL;
+ *realms_out = list;
+ return 0;
+}
+
+static krb5_error_code
+error(krb5_context context, krb5_hostrealm_moddata data, char ***realms_out)
+{
+ *realms_out = NULL;
+ return KRB5_ERR_NO_SERVICE;
+}
+
+static void
+free_realmlist(krb5_context context, krb5_hostrealm_moddata data,
+ char **list)
+{
+ krb5_free_host_realm(context, list);
+}
+
+krb5_error_code
+hostrealm_test1_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+krb5_error_code
+hostrealm_test2_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+krb5_error_code
+hostrealm_test1_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_hostrealm_vtable vt;
+
+ if (maj_ver != 1)
+ return KRB5_PLUGIN_VER_NOTSUPP;
+ vt = (krb5_hostrealm_vtable)vtable;
+ vt->name = "test1";
+ vt->host_realm = split_comps;
+ vt->fallback_realm = split_comps;
+ vt->default_realm = multi_defrealm;
+ vt->free_list = free_realmlist;
+ return 0;
+}
+
+krb5_error_code
+hostrealm_test2_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_hostrealm_vtable vt;
+
+ if (maj_ver != 1)
+ return KRB5_PLUGIN_VER_NOTSUPP;
+ vt = (krb5_hostrealm_vtable)vtable;
+ vt->name = "test2";
+ vt->host_realm = maybe_realm;
+ vt->default_realm = error;
+ vt->free_list = free_realmlist;
+ return 0;
+}
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index 5828a90212..3525b48ad9 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -26,6 +26,9 @@ gcred: gcred.o $(KRB5_BASE_DEPLIBS)
hist: hist.o $(KDB5_DEPLIBS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS)
$(CC_LINK) -o $@ hist.o $(KDB5_LIBS) $(KADMSRV_LIBS) $(KRB5_BASE_LIBS)
+hrealm: hrealm.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ hrealm.o $(KRB5_BASE_LIBS)
+
kdbtest: kdbtest.o $(KDB5_DEPLIBS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS)
$(CC_LINK) -o $@ kdbtest.o $(KDB5_LIBS) $(KADMSRV_LIBS) \
$(KRB5_BASE_LIBS)
@@ -84,7 +87,8 @@ kdb_check: kdc.conf krb5.conf
$(RUN_SETUP) $(VALGRIND) ../kadmin/dbutil/kdb5_util $(KADMIN_OPTS) destroy -f
$(RM) $(TEST_DB)* stash_file
-check-pytests:: gcred hist kdbtest plugorder responder t_init_creds t_localauth
+check-pytests:: gcred hist hrealm kdbtest plugorder responder
+check-pytests:: t_init_creds t_localauth
$(RUNPYTEST) $(srcdir)/t_general.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_dump.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_iprop.py $(PYTESTFLAGS)
@@ -95,6 +99,7 @@ check-pytests:: gcred hist kdbtest plugorder responder t_init_creds t_localauth
$(RUNPYTEST) $(srcdir)/t_localauth.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_kadm5_hook.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_pwqual.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_hostrealm.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_kdb_locking.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_keyrollover.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_renew.py $(PYTESTFLAGS)
@@ -115,6 +120,6 @@ check-pytests:: gcred hist kdbtest plugorder responder t_init_creds t_localauth
$(RUNPYTEST) $(srcdir)/t_cve-2013-1417.py $(PYTESTFLAGS)
clean::
- $(RM) gcred hist kdbtest plugorder responder t_init_creds t_localauth
- $(RM) krb5.conf kdc.conf
+ $(RM) gcred hist hrealm kdbtest plugorder responder
+ $(RM) t_init_creds t_localauth krb5.conf kdc.conf
$(RM) -rf kdc_realm/sandbox ldap
diff --git a/src/tests/hrealm.c b/src/tests/hrealm.c
new file mode 100644
index 0000000000..f464c8fd66
--- /dev/null
+++ b/src/tests/hrealm.c
@@ -0,0 +1,99 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/hrealm.c - Test harness for host-realm interfaces */
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/*
+ * This program is intended to be run from a python script as:
+ *
+ * hrealm -h|-f|-d [hostname]
+ *
+ * Calls krb5_get_host_realm, krb5_get_fallback_host_realm, or
+ * krb5_default_realm depending on the option given. For the first two
+ * choices, hostname or NULL is passed as the argument. The results are
+ * displayed one per line.
+ */
+
+#include "k5-int.h"
+
+static krb5_context ctx;
+
+static void
+check(krb5_error_code code)
+{
+ const char *errmsg;
+
+ if (code) {
+ errmsg = krb5_get_error_message(ctx, code);
+ fprintf(stderr, "%s\n", errmsg);
+ krb5_free_error_message(ctx, errmsg);
+ exit(1);
+ }
+}
+
+static void
+display(char **realms)
+{
+ while (realms != NULL && *realms != NULL)
+ printf("%s\n", *realms++);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_data d;
+ char **realms, *realm;
+
+ check(krb5_init_context(&ctx));
+
+ /* Parse arguments. */
+ if (argc < 2 || argc > 3)
+ abort();
+
+ if (strcmp(argv[1], "-d") == 0) {
+ check(krb5_get_default_realm(ctx, &realm));
+ printf("%s\n", realm);
+ krb5_free_default_realm(ctx, realm);
+ } else if (strcmp(argv[1], "-h") == 0) {
+ check(krb5_get_host_realm(ctx, argv[2], &realms));
+ display(realms);
+ krb5_free_host_realm(ctx, realms);
+ } else if (strcmp(argv[1], "-f") == 0) {
+ assert(argc == 3);
+ d = string2data(argv[2]);
+ check(krb5_get_fallback_host_realm(ctx, &d, &realms));
+ display(realms);
+ krb5_free_host_realm(ctx, realms);
+ } else {
+ abort();
+ }
+ krb5_free_context(ctx);
+ return 0;
+}
diff --git a/src/tests/t_hostrealm.py b/src/tests/t_hostrealm.py
new file mode 100644
index 0000000000..76b282d2ac
--- /dev/null
+++ b/src/tests/t_hostrealm.py
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+from k5test import *
+
+plugin = os.path.join(buildtop, "plugins", "hostrealm", "test",
+ "hostrealm_test.so")
+
+# Disable the "dns" module (we can't easily test TXT lookups) and
+# arrange the remaining modules in an order which makes sense for most
+# tests.
+conf = {'plugins': {'hostrealm': {'module': ['test1:' + plugin,
+ 'test2:' + plugin],
+ 'enable_only': ['test2', 'profile',
+ 'domain', 'test1']}},
+ 'domain_realm': {'.x': 'DOTMATCH', 'x': 'MATCH', '.1': 'NUMMATCH'}}
+realm = K5Realm(krb5_conf=conf, create_kdb=False)
+
+def test(realm, args, expected_realms, msg, env=None):
+ out = realm.run(['./hrealm'] + args, env=env)
+ if out.split('\n') != expected_realms + ['']:
+ fail(msg)
+
+def test_error(realm, args, expected_error, msg, env=None):
+ out = realm.run(['./hrealm'] + args, env=env, expected_code=1)
+ if expected_error not in out:
+ fail(msg)
+
+def testh(realm, host, expected_realms, msg, env=None):
+ test(realm, ['-h', host], expected_realms, msg, env=env)
+def testf(realm, host, expected_realms, msg, env=None):
+ test(realm, ['-f', host], expected_realms, msg, env=env)
+def testd(realm, expected_realm, msg, env=None):
+ test(realm, ['-d'], [expected_realm], msg, env=env)
+def testh_error(realm, host, expected_error, msg, env=None):
+ test_error(realm, ['-h', host], expected_error, msg, env=env)
+def testf_error(realm, host, expected_error, msg, env=None):
+ test_error(realm, ['-f', host], expected_error, msg, env=env)
+def testd_error(realm, expected_error, msg, env=None):
+ test_error(realm, ['-d'], expected_error, msg, env=env)
+
+###
+### krb5_get_host_realm tests
+###
+
+# The test2 module returns a fatal error on hosts beginning with 'z',
+# and an answer on hosts begining with 'a'.
+testh_error(realm, 'zoo', 'service not available', 'host_realm test2 z')
+testh(realm, 'abacus', ['a'], 'host_realm test2 a')
+
+# The profile module gives answers for hostnames equal to or ending in
+# 'X', due to [domain_realms]. There is also an entry for hostnames
+# ending in '1', but hostnames which appear to be IP or IPv6 addresses
+# should instead fall through to test1.
+testh(realm, 'x', ['MATCH'], 'host_realm profile x')
+testh(realm, '.x', ['DOTMATCH'], 'host_realm profile .x')
+testh(realm, 'b.x', ['DOTMATCH'], 'host_realm profile b.x')
+testh(realm, '.b.c.x', ['DOTMATCH'], 'host_realm profile .b.c.x')
+testh(realm, 'b.1', ['NUMMATCH'], 'host_realm profile b.1')
+testh(realm, '4.3.2.1', ['4', '3', '2', '1'], 'host_realm profile 4.3.2.1')
+testh(realm, 'b:c.x', ['b:c', 'x'], 'host_realm profile b:c.x')
+# hostname cleaning should convert "X." to "x" before matching.
+testh(realm, 'X.', ['MATCH'], 'host_realm profile X.')
+
+# The test1 module returns a list of the hostname components.
+testh(realm, 'b.c.d', ['b', 'c', 'd'], 'host_realm test1')
+
+# If no module returns a result, we should get the referral realm.
+testh(realm, '', [''], 'host_realm referral realm')
+
+###
+### krb5_get_fallback_host_realm tests
+###
+
+# Return a special environment with realm_try_domains set to n.
+def try_env(realm, testname, n):
+ conf = {'libdefaults': {'realm_try_domains': str(n)}}
+ return realm.special_env(testname, False, krb5_conf=conf)
+
+# The domain module will answer with the uppercased parent domain,
+# with no special configuration.
+testf(realm, 'a.b.c', ['B.C'], 'fallback_realm domain a.b.c')
+
+# With realm_try_domains = 0, the hostname itself will be looked up as
+# a realm and returned if found.
+try0 = try_env(realm, 'try0', 0)
+testf(realm, 'krbtest.com', ['KRBTEST.COM'], 'fallback_realm try0', env=try0)
+testf(realm, 'a.b.krbtest.com', ['B.KRBTEST.COM'],
+ 'fallback_realm try0 grandparent', env=try0)
+testf(realm, 'a.b.c', ['B.C'], 'fallback_realm try0 nomatch', env=try0)
+
+# With realm_try_domains = 2, the parent and grandparent will be
+# checked as well, but it stops there.
+try2 = try_env(realm, 'try2', 2)
+testf(realm, 'krbtest.com', ['KRBTEST.COM'], 'fallback_realm try2', env=try2)
+testf(realm, 'a.b.krbtest.com', ['KRBTEST.COM'],
+ 'fallback_realm try2 grandparent', env=try2)
+testf(realm, 'a.b.c.krbtest.com', ['B.C.KRBTEST.COM'],
+ 'fallback_realm try2 great-grandparent', env=try2)
+
+# The test1 module answers with a list of components. Use an IPv4
+# address to bypass the domain module.
+testf(realm, '1.2.3.4', ['1', '2', '3', '4'], 'fallback_realm test1')
+
+# If no module answers, the default realm is returned. The test2
+# module returns an error when we try to look that up.
+testf_error(realm, '', 'service not available', 'fallback_realm default')
+
+###
+### krb5_get_default_realm tests
+###
+
+# The test2 module returns an error.
+testd_error(realm, 'service not available', 'default_realm test2')
+
+# The profile module returns the default realm from the profile.
+# Disable test2 to expose this behavior.
+disable_conf = {'plugins': {'hostrealm': {'disable': 'test2'}}}
+notest2 = realm.special_env('notest2', False, krb5_conf=disable_conf)
+testd(realm, 'KRBTEST.COM', 'default_realm profile', env=notest2)
+
+# The test1 module returns a list of two realms, of which we can only
+# see the first. Remove the profile default_realm setting to expose
+# this behavior.
+remove_default = {'libdefaults': {'default_realm': None}}
+nodefault_conf = dict(disable_conf.items() + remove_default.items())
+nodefault = realm.special_env('nodefault', False, krb5_conf=nodefault_conf)
+testd(realm, 'one', 'default_realm test1', env=nodefault)
+
+success('hostrealm interface tests')