/tests/data/.placeholder
/tests/data/dictionary.*
/tests/heimdal/plugin
+/tests/mit/plugin
/tests/portable/asprintf-t
/tests/portable/snprintf-t
/tests/portable/strlcat-t
The krb5-strength package as a whole is:
- Copyright 2006, 2007, 2009 Board of Trustees, Leland Stanford Jr.
+ Copyright 2006, 2007, 2009, 2010 Board of Trustees, Leland Stanford Jr.
University. All rights reserved.
and released under the following license:
# Automake makefile for krb5-strength.
#
# Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2007, 2009 Board of Trustees, Leland Stanford Jr. University
+# Copyright 2007, 2009, 2010 Board of Trustees, Leland Stanford Jr. University
#
# See LICENSE for licensing terms.
AUTOMAKE_OPTIONS = foreign subdir-objects
ACLOCAL_AMFLAGS = -I m4
-EXTRA_DIST = autogen cracklib/HISTORY cracklib/LICENCE cracklib/README \
- cracklib/mkdict patches/README patches/mit-krb5-1.4.4 tests/TESTS \
- tests/heimdal/external-t tests/heimdal/plugin-t tests/tap/libtap.sh
+EXTRA_DIST = autogen cracklib/HISTORY cracklib/LICENCE cracklib/README \
+ cracklib/mkdict patches/README patches/mit-krb5-1.4.4 tests/TESTS \
+ tests/heimdal/external-t tests/heimdal/plugin-t tests/mit/plugin-t \
+ tests/tap/libtap.sh
# Do this globally. Everything needs to find the Kerberos headers and
# libraries.
# Rules for building the password strength plugin.
module_LTLIBRARIES = plugin/passwd_strength.la
-plugin_passwd_strength_la_SOURCES = plugin/api.c plugin/heimdal.c
+plugin_passwd_strength_la_SOURCES = plugin/api.c plugin/heimdal.c plugin/mit.c
plugin_passwd_strength_la_LDFLAGS = -module -avoid-version
plugin_passwd_strength_la_LIBADD = cracklib/libcracklib.la \
portable/libportable.la
$(MAKE) V=0 CFLAGS='$(WARNINGS)' $(check_PROGRAMS)
# The bits below are for the test suite, not for the main package.
-check_PROGRAMS = tests/heimdal/plugin tests/portable/asprintf-t \
- tests/portable/snprintf-t tests/portable/strlcat-t \
- tests/portable/strlcpy-t tests/runtests
+check_PROGRAMS = tests/heimdal/plugin tests/mit/plugin \
+ tests/portable/asprintf-t tests/portable/snprintf-t \
+ tests/portable/strlcat-t tests/portable/strlcpy-t tests/runtests
tests_runtests_CPPFLAGS = -DSOURCE='"$(abs_top_srcdir)/tests"' \
-DBUILD='"$(abs_top_builddir)/tests"'
check_LIBRARIES = tests/tap/libtap.a
tests_heimdal_plugin_SOURCES = tests/heimdal/plugin.c
tests_heimdal_plugin_LDADD = portable/libportable.la $(KRB5_LIBS) $(DL_LIBS)
+tests_mit_plugin_SOURCES = tests/mit/plugin.c
+tests_mit_plugin_LDADD = portable/libportable.la $(KRB5_LIBS) $(DL_LIBS)
tests_portable_asprintf_t_SOURCES = tests/portable/asprintf-t.c \
tests/portable/asprintf.c
tests_portable_asprintf_t_LDADD = tests/tap/libtap.a portable/libportable.la
dynamically loaded password strength checking API and can be used as a
Heimdal kadmin plugin.
+ Add a new plugin API for MIT Kerberos modelled after the plugin API
+ used for other MIT Kerberos plugins. Thanks to Marcus Watts for
+ substantial research and contributions to the interface design.
+
Fixed the data format written by the included packer program to add
enough nul bytes at the end of the data. Previously, there was not
enough trailing nul bytes for the expected input format, leading to
dnl Process this file with autoconf to produce a configure script.
dnl
dnl Written by Russ Allbery <rra@stanford.edu>
-dnl Copyright 2006, 2007, 2009
+dnl Copyright 2006, 2007, 2009, 2010
dnl Board of Trustees, Leland Stanford Jr. University
dnl
dnl See LICENSE for licensing terms.
RRA_LIB_KRB5
RRA_LIB_KRB5_SWITCH
-AC_CHECK_HEADERS([kadm5-pwcheck.h])
+AC_CHECK_HEADERS([kadm5-pwcheck.h krb5/pwcheck_plugin.h])
AC_CHECK_TYPES([krb5_realm], , , [#include <krb5.h>])
RRA_LIB_KRB5_RESTORE
--- /dev/null
+/*
+ * MIT Kerberos shared module API.
+ *
+ * This is the glue required for a Heimdal password quality check via a
+ * dynamically loaded module. Retrieves the dictionary path from krb5.conf.
+ * This may change in later versions via a mechanism to pass profile
+ * information from kadmind to the plugin.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * Copyright 2010 Board of Trustees, Leland Stanford Jr. Unversity
+ *
+ * See LICENSE for licensing terms.
+ */
+
+#include <config.h>
+#include <portable/system.h>
+
+#include <errno.h>
+#include <krb5.h>
+
+#include <plugin/api.h>
+
+/* Skip this entire file if building with Heimdal. */
+#ifndef HAVE_KRB5_REALM
+
+/* Used for unused parameters to silence gcc warnings. */
+#define UNUSED __attribute__((__unused__))
+
+/* Allow for a build without the plugin header. */
+# ifdef HAVE_KRB5_PWCHECK_PLUGIN_H
+# include <krb5/pwcheck_plugin.h>
+# else
+typedef struct krb5plugin_kadmin_pwcheck_ftable_v0 {
+ int minor_version;
+ krb5_error_code (*init)(krb5_context, void **);
+ void (*fini)(krb5_context, void *);
+ int (*check)(krb5_context, void *, krb5_const_principal,
+ const krb5_data *password);
+} krb5plugin_kadmin_pwcheck_ftable_v0;
+# endif /* !HAVE_KRB5_PWCHECK_PLUGIN_H */
+
+
+/*
+ * Initialize the library. We can't just call pwcheck_init, since currently
+ * kadmind doesn't tell us the dictionary path. So first look up where the
+ * dictionary is, and then call pwcheck_init.
+ */
+static krb5_error_code
+init(krb5_context context, void **data)
+{
+ char *dictionary = NULL;
+
+ krb5_appdefault_string(context, "krb5-strength", NULL,
+ "password_dictionary", "", &dictionary);
+ if (dictionary == NULL || dictionary[0] == '\0') {
+ krb5_set_error_message(context, KRB5_PLUGIN_OP_NOTSUPP,
+ "password_dictionary not configured in"
+ " krb5.conf");
+ return KRB5_PLUGIN_OP_NOTSUPP;
+ }
+ if (pwcheck_init(data, dictionary) != 0) {
+ krb5_set_error_message(context, errno, "Cannot initialize strength"
+ " checking with dictionary %s: %s", dictionary,
+ strerror(errno));
+ return errno;
+ }
+ return 0;
+}
+
+
+/*
+ * Check the password. We need to transform the krb5_data struct and the
+ * principal passed us by kadmind into nul-terminated strings for our check.
+ */
+static krb5_error_code
+check(krb5_context context, void *data, krb5_const_principal princ,
+ const krb5_data *password)
+{
+ char *pastring;
+ char *name = NULL;
+ krb5_error_code status;
+ char message[BUFSIZ];
+
+ status = krb5_unparse_name(context, princ, &name);
+ if (status != 0)
+ return status;
+ pastring = malloc(password->length + 1);
+ if (pastring == NULL) {
+ status = errno;
+ krb5_set_error_message(context, status, "%s", strerror(status));
+ krb5_free_unparsed_name(context, name);
+ return status;
+ }
+ memcpy(pastring, password->data, password->length);
+ pastring[password->length] = '\0';
+ status = pwcheck_check(data, pastring, name, message, sizeof(message));
+ if (status != 0)
+ krb5_set_error_message(context, status, "%s", message);
+ free(pastring);
+ krb5_free_unparsed_name(context, name);
+ return status;
+}
+
+
+/*
+ * Shut down the library.
+ */
+static void
+fini(krb5_context context UNUSED, void *data)
+{
+ pwcheck_close(data);
+}
+
+
+/* The public symbol that MIT Kerberos looks for. */
+krb5plugin_kadmin_pwcheck_ftable_v0 kadmin_pwcheck_0 = {
+ 0, init, fini, check
+};
+
+#endif /* !HAVE_KRB5_REALM */
heimdal/external
heimdal/plugin
+mit/plugin
portable/asprintf
portable/snprintf
portable/strlcat
--- /dev/null
+#!/bin/sh
+#
+# Test suite wrapper for the MIT Kerberos shared module API.
+#
+# Written by Russ Allbery <rra@stanford.edu>
+# Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University
+#
+# See LICENSE for licensing terms.
+
+. "$SOURCE/tap/libtap.sh"
+cd "$BUILD/data"
+
+# Run the plugin program to check a password. Takes the test description,
+# the principal, the password, the expected exit status, and the expected
+# output to standard error.
+ok_password () {
+ local desc princ password w_status w_stderr status stderr
+ desc="$1"
+ princ="$2"
+ password="$3"
+ w_status="$4"
+ w_stderr="$5"
+ stderr=`"$BUILD/mit/plugin" "$princ" "$password" 2>&1`
+ status="$?"
+ echo "# status: $status"
+ echo "# stderr: $stderr"
+ ok "$desc: status" [ "$status" -eq "$w_status" ]
+ ok "$desc: stderr" [ "$stderr" = "$w_stderr" ]
+}
+
+# We need a modified krb5.conf file to add the password_dictionary setting.
+# We first generate a modified copy of the krb5.conf file that doesn't contain
+# this setting so that we can test error handling.
+krb5conf=
+for p in /etc/krb5.conf /usr/local/etc/krb5.conf data/krb5.conf ; do
+ if [ -r "$p" ] ; then
+ krb5conf="$p"
+ sed -e '/^[ ]*password_dictionary[ ]*=/d' "$p" > ./krb5.conf
+ KRB5_CONFIG="./krb5.conf"
+ export KRB5_CONFIG
+ break
+ fi
+done
+if [ -z "$krb5conf" ] ; then
+ skip_all 'no krb5.conf found, put one in tests/data/krb5.conf'
+fi
+
+# Check whether we can run the test at all.
+"$BUILD/mit/plugin" 'test@EXAMPLE.COM' 'test' >/dev/null 2>&1
+if [ $? = 42 ] ; then
+ rm -f krb5.conf
+ skip_all 'not built against MIT Kerberos libraries'
+fi
+
+# Okay, we should be good to run the test suite.
+plan 28
+
+# We don't have a password_dictionary setting, so we should fail with an
+# initialization error.
+ok_password "no dictionary configured" 'test@EXAMPLE.ORG' 'password' 1 \
+ 'password_dictionary not configured in krb5.conf'
+
+# Now add the password dictionary configuration.
+cat <<EOF >> ./krb5.conf
+
+[appdefaults]
+ krb5-strength = {
+ password_dictionary = $BUILD/data/dictionary
+ }
+
+EOF
+
+# Check the basic functionality.
+ok_password "good password" 'test@EXAMPLE.ORG' 'known good password' 0 ''
+ok_password "password in dictionary" 'test@EXAMPLE.ORG' 'password' 1 \
+ 'it is based on a dictionary word'
+ok_password "password in dictionary" 'test@EXAMPLE.ORG' 'bitter' 1 \
+ 'it is based on a dictionary word'
+ok_password "password in dictionary" 'test@EXAMPLE.ORG' 'rettib' 1 \
+ 'it is based on a (reversed) dictionary word'
+ok_password "password too short" 'test@EXAMPLE.ORG' 'food' 1 \
+ "it is too short"
+ok_password "password way too short" 'test@EXAMPLE.ORG' 'foo' 1 \
+ "it's WAY too short"
+ok_password "password empty" 'test@EXAMPLE.ORG' '' 1 \
+ "it's WAY too short"
+ok_password "password all whitespace" 'test@EXAMPLE.ORG' ' ' 1 \
+ 'it does not contain enough DIFFERENT characters'
+ok_password "password too simplistic" 'test@EXAMPLE.ORG' 'abcdefghi' 1 \
+ 'it is too simplistic/systematic'
+ok_password "not enough characters" 'test@EXAMPLE.ORG' '22413411' 1 \
+ 'it does not contain enough DIFFERENT characters'
+ok_password "password based on principal" 'someuser@EXAMPLE.ORG' 'someuser' \
+ 1 'Password based on username'
+ok_password "password based on principal" 'someuser@EXAMPLE.ORG' 'resuemos' \
+ 1 'Password based on username'
+ok_password "password is principal" 'test@EXAMPLE.ORG' 'test@EXAMPLE.ORG' \
+ 1 'Password based on username'
+
+# Clean up.
+rm -f krb5.conf
--- /dev/null
+/*
+ * Test for the MIT Kerberos shared module API.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * Copyright 2010 Board of Trustees, Leland Stanford Jr. University
+ *
+ * See LICENSE for licensing terms.
+ */
+
+#include <config.h>
+#include <portable/system.h>
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <krb5.h>
+
+/* Allow for a build without the plugin header. */
+# ifdef HAVE_KRB5_PWCHECK_PLUGIN_H
+# include <krb5/pwcheck_plugin.h>
+# else
+typedef struct krb5plugin_kadmin_pwcheck_ftable_v0 {
+ int minor_version;
+ krb5_error_code (*init)(krb5_context, void **);
+ void (*fini)(krb5_context, void *);
+ int (*check)(krb5_context, void *, krb5_const_principal,
+ const krb5_data *password);
+} krb5plugin_kadmin_pwcheck_ftable_v0;
+# endif /* !HAVE_KRB5_PWCHECK_PLUGIN_H */
+
+
+/*
+ * Expects a principal and a password to check on the command line. Loads the
+ * MIT Kerberos plugin, converts the input into the necessary format, calls
+ * the plugin, and reports the results. Exits with a status matching the
+ * return value of the plugin function.
+ *
+ * We assume that the plugin is available as:
+ *
+ * BUILD/../plugin/.libs/passwd_strength.so
+ *
+ * since we don't want to embed Libtool's libtldl just to run a test.
+ */
+int
+main(int argc, char *argv[])
+{
+ const char *build;
+ char *path;
+ size_t length;
+ krb5_context ctx;
+ krb5_principal princ;
+ krb5_data password;
+ krb5_error_code status;
+ void *handle, *data;
+ struct krb5plugin_kadmin_pwcheck_ftable_v0 *verifier;
+
+ /*
+ * If we're not building with MIT Kerberos, we can't run this test. Exit
+ * with a special status to communicate this to the test wrapper.
+ */
+#ifdef HAVE_KRB5_REALM
+ exit(42);
+#endif
+
+ /* Build the path of the plugin. */
+ if (argc != 3) {
+ fprintf(stderr, "Wrong number of arguments\n");
+ exit(1);
+ }
+ build = getenv("BUILD");
+ if (build == NULL) {
+ fprintf(stderr, "No BUILD environment variable set\n");
+ exit(1);
+ }
+ length = strlen(build) + strlen("/../plugin/.libs/passwd_strength.so");
+ path = malloc(length + 1);
+ if (path == NULL) {
+ fprintf(stderr, "Cannot allocate memory: %s\n", strerror(errno));
+ exit(1);
+ }
+ strlcpy(path, build, length + 1);
+ strlcat(path, "/../plugin/.libs/passwd_strength.so", length + 1);
+
+ /* Initialize the data structures. */
+ status = krb5_init_context(&ctx);
+ if (status != 0) {
+ fprintf(stderr, "Cannot initialize Kerberos context\n");
+ exit(1);
+ }
+ status = krb5_parse_name(ctx, argv[1], &princ);
+ if (status != 0) {
+ fprintf(stderr, "Cannot parse principal name\n");
+ exit(1);
+ }
+ password.length = strlen(argv[2]);
+ password.data = argv[2];
+
+ /* Load the module and find the correct symbol. */
+ handle = dlopen(path, RTLD_NOW);
+ if (handle == NULL) {
+ fprintf(stderr, "Cannot dlopen %s: %s\n", path, dlerror());
+ exit(1);
+ }
+ verifier = dlsym(handle, "kadmin_pwcheck_0");
+ if (verifier == NULL) {
+ fprintf(stderr, "Cannot get kadmin_pwcheck_0 symbol: %s\n", dlerror());
+ exit(1);
+ }
+ if (verifier->minor_version != 0
+ || verifier->init == NULL
+ || verifier->check == NULL
+ || verifier->fini == NULL) {
+ fprintf(stderr, "Invalid metadata in plugin\n");
+ exit(1);
+ }
+ status = verifier->init(ctx, &data);
+ if (status != 0) {
+ fprintf(stderr, "%s\n", krb5_get_error_message(ctx, status));
+ exit(1);
+ }
+ status = verifier->check(ctx, data, princ, &password);
+ if (status != 0)
+ fprintf(stderr, "%s\n", krb5_get_error_message(ctx, status));
+ verifier->fini(ctx, data);
+ exit(status);
+}