svn rev #24203: branches/plugins2/ pwqual_combo/ src/include/ src/include/krb5/ ...
ghudson@MIT.EDU
ghudson at MIT.EDU
Wed Jul 21 23:13:38 EDT 2010
http://mv.ezproxy.com.ezproxyberklee.flo.org/fisheye/changelog/krb5/?cs=24203
Commit By: ghudson
Log Message:
Proof of concept code for a candidate plugin framework.
Changed Files:
A branches/plugins2/README.BRANCH
A branches/plugins2/pwqual_combo/
A branches/plugins2/pwqual_combo/Makefile
A branches/plugins2/pwqual_combo/combo.c
U branches/plugins2/src/include/Makefile.in
U branches/plugins2/src/include/k5-int.h
A branches/plugins2/src/include/krb5/plugin.h
A branches/plugins2/src/include/krb5/pwqual_plugin.h
U branches/plugins2/src/lib/kadm5/server_internal.h
U branches/plugins2/src/lib/kadm5/srv/Makefile.in
U branches/plugins2/src/lib/kadm5/srv/libkadm5srv_mit.exports
A branches/plugins2/src/lib/kadm5/srv/pwqual.c
A branches/plugins2/src/lib/kadm5/srv/pwqual_dict.c
A branches/plugins2/src/lib/kadm5/srv/pwqual_policy.c
D branches/plugins2/src/lib/kadm5/srv/server_dict.c
U branches/plugins2/src/lib/kadm5/srv/server_init.c
U branches/plugins2/src/lib/kadm5/srv/server_misc.c
U branches/plugins2/src/lib/kadm5/srv/svr_principal.c
U branches/plugins2/src/lib/krb5/krb/Makefile.in
U branches/plugins2/src/lib/krb5/krb/init_ctx.c
A branches/plugins2/src/lib/krb5/krb/plugin.c
U branches/plugins2/src/lib/krb5/libkrb5.exports
Added: branches/plugins2/README.BRANCH
===================================================================
--- branches/plugins2/README.BRANCH (rev 0)
+++ branches/plugins2/README.BRANCH 2010-07-22 03:13:38 UTC (rev 24203)
@@ -0,0 +1,175 @@
+This branch demonstrates a possible krb5 plugin infrastructure.
+
+----- Design -----
+
+The design decisions made in this infrastructure are:
+
+1. Configuration for plugin module discovery and filtering
+
+Built-in modules are automatically discoverable after being registered
+by the consumer. Dynamic modules must be explicitly configured in
+order to be discoverable. The discoverable set of modules can be
+filtered by either enabling a specific set of modules by name, or
+disabling modules by name.
+
+The profile schema used in this branch is:
+
+ [preauth]
+ interfacename = {
+ # May take multiple values; only named plugins will be enabled.
+ enable_only = name
+
+ # May take multiple values; named plugins will be disabled.
+ disable = name
+
+ # Establishes a mapping from a module name to a dynamic object.
+ module = modname:pathname
+ }
+
+The expectation is that the profile library will gain include-file
+support so that mappings for dynamic objects can be specified in
+profile fragments which are owned by the OS packages which implement
+the module. Filtering rules are expected to be specified in the main
+krb5.conf by the system administrator.
+
+2. Consumer-facing API
+
+Each pluggable interface has a corresponding consumer-facing API. The
+API consists of:
+
+* An interface-specific handle type, encapsulating the type of a
+ plugin module and possibly a resource instantiating that type (e.g a
+ keytab identifier).
+
+* An interface-specific loader function, which creates a handle or a
+ list of handles. Lists of handles would be used by one-to-many
+ pluggable interfaces where the consumer wants to consult all
+ available modules.
+
+3. Producer-facing dynamic module ABI
+
+A dynamic object can implement one or more plugin modules. To
+implement a plugin module, a dynamic object exports a symbol named
+<interfacename>_<modulename>_init.
+
+Module init functions accept as arguments a krb5_context, a major
+version, a minor version, and a pointer to a caller-allocated vtable
+(passed as an abstract type). Major versions correspond to complete
+revisions of the vtable, while minor versions indicate extensions of a
+vtable type.
+
+Based on the major version, the init function casts the vtable pointer
+to the correct interface-specific type, and then fills in fields of
+that vtable, stopping as indicated by the minor version.
+
+4. Framework
+
+The following functions are used by interface-specific loader
+functions:
+
+* k5_plugin_load: Given a numeric interface ID and a module name,
+ return the init function for the named module.
+
+* k5_plugin_load_all: Given a numeric interface ID, return the init
+ functions of all modules for that interface.
+
+The following function is used by pluggable interface consumers:
+
+* k5_plugin_register: Registers a built-in plugin module under a
+ specified interface ID and plugin name.
+
+----- Branch walkthrough -----
+
+The domain-independent framework code lives in:
+
+ * include/k5-int.h -- framework declarations and context fields
+ * lib/krb5/krb/plugin.c -- the framework implementation
+ * lib/krb5/krb/init_ctx.c -- krb5_free_context addition
+
+The framework is demonstrated with a password quality pluggable
+interface used by libkadm5srv. The code for this interface lives in:
+
+ * lib/kadm5/server_internal.h -- declarations for consumer API
+ * lib/kadm5/srv/pwqual.c -- consumer API implementation
+ * lib/kadm5/srv/pwqual_dict.c -- built-in module using dictionary
+ * lib/kadm5/srv/pwqual_policy.c -- built-in module using policy
+ * lib/kadm5/srv/server_misc.c -- consumer logic
+ * lib/kadm5/srv/server_dict.c -- removed (logic moved to pwqual_dict.c)
+ * lib/kadm5/srv/svr_principal.c -- some call sites adjusted
+ * lib/kadm5/srv/server_init.c -- some call sites adjusted
+
+There is also a sample dynamic plugin implementation in the directory
+pwqual_combo (at the top level, not under src). This code simulates a
+third-party plugin and so uses its own (not very good) build system.
+The module rejects passwords which are combinations of two words from
+the dictionary file.
+
+----- Trying out the code -----
+
+These steps demonstrate the functioning of the code.
+
+1. Build the branch normally and install it somewhere.
+
+2. cd into the pwqual_combo directory and build it with "make
+ -I/path/to/install/include". The Makefile probably only works on
+ Linux-based operating systems.
+
+3. Go back to the main build directory and run "make testrealm" to
+ create a functioning environment.
+
+4. Add the following configuration to testdir/krb5.master.conf to make
+ pwqual_combo discoverable:
+
+ [plugins]
+ pwqual = {
+ module = combo:/path/to/pwqual_combo.so
+ }
+
+5. Create a file /tmp/dict containing the lines "books" and "sharks".
+ In the realm definition for KRBTEST.COM in krb5.master.conf, add
+ the setting "dict_file = /tmp/dict".
+
+6. Run kadmin.local and create a policy with "addpol -minlength 4
+testpolicy". Associated it with the principal user with "modprinc
+-policy testpolicy user".
+
+7. Inside kadmin.local, try some password change with "cpw user". You
+should be able to see that all three password quality modules are
+functioning: you won't be able to set passwords shorter than four
+characters long (the policy module), or the passwords "books" or
+"sharks" (the dict module), or passwords named "sharksbooks" or
+"bookssharks" (the combo module).
+
+8. Quit out of kadmin.local and edit testdir/krb5.master.conf again.
+Play with the filtering rules by adding, alongside the "module"
+directive, one or more assignments for enable_only and/or disable.
+For instance, if you disable the policy module, you should find that
+(upon restarting kadmin.local) you can set passwords shorter than four
+characters again.
+
+----- What's wrong with this branch -----
+
+The krb5 code on this branch is mostly complete, but as a
+demonstration branch it is not perfect. Problems include:
+
+* In some cases (marked by XXX comments), overly vague error codes are
+ returned where new error codes should have been created. This is
+ because the krb5 trunk's krb5 error table is currently full, and
+ rectifying that problem is out of scope for the branch.
+
+* Opening and closing password quality plugins should perhaps be
+ hidden by the password quality consumer API--that is, the open
+ method should be invoked by the loader, and the close method by
+ k5_pwqual_free_handles. Currently the responsibility for invoking
+ these methods rests with the consumer code in server_misc.c.
+
+* At Tom's suggestion, new internal functions with external linkage
+ are using the prefix "k5_" instead of "krb5int_". This practice
+ should be validated by the dev community (and perhaps made uniform
+ in the code base, although that would result in a lot of churn) or
+ abandoned.
+
+* The decisions about what is a typedef and what is a simple structure
+ type are kind of haphazard, erring on the side of using typedefs.
+
+* The Hesiod support in server_misc.c was ripped out.
Added: branches/plugins2/pwqual_combo/Makefile
===================================================================
--- branches/plugins2/pwqual_combo/Makefile (rev 0)
+++ branches/plugins2/pwqual_combo/Makefile 2010-07-22 03:13:38 UTC (rev 24203)
@@ -0,0 +1,14 @@
+# Dummy non-portable build system for sample password quality plugin.
+
+INCLUDES = -I/path/to/krb5/prefix/include
+
+all: pwqual_combo.so
+
+pwqual_combo.so: combo.so
+ gcc -shared -fPIC -o $@ combo.so
+
+combo.so: combo.c
+ gcc -fPIC -DSHARED $(INCLUDES) -g -c combo.c -o $@
+
+clean:
+ rm -f combo.so pwqual_combo.so
Added: branches/plugins2/pwqual_combo/combo.c
===================================================================
--- branches/plugins2/pwqual_combo/combo.c (rev 0)
+++ branches/plugins2/pwqual_combo/combo.c 2010-07-22 03:13:38 UTC (rev 24203)
@@ -0,0 +1,183 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * Sample password quality plugin which checks for dictionary word combos
+ */
+
+
+#include <krb5.h>
+#include <krb5/pwqual_plugin.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+typedef struct combo_moddata_st {
+ char **word_list; /* list of word pointers */
+ char *word_block; /* actual word data */
+ size_t word_count; /* number of words */
+} *combo_moddata;
+
+static int
+word_compare(const void *s1, const void *s2)
+{
+ return (strcasecmp(*(const char **)s1, *(const char **)s2));
+}
+
+static krb5_error_code
+init_dict(combo_moddata dict, const char *dict_file)
+{
+ int fd;
+ size_t len, i;
+ char *p, *t;
+ struct stat sb;
+
+ if (dict_file == NULL)
+ return 0;
+ if ((fd = open(dict_file, O_RDONLY)) == -1)
+ return (errno == ENOENT) ? 0 : errno;
+ if (fstat(fd, &sb) == -1) {
+ close(fd);
+ return errno;
+ }
+ if ((dict->word_block = malloc(sb.st_size + 1)) == NULL)
+ return ENOMEM;
+ if (read(fd, dict->word_block, sb.st_size) != sb.st_size)
+ return errno;
+ (void) close(fd);
+ dict->word_block[sb.st_size] = '\0';
+
+ p = dict->word_block;
+ len = sb.st_size;
+ while(len > 0 && (t = memchr(p, '\n', len)) != NULL) {
+ *t = '\0';
+ len -= t - p + 1;
+ p = t + 1;
+ dict->word_count++;
+ }
+ if ((dict->word_list = malloc(dict->word_count * sizeof(char *))) == NULL)
+ return ENOMEM;
+ p = dict->word_block;
+ for (i = 0; i < dict->word_count; i++) {
+ dict->word_list[i] = p;
+ p += strlen(p) + 1;
+ }
+ qsort(dict->word_list, dict->word_count, sizeof(char *), word_compare);
+ return 0;
+}
+
+static void
+destroy_dict(combo_moddata dict)
+{
+ if (dict == NULL)
+ return;
+ free(dict->word_list);
+ free(dict->word_block);
+ free(dict);
+ return;
+}
+
+static krb5_error_code
+combo_open(krb5_context context, const char *dict_file,
+ krb5_pwqual_moddata *data)
+{
+ krb5_error_code ret;
+ combo_moddata dict;
+
+ *data = NULL;
+
+ /* Allocate and initialize a dictionary structure. */
+ dict = malloc(sizeof(*dict));
+ if (dict == NULL)
+ return ENOMEM;
+ dict->word_list = NULL;
+ dict->word_block = NULL;
+ dict->word_count = 0;
+
+ /* Fill in the dictionary structure with data from dict_file. */
+ ret = init_dict(dict, dict_file);
+ if (ret != 0) {
+ destroy_dict(dict);
+ return ret;
+ }
+
+ *data = (krb5_pwqual_moddata)dict;
+ return 0;
+}
+
+static krb5_error_code
+combo_check(krb5_context context, krb5_pwqual_moddata data,
+ const char *password, kadm5_policy_ent_t policy,
+ krb5_principal princ)
+{
+ combo_moddata dict = (combo_moddata)data;
+ size_t i, j, len, pwlen;
+ const char *remainder;
+
+ if (dict->word_list == NULL)
+ return 0;
+
+ pwlen = strlen(password);
+ for (i = 0; i < dict->word_count; i++) {
+ len = strlen(dict->word_list[i]);
+ if (len >= pwlen)
+ continue;
+ if (strncasecmp(password, dict->word_list[i], len) != 0)
+ continue;
+ remainder = password + len;
+ for (i = 0; i < dict->word_count; i++) {
+ if (strcasecmp(remainder, dict->word_list[i]) == 0)
+ return KADM5_PASS_Q_DICT;
+ }
+ }
+
+ return 0;
+}
+
+static void
+combo_close(krb5_context context, krb5_pwqual_moddata data)
+{
+ destroy_dict((combo_moddata)data);
+}
+
+krb5_error_code
+pwqual_combo_init(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_pwqual_vtable vt;
+
+ if (maj_ver != 1)
+ return EINVAL; /* XXX create error code */
+ vt = (krb5_pwqual_vtable)vtable;
+ vt->open = combo_open;
+ vt->check = combo_check;
+ vt->close = combo_close;
+ return 0;
+}
Modified: branches/plugins2/src/include/Makefile.in
===================================================================
--- branches/plugins2/src/include/Makefile.in 2010-07-22 03:03:15 UTC (rev 24202)
+++ branches/plugins2/src/include/Makefile.in 2010-07-22 03:13:38 UTC (rev 24203)
@@ -136,6 +136,8 @@
$(INSTALL_DATA) $(srcdir)/kdb.h $(DESTDIR)$(KRB5_INCDIR)$(S)kdb.h
$(INSTALL_DATA) krb5/krb5.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)krb5.h
$(INSTALL_DATA) $(srcdir)/krb5/locate_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)locate_plugin.h
+ $(INSTALL_DATA) $(srcdir)/krb5/plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)plugin.h
+ $(INSTALL_DATA) $(srcdir)/krb5/pwqual_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)pwqual_plugin.h
$(INSTALL_DATA) profile.h $(DESTDIR)$(KRB5_INCDIR)$(S)profile.h
$(INSTALL_DATA) $(srcdir)/gssapi.h $(DESTDIR)$(KRB5_INCDIR)$(S)gssapi.h
Modified: branches/plugins2/src/include/k5-int.h
===================================================================
--- branches/plugins2/src/include/k5-int.h 2010-07-22 03:03:15 UTC (rev 24202)
+++ branches/plugins2/src/include/k5-int.h 2010-07-22 03:13:38 UTC (rev 24203)
@@ -168,6 +168,7 @@
*/
#include <errno.h>
#include "krb5.h"
+#include <krb5/plugin.h>
#include "profile.h"
#include "port-sockets.h"
@@ -205,12 +206,14 @@
#define KRB5_CONF_DEFAULT_PRINCIPAL_EXPIRATION "default_principal_expiration"
#define KRB5_CONF_DEFAULT_PRINCIPAL_FLAGS "default_principal_flags"
#define KRB5_CONF_DICT_FILE "dict_file"
+#define KRB5_CONF_DISABLE "disable"
#define KRB5_CONF_DISABLE_LAST_SUCCESS "disable_last_success"
#define KRB5_CONF_DISABLE_LOCKOUT "disable_lockout"
#define KRB5_CONF_DNS_LOOKUP_KDC "dns_lookup_kdc"
#define KRB5_CONF_DNS_LOOKUP_REALM "dns_lookup_realm"
#define KRB5_CONF_DNS_FALLBACK "dns_fallback"
#define KRB5_CONF_DOMAIN_REALM "domain_realm"
+#define KRB5_CONF_ENABLE_ONLY "enable_only"
#define KRB5_CONF_EXTRA_ADDRESSES "extra_addresses"
#define KRB5_CONF_FORWARDABLE "forwardable"
#define KRB5_CONF_HOST_BASED_SERVICES "host_based_services"
@@ -245,9 +248,11 @@
#define KRB5_CONF_MASTER_KDC "master_kdc"
#define KRB5_CONF_MAX_LIFE "max_life"
#define KRB5_CONF_MAX_RENEWABLE_LIFE "max_renewable_life"
+#define KRB5_CONF_MODULE "module"
#define KRB5_CONF_NOADDRESSES "noaddresses"
#define KRB5_CONF_NO_HOST_REFERRAL "no_host_referral"
#define KRB5_CONF_PERMITTED_ENCTYPES "permitted_enctypes"
+#define KRB5_CONF_PLUGINS "plugins"
#define KRB5_CONF_PREAUTH_MODULE_DIR "preauth_module_dir"
#define KRB5_CONF_PREFERRED_PREAUTH_TYPES "preferred_preauth_types"
#define KRB5_CONF_PROXIABLE "proxiable"
@@ -1424,6 +1429,56 @@
krb5_authdata_context context, const char *module,
void *ptr);
+/* Plugin framework */
+
+/*
+ * A linked list entry mapping a module name to a module init function. The
+ * entry may also include a dynamic object handle so that it can be released
+ * when the context is destroyed.
+ */
+struct plugin_mapping {
+ char *modname;
+ krb5_plugin_init_fn module;
+ struct plugin_file_handle *dyn_handle;
+ struct plugin_mapping *next;
+};
+
+/* Holds krb5_context information about each pluggable interface. */
+struct plugin_interface {
+ struct plugin_mapping *modules;
+ krb5_boolean configured;
+};
+
+/* A list of plugin interface IDs. Make sure to increment
+ * PLUGIN_NUM_INTERFACES when a new interface is added. */
+#define PLUGIN_INTERFACE_PWQUAL 0
+#define PLUGIN_NUM_INTERFACES 1
+
+/* Retrieve the plugin module of type interface_id and name modname,
+ * storing the result into module. */
+krb5_error_code
+k5_plugin_load(krb5_context context, int interface_id, const char *modname,
+ krb5_plugin_init_fn *module);
+
+/* Retrieve all plugin modules of type interface_id, storing the result
+ * into modules. Free the result with k5_plugin_free_handles. */
+krb5_error_code
+k5_plugin_load_all(krb5_context context, int interface_id,
+ krb5_plugin_init_fn **modules);
+
+/* Release a module list allocated by k5_plugin_load_all. */
+void
+k5_plugin_free_modules(krb5_context context, krb5_plugin_init_fn *modules);
+
+/* Register a plugin module of type interface_id and name modname. */
+krb5_error_code
+k5_plugin_register(krb5_context context, int interface_id, const char *modname,
+ krb5_plugin_init_fn module);
+
+/* Destroy the module state within context; used by krb5_free_context. */
+void
+k5_plugin_free_context(krb5_context context);
+
struct _kdb5_dal_handle; /* private, in kdb5.h */
typedef struct _kdb5_dal_handle kdb5_dal_handle;
struct _kdb_log_context;
@@ -1478,6 +1533,8 @@
krb5_trace_callback trace_callback;
void *trace_callback_data;
+
+ struct plugin_interface plugins[PLUGIN_NUM_INTERFACES];
};
/* could be used in a table to find an etype and initialize a block */
Added: branches/plugins2/src/include/krb5/plugin.h
===================================================================
--- branches/plugins2/src/include/krb5/plugin.h (rev 0)
+++ branches/plugins2/src/include/krb5/plugin.h 2010-07-22 03:13:38 UTC (rev 24203)
@@ -0,0 +1,48 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * <krb5/plugin.h>
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * Generic declarations for dynamic modules implementing krb5 plugin modules.
+ */
+
+#ifndef KRB5_PLUGIN_H
+#define KRB5_PLUGIN_H
+
+/* krb5_plugin_vtable is an abstract type. Module init functions will cast it
+ * to the appropriate interface-specific vtable type. */
+typedef struct krb5_plugin_vtable_st *krb5_plugin_vtable;
+
+/*
+ krb5_plugin_init_fn is the type of a module init function. Based on the
+ * maj_ver argument, the init function should cast vtable to the appropriate
+ * type and then fill it in. If a vtable has been expanded, min_ver indicates
+ * which version of the vtable is being filled in.
+ */
+typedef krb5_error_code
+(*krb5_plugin_init_fn)(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+#endif /* KRB5_PLUGIN_H */
Added: branches/plugins2/src/include/krb5/pwqual_plugin.h
===================================================================
--- branches/plugins2/src/include/krb5/pwqual_plugin.h (rev 0)
+++ branches/plugins2/src/include/krb5/pwqual_plugin.h 2010-07-22 03:13:38 UTC (rev 24203)
@@ -0,0 +1,60 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * prototype/prototype.h
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * Declarations for password quality plugin module implementors.
+ */
+
+#ifndef KRB5_PWQUAL_PLUGIN_H
+#define KRB5_PWQUAL_PLUGIN_H
+
+#include <krb5/krb5.h>
+#include <krb5/plugin.h>
+#include <kadm5/admin.h>
+#include <kdb.h>
+
+/* An abstract type for password quality module data. */
+typedef struct krb5_pwqual_moddata_st *krb5_pwqual_moddata;
+
+/* Password quality plugin vtable for major version 1. */
+typedef struct krb5_pwqual_vtable_st {
+ /* Optional: Initialize module data. dictfile is the realm's configured
+ * dictionary filename. */
+ krb5_error_code (*open)(krb5_context context, const char *dict_file,
+ krb5_pwqual_moddata *data);
+
+ /* Mandatory: Check a password for the principal princ, possibly making use
+ * of the password policy given by policy. Return an error if the password
+ * check fails. */
+ krb5_error_code (*check)(krb5_context context, krb5_pwqual_moddata data,
+ const char *password, kadm5_policy_ent_t policy,
+ krb5_principal princ);
+
+ /* Optional: Release resources used by module data. */
+ void (*close)(krb5_context context, krb5_pwqual_moddata data);
+} *krb5_pwqual_vtable;
+
+#endif /* KRB5_PWQUAL_PLUGIN_H */
Modified: branches/plugins2/src/lib/kadm5/server_internal.h
===================================================================
--- branches/plugins2/src/lib/kadm5/server_internal.h 2010-07-22 03:03:15 UTC (rev 24202)
+++ branches/plugins2/src/lib/kadm5/server_internal.h 2010-07-22 03:13:38 UTC (rev 24203)
@@ -22,6 +22,7 @@
#include <errno.h>
#include <kdb.h>
#include <kadm5/admin.h>
+#include <krb5/plugin.h>
#include "admin_internal.h"
/*
@@ -33,6 +34,9 @@
*/
#define INITIAL_HIST_KVNO 2
+/* A pwqual_handle represents a password quality plugin module. */
+typedef struct pwqual_handle_st *pwqual_handle;
+
typedef struct _kadm5_server_handle_t {
krb5_ui_4 magic_number;
krb5_ui_4 struct_version;
@@ -42,6 +46,7 @@
kadm5_config_params params;
struct _kadm5_server_handle_t *lhandle;
char **db_args;
+ pwqual_handle *qual_handles;
} kadm5_server_handle_rec, *kadm5_server_handle_t;
#define OSA_ADB_PRINC_VERSION_1 0x12345C01
@@ -65,8 +70,7 @@
kadm5_ret_t adb_policy_init(kadm5_server_handle_t handle);
kadm5_ret_t adb_policy_close(kadm5_server_handle_t handle);
kadm5_ret_t passwd_check(kadm5_server_handle_t handle,
- char *pass, int use_policy,
- kadm5_policy_ent_t policy,
+ const char *pass, kadm5_policy_ent_t policy,
krb5_principal principal);
kadm5_ret_t principal_exists(krb5_principal principal);
krb5_error_code kdb_init_master(kadm5_server_handle_t handle,
@@ -90,9 +94,8 @@
void (*iter_fct)(void *, krb5_principal),
void *data);
-int init_dict(kadm5_config_params *);
-int find_word(const char *word);
-void destroy_dict(void);
+kadm5_ret_t init_pwqual(kadm5_server_handle_t handle);
+void destroy_pwqual(kadm5_server_handle_t handle);
/* XXX this ought to be in libkrb5.a, but isn't */
kadm5_ret_t krb5_copy_key_data_contents(krb5_context context,
@@ -153,4 +156,44 @@
void
osa_free_princ_ent(osa_princ_ent_t val);
+/*** Password quality plugin consumer interface ***/
+
+/* Load the available password quality plugins and store the result into
+ * *handles. Free the result with k5_pwqual_free_handles. */
+krb5_error_code
+k5_pwqual_load(krb5_context context, pwqual_handle **handles);
+
+/* Release a handle list allocated by k5_pwqual_load. All modules must have
+ * been closed by the caller. */
+krb5_error_code
+k5_pwqual_free_handles(krb5_context context, pwqual_handle *handles);
+
+/* Initialize a password quality plugin, possibly using the realm's configured
+ * dictionary filename. */
+krb5_error_code
+k5_pwqual_open(krb5_context context, pwqual_handle handle,
+ const char *dict_file);
+
+/* Check a password using a password quality plugin. */
+krb5_error_code
+k5_pwqual_check(krb5_context context, pwqual_handle handle,
+ const char *password, kadm5_policy_ent_t policy,
+ krb5_principal princ);
+
+/* Release the memory used by a password quality plugin. */
+void
+k5_pwqual_close(krb5_context context, pwqual_handle handle);
+
+/*** Init functions for built-in password quality modules ***/
+
+/* The dict module checks passwords against the realm's dictionary. */
+krb5_error_code
+pwqual_dict_init(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+/* The policy module enforces password policy constraints. */
+krb5_error_code
+pwqual_policy_init(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
#endif /* __KADM5_SERVER_INTERNAL_H__ */
Modified: branches/plugins2/src/lib/kadm5/srv/Makefile.in
===================================================================
--- branches/plugins2/src/lib/kadm5/srv/Makefile.in 2010-07-22 03:03:15 UTC (rev 24202)
+++ branches/plugins2/src/lib/kadm5/srv/Makefile.in 2010-07-22 03:13:38 UTC (rev 24203)
@@ -27,36 +27,42 @@
SHLIB_RDIRS=$(KRB5_LIBDIR)
RELDIR=kadm5/srv
-SRCS = $(srcdir)/svr_policy.c \
+SRCS = $(srcdir)/pwqual.c \
+ $(srcdir)/pwqual_dict.c \
+ $(srcdir)/pwqual_policy.c \
+ $(srcdir)/svr_policy.c \
$(srcdir)/svr_principal.c \
$(srcdir)/server_acl.c \
$(srcdir)/server_kdb.c \
$(srcdir)/server_misc.c \
$(srcdir)/server_init.c \
- $(srcdir)/server_dict.c \
$(srcdir)/svr_iters.c \
$(srcdir)/svr_chpass_util.c \
$(srcdir)/adb_xdr.c
-OBJS = svr_policy.$(OBJEXT) \
+OBJS = pwqual.$(OBJEXT) \
+ pwqual_dict.$(OBJEXT) \
+ pwqual_policy.$(OBJECT) \
+ svr_policy.$(OBJEXT) \
svr_principal.$(OBJEXT) \
server_acl.$(OBJEXT) \
server_kdb.$(OBJEXT) \
server_misc.$(OBJEXT) \
server_init.$(OBJEXT) \
- server_dict.$(OBJEXT) \
svr_iters.$(OBJEXT) \
svr_chpass_util.$(OBJEXT) \
adb_xdr.$(OBJEXT)
STLIBOBJS = \
+ pwqual.o \
+ pwqual_dict.o \
+ pwqual_policy.o \
svr_policy.o \
svr_principal.o \
server_acl.o \
server_kdb.o \
server_misc.o \
server_init.o \
- server_dict.o \
svr_iters.o \
svr_chpass_util.o \
adb_xdr.o
Modified: branches/plugins2/src/lib/kadm5/srv/libkadm5srv_mit.exports
===================================================================
--- branches/plugins2/src/lib/kadm5/srv/libkadm5srv_mit.exports 2010-07-22 03:03:15 UTC (rev 24202)
+++ branches/plugins2/src/lib/kadm5/srv/libkadm5srv_mit.exports 2010-07-22 03:13:38 UTC (rev 24203)
@@ -7,10 +7,7 @@
kadm5int_acl_init
adb_policy_close
adb_policy_init
-destroy_dict
-find_word
hist_princ
-init_dict
kadm5_set_use_password_server
kadm5_chpass_principal
kadm5_chpass_principal_3
Added: branches/plugins2/src/lib/kadm5/srv/pwqual.c
===================================================================
--- branches/plugins2/src/lib/kadm5/srv/pwqual.c (rev 0)
+++ branches/plugins2/src/lib/kadm5/srv/pwqual.c 2010-07-22 03:13:38 UTC (rev 24203)
@@ -0,0 +1,114 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * lib/kadm5/srv/pwqual.c
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * Consumer interface for password quality plugins
+ */
+
+#include "k5-int.h"
+#include "server_internal.h"
+#include <krb5/pwqual_plugin.h>
+
+struct pwqual_handle_st {
+ struct krb5_pwqual_vtable_st vt;
+ krb5_pwqual_moddata data;
+};
+
+krb5_error_code
+k5_pwqual_load(krb5_context context, pwqual_handle **handles)
+{
+ krb5_error_code ret;
+ krb5_plugin_init_fn *modules = NULL, *mod;
+ size_t count;
+ pwqual_handle *list = NULL, handle = NULL;
+
+ ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_PWQUAL, &modules);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Allocate a large enough list of handles. */
+ for (count = 0; modules[count] != NULL; count++);
+ list = k5alloc((count + 1) * sizeof(*list), &ret);
+ if (list == NULL)
+ goto cleanup;
+
+ /* For each module, allocate a handle and initialize its vtable. Skip
+ * modules which don't successfully initialize. */
+ count = 0;
+ for (mod = modules; *mod != NULL; mod++) {
+ handle = k5alloc(sizeof(*handle), &ret);
+ if (handle == NULL)
+ goto cleanup;
+ ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&handle->vt);
+ if (ret == 0)
+ list[count++] = handle;
+ else
+ free(handle);
+ }
+
+ *handles = list;
+ list = NULL;
+
+cleanup:
+ k5_plugin_free_modules(context, modules);
+ k5_pwqual_free_handles(context, list);
+ return ret;
+}
+
+krb5_error_code
+k5_pwqual_free_handles(krb5_context context, pwqual_handle *handles)
+{
+ /* It's the caller's responsibility to close each handle, so all of the
+ * module data should be freed by now, leaving only the list itself. */
+ free(handles);
+}
+
+krb5_error_code
+k5_pwqual_open(krb5_context context, pwqual_handle handle,
+ const char *dict_file)
+{
+ if (handle->data != NULL)
+ return EINVAL;
+ if (handle->vt.open == NULL)
+ return 0;
+ return handle->vt.open(context, dict_file, &handle->data);
+}
+
+krb5_error_code
+k5_pwqual_check(krb5_context context, pwqual_handle handle,
+ const char *password, kadm5_policy_ent_t policy,
+ krb5_principal princ)
+{
+ return handle->vt.check(context, handle->data, password, policy, princ);
+}
+
+void
+k5_pwqual_close(krb5_context context, pwqual_handle handle)
+{
+ if (handle->vt.close)
+ handle->vt.close(context, handle->data);
+ handle->data = NULL;
+}
Copied: branches/plugins2/src/lib/kadm5/srv/pwqual_dict.c (from rev 24200, trunk/src/lib/kadm5/srv/server_dict.c)
===================================================================
--- branches/plugins2/src/lib/kadm5/srv/pwqual_dict.c (rev 0)
+++ branches/plugins2/src/lib/kadm5/srv/pwqual_dict.c 2010-07-22 03:13:38 UTC (rev 24203)
@@ -0,0 +1,242 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include "k5-platform.h"
+#include <krb5/pwqual_plugin.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <kadm5/admin.h>
+#include "adm_proto.h"
+#include <syslog.h>
+#include "server_internal.h"
+
+typedef struct dict_moddata_st {
+ char **word_list; /* list of word pointers */
+ char *word_block; /* actual word data */
+ unsigned int word_count; /* number of words */
+} *dict_moddata;
+
+
+/*
+ * Function: word_compare
+ *
+ * Purpose: compare two words in the dictionary.
+ *
+ * Arguments:
+ * w1 (input) pointer to first word
+ * w2 (input) pointer to second word
+ * <return value> result of strcmp
+ *
+ * Requires:
+ * w1 and w2 to point to valid memory
+ *
+ */
+
+static int
+word_compare(const void *s1, const void *s2)
+{
+ return (strcasecmp(*(const char **)s1, *(const char **)s2));
+}
+
+/*
+ * Function: init-dict
+ *
+ * Purpose: Initialize in memory word dictionary
+ *
+ * Arguments:
+ * none
+ * <return value> KADM5_OK on success errno on failure;
+ * (but success on ENOENT)
+ *
+ * Requires:
+ * If WORDFILE exists, it must contain a list of words,
+ * one word per-line.
+ *
+ * Effects:
+ * If WORDFILE exists, it is read into memory sorted for future
+ * use. If it does not exist, it syslogs an error message and returns
+ * success.
+ *
+ * Modifies:
+ * word_list to point to a chunck of allocated memory containing
+ * pointers to words
+ * word_block to contain the dictionary.
+ *
+ */
+
+static int
+init_dict(dict_moddata dict, const char *dict_file)
+{
+ int fd;
+ size_t len, i;
+ char *p, *t;
+ struct stat sb;
+
+ if (dict_file == NULL) {
+ krb5_klog_syslog(LOG_INFO, "No dictionary file specified, continuing "
+ "without one.");
+ return KADM5_OK;
+ }
+ if ((fd = open(dict_file, O_RDONLY)) == -1) {
+ if (errno == ENOENT) {
+ krb5_klog_syslog(LOG_ERR,
+ "WARNING! Cannot find dictionary file %s, "
+ "continuing without one.", dict_file);
+ return KADM5_OK;
+ } else
+ return errno;
+ }
+ set_cloexec_fd(fd);
+ if (fstat(fd, &sb) == -1) {
+ close(fd);
+ return errno;
+ }
+ if ((dict->word_block = malloc(sb.st_size + 1)) == NULL)
+ return ENOMEM;
+ if (read(fd, dict->word_block, sb.st_size) != sb.st_size)
+ return errno;
+ (void) close(fd);
+ dict->word_block[sb.st_size] = '\0';
+
+ p = dict->word_block;
+ len = sb.st_size;
+ while(len > 0 && (t = memchr(p, '\n', len)) != NULL) {
+ *t = '\0';
+ len -= t - p + 1;
+ p = t + 1;
+ dict->word_count++;
+ }
+ if ((dict->word_list = malloc(dict->word_count * sizeof(char *))) == NULL)
+ return ENOMEM;
+ p = dict->word_block;
+ for (i = 0; i < dict->word_count; i++) {
+ dict->word_list[i] = p;
+ p += strlen(p) + 1;
+ }
+ qsort(dict->word_list, dict->word_count, sizeof(char *), word_compare);
+ return KADM5_OK;
+}
+
+/*
+ * Function: destroy_dict
+ *
+ * Purpose: destroy in-core copy of dictionary.
+ *
+ * Arguments:
+ * none
+ * <return value> none
+ * Requires:
+ * nothing
+ * Effects:
+ * frees up memory occupied by word_list and word_block
+ * sets count back to 0, and resets the pointers to NULL
+ *
+ * Modifies:
+ * word_list, word_block, and word_count.
+ *
+ */
+
+static void
+destroy_dict(dict_moddata dict)
+{
+ if (dict == NULL)
+ return;
+ free(dict->word_list);
+ free(dict->word_block);
+ free(dict);
+ return;
+}
+
+/* Implement the password quality open method by reading in dict_file. */
+static krb5_error_code
+dict_open(krb5_context context, const char *dict_file,
+ krb5_pwqual_moddata *data)
+{
+ krb5_error_code ret;
+ dict_moddata dict;
+
+ *data = NULL;
+
+ /* Allocate and initialize a dictionary structure. */
+ dict = malloc(sizeof(*dict));
+ if (dict == NULL)
+ return ENOMEM;
+ dict->word_list = NULL;
+ dict->word_block = NULL;
+ dict->word_count = 0;
+
+ /* Fill in the dictionary structure with data from dict_file. */
+ ret = init_dict(dict, dict_file);
+ if (ret != 0) {
+ destroy_dict(dict);
+ return ret;
+ }
+
+ *data = (krb5_pwqual_moddata)dict;
+ return 0;
+}
+
+/* Implement the password quality check method by checking the password
+ * against the dictionary, as well as against principal components. */
+static krb5_error_code
+dict_check(krb5_context context, krb5_pwqual_moddata data,
+ const char *password, kadm5_policy_ent_t policy,
+ krb5_principal princ)
+{
+ dict_moddata dict = (dict_moddata)data;
+ int i, n;
+ char *cp;
+
+ /* Don't check the dictionary for principals with no password policy. */
+ if (policy == NULL)
+ return 0;
+
+ /* Check against words in the dictionary if we successfully loaded one. */
+ if (dict->word_list != NULL &&
+ bsearch(&password, dict->word_list, dict->word_count, sizeof(char *),
+ word_compare) != NULL)
+ return KADM5_PASS_Q_DICT;
+
+ /* Check against components of the principal. */
+ n = krb5_princ_size(handle->context, princ);
+ cp = krb5_princ_realm(handle->context, princ)->data;
+ if (strcasecmp(cp, password) == 0)
+ return KADM5_PASS_Q_DICT;
+ for (i = 0; i < n; i++) {
+ cp = krb5_princ_component(handle->context, princ, i)->data;
+ if (strcasecmp(cp, password) == 0)
+ return KADM5_PASS_Q_DICT;
+ }
+ return 0;
+}
+
+/* Implement the password quality close method. */
+static void
+dict_close(krb5_context context, krb5_pwqual_moddata data)
+{
+ destroy_dict((dict_moddata)data);
+}
+
+krb5_error_code
+pwqual_dict_init(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_pwqual_vtable vt;
+
+ if (maj_ver != 1)
+ return EINVAL; /* XXX create error code */
+ vt = (krb5_pwqual_vtable)vtable;
+ vt->open = dict_open;
+ vt->check = dict_check;
+ vt->close = dict_close;
+ return 0;
+}
Added: branches/plugins2/src/lib/kadm5/srv/pwqual_policy.c
===================================================================
--- branches/plugins2/src/lib/kadm5/srv/pwqual_policy.c (rev 0)
+++ branches/plugins2/src/lib/kadm5/srv/pwqual_policy.c 2010-07-22 03:13:38 UTC (rev 24203)
@@ -0,0 +1,81 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * lib/kadm5/srv/pwqual_policy.c
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * Password quality module to enforce password policy
+ */
+
+#include "k5-platform.h"
+#include <krb5/pwqual_plugin.h>
+#include <kadm5/admin.h>
+#include <ctype.h>
+#include "server_internal.h"
+
+/* Implement the password quality check module. */
+static krb5_error_code
+policy_check(krb5_context context, krb5_pwqual_moddata data,
+ const char *password, kadm5_policy_ent_t policy,
+ krb5_principal princ)
+{
+ int nupper = 0, nlower = 0, ndigit = 0, npunct = 0, nspec = 0;
+ const char *s;
+ unsigned char c;
+
+ if (policy == NULL)
+ return (*password == '\0') ? KADM5_PASS_Q_TOOSHORT : 0;
+
+ if(strlen(password) < (size_t)policy->pw_min_length)
+ return KADM5_PASS_Q_TOOSHORT;
+ s = password;
+ while ((c = (unsigned char)*s++)) {
+ if (islower(c))
+ nlower = 1;
+ else if (isupper(c))
+ nupper = 1;
+ else if (isdigit(c))
+ ndigit = 1;
+ else if (ispunct(c))
+ npunct = 1;
+ else
+ nspec = 1;
+ }
+ if ((nupper + nlower + ndigit + npunct + nspec) < policy->pw_min_classes)
+ return KADM5_PASS_Q_CLASS;
+ return 0;
+}
+
+krb5_error_code
+pwqual_policy_init(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_pwqual_vtable vt;
+
+ if (maj_ver != 1)
+ return EINVAL; /* XXX create error code */
+ vt = (krb5_pwqual_vtable)vtable;
+ vt->check = policy_check;
+ return 0;
+}
Modified: branches/plugins2/src/lib/kadm5/srv/server_init.c
===================================================================
--- branches/plugins2/src/lib/kadm5/srv/server_init.c 2010-07-22 03:03:15 UTC (rev 24202)
+++ branches/plugins2/src/lib/kadm5/srv/server_init.c 2010-07-22 03:13:38 UTC (rev 24203)
@@ -317,7 +317,7 @@
return ret;
}
- ret = init_dict(&handle->params);
+ ret = init_pwqual(handle);
if (ret) {
krb5_db_fini(handle->context);
krb5_free_principal(handle->context, handle->current_caller);
@@ -337,7 +337,7 @@
CHECK_HANDLE(server_handle);
- destroy_dict();
+ destroy_pwqual(handle);
adb_policy_close(handle);
krb5_db_fini(handle->context);
Modified: branches/plugins2/src/lib/kadm5/srv/server_misc.c
===================================================================
--- branches/plugins2/src/lib/kadm5/srv/server_misc.c 2010-07-22 03:03:15 UTC (rev 24202)
+++ branches/plugins2/src/lib/kadm5/srv/server_misc.c 2010-07-22 03:13:38 UTC (rev 24203)
@@ -13,10 +13,6 @@
#include <kdb.h>
#include <ctype.h>
#include <pwd.h>
-
-/* for strcasecmp */
-#include <string.h>
-
#include "server_internal.h"
kadm5_ret_t
@@ -37,147 +33,68 @@
return KADM5_OK;
}
-#ifdef HESIOD
-/* stolen from v4sever/kadm_funcs.c */
-static char *
-reverse(str)
- char *str;
+kadm5_ret_t
+init_pwqual(kadm5_server_handle_t handle)
{
- static char newstr[80];
- char *p, *q;
- int i;
+ krb5_error_code ret;
+ pwqual_handle *list, *h;
+ const char *dict_file = NULL;
- i = strlen(str);
- if (i >= sizeof(newstr))
- i = sizeof(newstr)-1;
- p = str+i-1;
- q = newstr;
- q[i]='\0';
- for(; i > 0; i--)
- *q++ = *p--;
+ ret = k5_plugin_register(handle->context, PLUGIN_INTERFACE_PWQUAL,
+ "dict", pwqual_dict_init);
+ if (ret != 0)
+ return ret;
- return(newstr);
-}
-#endif /* HESIOD */
+ ret = k5_plugin_register(handle->context, PLUGIN_INTERFACE_PWQUAL,
+ "policy", pwqual_policy_init);
+ if (ret != 0)
+ return ret;
-#if 0
-static int
-lower(str)
- char *str;
-{
- register char *cp;
- int effect=0;
+ ret = k5_pwqual_load(handle->context, &list);
+ if (ret != 0)
+ return ret;
- for (cp = str; *cp; cp++) {
- if (isupper(*cp)) {
- *cp = tolower(*cp);
- effect++;
+ if (handle->params.mask & KADM5_CONFIG_DICT_FILE)
+ dict_file = handle->params.dict_file;
+
+ for (h = list; *h != NULL; h++) {
+ ret = k5_pwqual_open(handle->context, *h, dict_file);
+ if (ret != 0) {
+ /* Close any previously opened modules and error out. */
+ for (; h > list; h--)
+ k5_pwqual_close(handle->context, *(h - 1));
+ k5_pwqual_free_handles(handle->context, list);
+ return ret;
}
}
- return(effect);
+
+ handle->qual_handles = list;
+ return 0;
}
-#endif
-#ifdef HESIOD
-static int
-str_check_gecos(gecos, pwstr)
- char *gecos;
- char *pwstr;
+/* Check a password against all available password quality plugin modules. */
+kadm5_ret_t
+passwd_check(kadm5_server_handle_t handle, const char *password,
+ kadm5_policy_ent_t policy, krb5_principal princ)
{
- char *cp, *ncp, *tcp;
+ krb5_error_code ret;
+ pwqual_handle *h;
- for (cp = gecos; *cp; ) {
- /* Skip past punctuation */
- for (; *cp; cp++)
- if (isalnum(*cp))
- break;
- /* Skip to the end of the word */
- for (ncp = cp; *ncp; ncp++)
- if (!isalnum(*ncp) && *ncp != '\'')
- break;
- /* Delimit end of word */
- if (*ncp)
- *ncp++ = '\0';
- /* Check word to see if it's the password */
- if (*cp) {
- if (!strcasecmp(pwstr, cp))
- return 1;
- tcp = reverse(cp);
- if (!strcasecmp(pwstr, tcp))
- return 1;
- cp = ncp;
- } else
- break;
+ for (h = handle->qual_handles; *h != NULL; h++) {
+ ret = k5_pwqual_check(handle->context, *h, password, policy, princ);
+ if (ret != 0)
+ return ret;
}
return 0;
}
-#endif /* HESIOD */
-/* some of this is stolen from gatekeeper ... */
-kadm5_ret_t
-passwd_check(kadm5_server_handle_t handle,
- char *password, int use_policy, kadm5_policy_ent_t pol,
- krb5_principal principal)
+void
+destroy_pwqual(kadm5_server_handle_t handle)
{
- int nupper = 0,
- nlower = 0,
- ndigit = 0,
- npunct = 0,
- nspec = 0;
- char c, *s, *cp;
-#ifdef HESIOD
- extern struct passwd *hes_getpwnam();
- struct passwd *ent;
-#endif
+ pwqual_handle *h;
- if(use_policy) {
- if(strlen(password) < pol->pw_min_length)
- return KADM5_PASS_Q_TOOSHORT;
- s = password;
- while ((c = *s++)) {
- if (islower((unsigned char) c)) {
- nlower = 1;
- continue;
- }
- else if (isupper((unsigned char) c)) {
- nupper = 1;
- continue;
- } else if (isdigit((unsigned char) c)) {
- ndigit = 1;
- continue;
- } else if (ispunct((unsigned char) c)) {
- npunct = 1;
- continue;
- } else {
- nspec = 1;
- continue;
- }
- }
- if ((nupper + nlower + ndigit + npunct + nspec) < pol->pw_min_classes)
- return KADM5_PASS_Q_CLASS;
- if((find_word(password) == KADM5_OK))
- return KADM5_PASS_Q_DICT;
- else {
- int i, n = krb5_princ_size(handle->context, principal);
- cp = krb5_princ_realm(handle->context, principal)->data;
- if (strcasecmp(cp, password) == 0)
- return KADM5_PASS_Q_DICT;
- for (i = 0; i < n ; i++) {
- cp = krb5_princ_component(handle->context, principal, i)->data;
- if (strcasecmp(cp, password) == 0)
- return KADM5_PASS_Q_DICT;
-#ifdef HESIOD
- ent = hes_getpwnam(cp);
- if (ent && ent->pw_gecos)
- if (str_check_gecos(ent->pw_gecos, password))
- return KADM5_PASS_Q_DICT; /* XXX new error code? */
-#endif
- }
- return KADM5_OK;
- }
- } else {
- if (strlen(password) < 1)
- return KADM5_PASS_Q_TOOSHORT;
- }
- return KADM5_OK;
+ for (h = handle->qual_handles; *h != NULL; h++)
+ k5_pwqual_close(handle->context, *h);
+ k5_pwqual_free_handles(handle->context, handle->qual_handles);
+ handle->qual_handles = NULL;
}
Modified: branches/plugins2/src/lib/kadm5/srv/svr_principal.c
===================================================================
--- branches/plugins2/src/lib/kadm5/srv/svr_principal.c 2010-07-22 03:03:15 UTC (rev 24202)
+++ branches/plugins2/src/lib/kadm5/srv/svr_principal.c 2010-07-22 03:13:38 UTC (rev 24203)
@@ -292,7 +292,7 @@
have_polent = TRUE;
}
if (password) {
- ret = passwd_check(handle, password, have_polent, &polent,
+ ret = passwd_check(handle, password, have_polent ? &polent : NULL,
entry->principal);
if (ret)
goto cleanup;
@@ -1341,8 +1341,8 @@
have_pol = 1;
}
- if ((ret = passwd_check(handle, password, adb.aux_attributes &
- KADM5_POLICY, &pol, principal)))
+ if ((ret = passwd_check(handle, password, have_pol ? &pol : NULL,
+ principal)))
goto done;
ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,
Modified: branches/plugins2/src/lib/krb5/krb/Makefile.in
===================================================================
--- branches/plugins2/src/lib/krb5/krb/Makefile.in 2010-07-22 03:03:15 UTC (rev 24202)
+++ branches/plugins2/src/lib/krb5/krb/Makefile.in 2010-07-22 03:13:38 UTC (rev 24203)
@@ -74,6 +74,7 @@
pac.o \
pac_sign.o \
parse.o \
+ plugin.o \
pr_to_salt.o \
preauth2.o \
gic_opt_set_pa.o \
@@ -173,6 +174,7 @@
$(OUTPRE)pac.$(OBJEXT) \
$(OUTPRE)pac_sign.$(OBJEXT) \
$(OUTPRE)parse.$(OBJEXT) \
+ $(OUTPRE)plugin.$(OBJEXT) \
$(OUTPRE)pr_to_salt.$(OBJEXT) \
$(OUTPRE)preauth2.$(OBJEXT) \
$(OUTPRE)gic_opt_set_pa.$(OBJEXT) \
@@ -273,6 +275,7 @@
$(srcdir)/pac.c \
$(srcdir)/pac_sign.c \
$(srcdir)/parse.c \
+ $(srcdir)/plugin.c \
$(srcdir)/pr_to_salt.c \
$(srcdir)/preauth2.c \
$(srcdir)/gic_opt_set_pa.c \
Modified: branches/plugins2/src/lib/krb5/krb/init_ctx.c
===================================================================
--- branches/plugins2/src/lib/krb5/krb/init_ctx.c 2010-07-22 03:03:15 UTC (rev 24202)
+++ branches/plugins2/src/lib/krb5/krb/init_ctx.c 2010-07-22 03:13:38 UTC (rev 24203)
@@ -273,6 +273,8 @@
ctx->trace_callback(ctx, NULL, ctx->trace_callback_data);
#endif
+ k5_plugin_free_context(ctx);
+
ctx->magic = 0;
free(ctx);
}
Added: branches/plugins2/src/lib/krb5/krb/plugin.c
===================================================================
--- branches/plugins2/src/lib/krb5/krb/plugin.c (rev 0)
+++ branches/plugins2/src/lib/krb5/krb/plugin.c 2010-07-22 03:13:38 UTC (rev 24203)
@@ -0,0 +1,382 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * lib/krb5/krb/plugin.c
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * Plugin framework functions
+ */
+
+#include "k5-int.h"
+
+const char *interface_names[PLUGIN_NUM_INTERFACES] = {
+ "pwqual"
+};
+
+/* Return the context's interface structure for id, or NULL if invalid. */
+static inline struct plugin_interface *
+get_interface(krb5_context context, int id)
+{
+ if (context == NULL || id < 0 || id >= PLUGIN_NUM_INTERFACES)
+ return NULL;
+ return &context->plugins[id];
+}
+
+/* Release the memory associated with the linked list entry map. */
+static void
+free_plugin_mapping(struct plugin_mapping *map)
+{
+ if (map == NULL)
+ return;
+ free(map->modname);
+ if (map->dyn_handle != NULL)
+ krb5int_close_plugin(map->dyn_handle);
+ free(map);
+}
+
+/*
+ * Register a mapping from modname to module. On success, dyn_handle is
+ * remembered in the mapping and will be released when the mapping is
+ * overwritten or the context is destroyed.
+ */
+static krb5_error_code
+register_module(krb5_context context, struct plugin_interface *interface,
+ const char *modname, krb5_plugin_init_fn module,
+ struct plugin_file_handle *dyn_handle)
+{
+ struct plugin_mapping *map, **pmap;
+
+ /* If a mapping already exists for modname, remove it. */
+ for (pmap = &interface->modules; *pmap != NULL; pmap = &(*pmap)->next) {
+ map = *pmap;
+ if (strcmp(map->modname, modname) == 0) {
+ *pmap = map->next;
+ free_plugin_mapping(map);
+ break;
+ }
+ }
+
+ /* Create a new mapping structure. */
+ map = malloc(sizeof(*map));
+ if (map == NULL)
+ return ENOMEM;
+ map->modname = strdup(modname);
+ if (map->modname == NULL) {
+ free(map);
+ return ENOMEM;
+ }
+ map->module = module;
+ map->dyn_handle = dyn_handle;
+
+ /* Chain it into the list. */
+ map->next = interface->modules;
+ interface->modules = map;
+ return 0;
+}
+
+/* Parse a profile module string of the form "modname:modpath" into its
+ * component parts. */
+static krb5_error_code
+parse_modstr(krb5_context context, const char *modstr,
+ char **modname, char **modpath)
+{
+ const char *sep;
+ char *name = NULL, *path = NULL;
+
+ *modname = NULL;
+ *modpath = NULL;
+
+ sep = strchr(modstr, ':');
+ if (sep == NULL) {
+ krb5_set_error_message(context, EINVAL, "Invalid module string %s",
+ modstr);
+ return EINVAL; /* XXX create specific error code */
+ }
+
+ /* Copy the module name. */
+ name = malloc(sep - modstr + 1);
+ if (name == NULL)
+ return ENOMEM;
+ memcpy(name, modstr, sep - modstr);
+ name[sep - modstr] = '\0';
+
+ /* Copy the module path. */
+ path = strdup(sep + 1);
+ if (path == NULL) {
+ free(name);
+ return ENOMEM;
+ }
+
+ *modname = name;
+ *modpath = path;
+ return 0;
+}
+
+/* Open a dynamic object at modpath, look up symname within it, and register
+ * the resulting init function as modname. */
+static krb5_error_code
+open_and_register(krb5_context context, struct plugin_interface *interface,
+ const char *modname, const char *modpath,
+ const char *symname)
+{
+ krb5_error_code ret;
+ struct plugin_file_handle *handle;
+ void (*init_fn)();
+
+ ret = krb5int_open_plugin(modpath, &handle, &context->err);
+ if (ret != 0)
+ return ret;
+
+ ret = krb5int_get_plugin_func(handle, symname, &init_fn, &context->err);
+ if (ret != 0) {
+ krb5int_close_plugin(handle);
+ return ret;
+ }
+
+ ret = register_module(context, interface, modname,
+ (krb5_plugin_init_fn)init_fn, handle);
+ if (ret != 0)
+ krb5int_close_plugin(handle);
+ return ret;
+}
+
+/* Register the plugins given by the profile strings in modules. */
+static krb5_error_code
+register_dyn_modules(krb5_context context, struct plugin_interface *interface,
+ const char *iname, char **modules)
+{
+ krb5_error_code ret;
+ char *modname = NULL, *modpath = NULL, *symname = NULL;
+
+ for (; *modules != NULL; modules++) {
+ ret = parse_modstr(context, *modules, &modname, &modpath);
+ if (ret != 0)
+ return ret;
+ if (asprintf(&symname, "%s_%s_init", iname, modname) < 0) {
+ free(modname);
+ free(modpath);
+ return ENOMEM;
+ }
+ /* XXX should errors here be fatal, or just ignore the module? */
+ ret = open_and_register(context, interface, modname, modpath, symname);
+ free(modname);
+ free(modpath);
+ free(symname);
+ if (ret != 0)
+ return ret;
+ }
+ return 0;
+}
+
+/* Return true if value is found in list. */
+static krb5_boolean
+find_in_list(char **list, const char *value)
+{
+ for (; *list != NULL; list++) {
+ if (strcmp(*list, value) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Remove any registered modules whose names are not present in enable. */
+static void
+filter_enable(krb5_context context, struct plugin_interface *interface,
+ char **enable)
+{
+ struct plugin_mapping *map, **pmap;
+
+ pmap = &interface->modules;
+ while (*pmap != NULL) {
+ map = *pmap;
+ if (!find_in_list(enable, map->modname)) {
+ *pmap = map->next;
+ free_plugin_mapping(map);
+ } else
+ pmap = &map->next;
+ }
+}
+
+/* Remove any registered modules whose names are present in disable. */
+static void
+filter_disable(krb5_context context, struct plugin_interface *interface,
+ char **disable)
+{
+ struct plugin_mapping *map, **pmap;
+
+ pmap = &interface->modules;
+ while (*pmap != NULL) {
+ map = *pmap;
+ if (find_in_list(disable, map->modname)) {
+ *pmap = map->next;
+ free_plugin_mapping(map);
+ } else
+ pmap = &map->next;
+ }
+}
+
+/* Ensure that a plugin interface is configured. id is assumed to be valid. */
+static krb5_error_code
+configure_interface(krb5_context context, int id)
+{
+ krb5_error_code ret;
+ struct plugin_interface *interface = &context->plugins[id];
+ const char *iname = interface_names[id];
+ char **modules = NULL, **enable = NULL, **disable = NULL;
+ static const char *path[4];
+
+ if (interface->configured)
+ return 0;
+
+ /* Read the configuration variables for this interface. */
+ path[0] = KRB5_CONF_PLUGINS;
+ path[1] = iname;
+ path[2] = KRB5_CONF_MODULE;
+ path[3] = NULL;
+ ret = profile_get_values(context->profile, path, &modules);
+ if (ret != 0 && ret != PROF_NO_RELATION)
+ goto cleanup;
+ path[2] = KRB5_CONF_ENABLE_ONLY;
+ ret = profile_get_values(context->profile, path, &enable);
+ if (ret != 0 && ret != PROF_NO_RELATION)
+ goto cleanup;
+ path[2] = KRB5_CONF_DISABLE;
+ ret = profile_get_values(context->profile, path, &disable);
+ if (ret != 0 && ret != PROF_NO_RELATION)
+ goto cleanup;
+
+ if (modules != NULL) {
+ ret = register_dyn_modules(context, interface, iname, modules);
+ if (ret != 0)
+ return ret;
+ }
+ if (enable != NULL)
+ filter_enable(context, interface, enable);
+ if (disable != NULL)
+ filter_disable(context, interface, disable);
+
+ ret = 0;
+cleanup:
+ profile_free_list(modules);
+ profile_free_list(enable);
+ profile_free_list(disable);
+ return ret;
+}
+
+krb5_error_code
+k5_plugin_load(krb5_context context, int interface_id, const char *modname,
+ krb5_plugin_init_fn *module)
+{
+ krb5_error_code ret;
+ struct plugin_interface *interface = get_interface(context, interface_id);
+ struct plugin_mapping *map;
+
+ if (interface == NULL)
+ return EINVAL;
+ ret = configure_interface(context, interface_id);
+ if (ret != 0)
+ return ret;
+ for (map = interface->modules; map != NULL; map = map->next) {
+ if (strcmp(map->modname, modname) == 0) {
+ *module = map->module;
+ return 0;
+ }
+ }
+ return ENOENT; /* XXX Create error code? */
+}
+
+krb5_error_code
+k5_plugin_load_all(krb5_context context, int interface_id,
+ krb5_plugin_init_fn **modules)
+{
+ krb5_error_code ret;
+ struct plugin_interface *interface = get_interface(context, interface_id);
+ struct plugin_mapping *map;
+ krb5_plugin_init_fn *list;
+ size_t count;
+
+ if (interface == NULL)
+ return EINVAL;
+ ret = configure_interface(context, interface_id);
+ if (ret != 0)
+ return ret;
+
+ /* Count the modules and allocate a list to hold them. */
+ count = 0;
+ for (map = interface->modules; map != NULL; map = map->next)
+ count++;
+ list = malloc((count + 1) * sizeof(*list));
+ if (list == NULL)
+ return ENOMEM;
+
+ /* Place each module's init function into list. */
+ count = 0;
+ for (map = interface->modules; map != NULL; map = map->next)
+ list[count++] = map->module;
+ list[count] = NULL;
+
+ *modules = list;
+ return 0;
+}
+
+void
+k5_plugin_free_modules(krb5_context context, krb5_plugin_init_fn *modules)
+{
+ free(modules);
+}
+
+krb5_error_code
+k5_plugin_register(krb5_context context, int interface_id, const char *modname,
+ krb5_plugin_init_fn module)
+{
+ struct plugin_interface *interface = get_interface(context, interface_id);
+
+ if (interface == NULL)
+ return EINVAL;
+
+ /* Disallow registering plugins after load. We may need to reconsider
+ * this, but it simplifies the design. */
+ if (interface->configured)
+ return EINVAL;
+
+ return register_module(context, interface, modname, module, NULL);
+}
+
+void
+k5_plugin_free_context(krb5_context context)
+{
+ int i;
+ struct plugin_interface *interface;
+ struct plugin_mapping *map, *next;
+
+ for (i = 0; i < PLUGIN_NUM_INTERFACES; i++) {
+ interface = &context->plugins[i];
+ for (map = interface->modules; map != NULL; map = next) {
+ next = map->next;
+ free_plugin_mapping(map);
+ }
+ interface->modules = NULL;
+ interface->configured = FALSE;
+ }
+}
Modified: branches/plugins2/src/lib/krb5/libkrb5.exports
===================================================================
--- branches/plugins2/src/lib/krb5/libkrb5.exports 2010-07-22 03:03:15 UTC (rev 24202)
+++ branches/plugins2/src/lib/krb5/libkrb5.exports 2010-07-22 03:13:38 UTC (rev 24203)
@@ -104,6 +104,10 @@
initialize_krb5_error_table
initialize_kv5m_error_table
initialize_prof_error_table
+k5_plugin_free_modules
+k5_plugin_load
+k5_plugin_load_all
+k5_plugin_register
krb524_convert_creds_kdc
krb524_init_ets
krb5_425_conv_principal
More information about the cvs-krb5
mailing list