]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Proof of concept code for a candidate plugin framework
authorGreg Hudson <ghudson@mit.edu>
Thu, 22 Jul 2010 03:13:38 +0000 (03:13 +0000)
committerGreg Hudson <ghudson@mit.edu>
Thu, 22 Jul 2010 03:13:38 +0000 (03:13 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/branches/plugins2@24203 dc483132-0cff-0310-8789-dd5450dbe970

21 files changed:
README.BRANCH [new file with mode: 0644]
pwqual_combo/Makefile [new file with mode: 0644]
pwqual_combo/combo.c [new file with mode: 0644]
src/include/Makefile.in
src/include/k5-int.h
src/include/krb5/plugin.h [new file with mode: 0644]
src/include/krb5/pwqual_plugin.h [new file with mode: 0644]
src/lib/kadm5/server_internal.h
src/lib/kadm5/srv/Makefile.in
src/lib/kadm5/srv/libkadm5srv_mit.exports
src/lib/kadm5/srv/pwqual.c [new file with mode: 0644]
src/lib/kadm5/srv/pwqual_dict.c [new file with mode: 0644]
src/lib/kadm5/srv/pwqual_policy.c [new file with mode: 0644]
src/lib/kadm5/srv/server_dict.c [deleted file]
src/lib/kadm5/srv/server_init.c
src/lib/kadm5/srv/server_misc.c
src/lib/kadm5/srv/svr_principal.c
src/lib/krb5/krb/Makefile.in
src/lib/krb5/krb/init_ctx.c
src/lib/krb5/krb/plugin.c [new file with mode: 0644]
src/lib/krb5/libkrb5.exports

diff --git a/README.BRANCH b/README.BRANCH
new file mode 100644 (file)
index 0000000..b5ee7f7
--- /dev/null
@@ -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.
diff --git a/pwqual_combo/Makefile b/pwqual_combo/Makefile
new file mode 100644 (file)
index 0000000..ee7905b
--- /dev/null
@@ -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
diff --git a/pwqual_combo/combo.c b/pwqual_combo/combo.c
new file mode 100644 (file)
index 0000000..5c756e4
--- /dev/null
@@ -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;
+}
index 81cd6e18da2e3a760cad4ec9e0c3078356f6b5df..1f3564a7c6cda38c04a39c441a564721f06358c3 100644 (file)
@@ -136,6 +136,8 @@ install-headers-unix install:: krb5/krb5.h profile.h
        $(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
 
index 19bf26b380b21660b12b76b420213167c29adbb5..cb7c5ae9e057cc60564b5298c6c579b33cb2b20b 100644 (file)
@@ -168,6 +168,7 @@ typedef INT64_TYPE krb5_int64;
  */
 #include <errno.h>
 #include "krb5.h"
+#include <krb5/plugin.h>
 #include "profile.h"
 
 #include "port-sockets.h"
@@ -205,12 +206,14 @@ typedef INT64_TYPE krb5_int64;
 #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 @@ typedef INT64_TYPE krb5_int64;
 #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_free_internal(krb5_context kcontext,
                             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 @@ struct _krb5_context {
 
     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 */
diff --git a/src/include/krb5/plugin.h b/src/include/krb5/plugin.h
new file mode 100644 (file)
index 0000000..9c6c0f4
--- /dev/null
@@ -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 */
diff --git a/src/include/krb5/pwqual_plugin.h b/src/include/krb5/pwqual_plugin.h
new file mode 100644 (file)
index 0000000..a5eeb8c
--- /dev/null
@@ -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 */
index cc589fad23dcd6dcba7d11f4154ce396e79ed3f2..52f71e9e5a08b1d926a61719cdedba4ce96a1f9d 100644 (file)
@@ -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 @@ typedef struct _kadm5_server_handle_t {
     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 @@ typedef struct _osa_princ_ent_t {
 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 @@ krb5_error_code     kdb_iter_entry(kadm5_server_handle_t handle,
                                    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 @@ bool_t          xdr_osa_princ_ent_rec(XDR *xdrs, osa_princ_ent_t objp);
 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__ */
index c7e0fac9b0a50c96e324ce35ffa23e434c097814..6000d73b6ac3d9351e9b217153d4e7349ecd4bbc 100644 (file)
@@ -27,36 +27,42 @@ SHLIB_DIRS=-L$(TOPLIBD)
 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
index 6da95bd7cebdc2e9b2b3e3e26317bea06aa7dce8..345957a139d02bb33ce5fef6a591a19ef50c7a73 100644 (file)
@@ -7,10 +7,7 @@ kadm5int_acl_impose_restrictions
 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
diff --git a/src/lib/kadm5/srv/pwqual.c b/src/lib/kadm5/srv/pwqual.c
new file mode 100644 (file)
index 0000000..4452376
--- /dev/null
@@ -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;
+}
diff --git a/src/lib/kadm5/srv/pwqual_dict.c b/src/lib/kadm5/srv/pwqual_dict.c
new file mode 100644 (file)
index 0000000..60bc5ff
--- /dev/null
@@ -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;
+}
diff --git a/src/lib/kadm5/srv/pwqual_policy.c b/src/lib/kadm5/srv/pwqual_policy.c
new file mode 100644 (file)
index 0000000..978744d
--- /dev/null
@@ -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;
+}
diff --git a/src/lib/kadm5/srv/server_dict.c b/src/lib/kadm5/srv/server_dict.c
deleted file mode 100644 (file)
index 81cc5f9..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-/* -*- 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    <sys/types.h>
-#include    <sys/file.h>
-#include    <fcntl.h>
-#include    <sys/stat.h>
-#include    <unistd.h>
-#include <errno.h>
-#include    <kadm5/admin.h>
-#include    <stdlib.h>
-#include    <stdio.h>
-#include    <string.h>
-#ifdef HAVE_MEMORY_H
-#include    <memory.h>
-#endif
-#include    "adm_proto.h"
-#include    <syslog.h>
-#include    "server_internal.h"
-#include    "k5-platform.h"
-
-static char         **word_list = NULL;     /* list of word pointers */
-static char         *word_block = NULL;     /* actual word data */
-static unsigned int word_count = 0;         /* number of words */
-
-
-/*
- * 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.
- *
- */
-
-int init_dict(kadm5_config_params *params)
-{
-    int             fd,
-        len,
-        i;
-    char            *p,
-        *t;
-    struct  stat    sb;
-
-    if(word_list != NULL && word_block != NULL)
-        return KADM5_OK;
-    if (! (params->mask & KADM5_CONFIG_DICT_FILE)) {
-        krb5_klog_syslog(LOG_INFO, "No dictionary file specified, continuing "
-                         "without one.");
-        return KADM5_OK;
-    }
-    if ((fd = open(params->dict_file, O_RDONLY)) == -1) {
-        if (errno == ENOENT) {
-            krb5_klog_syslog(LOG_ERR,
-                             "WARNING!  Cannot find dictionary file %s, "
-                             "continuing without one.", params->dict_file);
-            return KADM5_OK;
-        } else
-            return errno;
-    }
-    set_cloexec_fd(fd);
-    if (fstat(fd, &sb) == -1) {
-        close(fd);
-        return errno;
-    }
-    if ((word_block = (char *) malloc(sb.st_size + 1)) == NULL)
-        return ENOMEM;
-    if (read(fd, word_block, sb.st_size) != sb.st_size)
-        return errno;
-    (void) close(fd);
-    word_block[sb.st_size] = '\0';
-
-    p = word_block;
-    len = sb.st_size;
-    while(len > 0 && (t = memchr(p, '\n', len)) != NULL) {
-        *t = '\0';
-        len -= t - p + 1;
-        p = t + 1;
-        word_count++;
-    }
-    if ((word_list = (char **) malloc(word_count * sizeof(char *))) == NULL)
-        return ENOMEM;
-    p = word_block;
-    for (i = 0; i < word_count; i++) {
-        word_list[i] = p;
-        p += strlen(p) + 1;
-    }
-    qsort(word_list, word_count, sizeof(char *), word_compare);
-    return KADM5_OK;
-}
-
-/*
- * Function: find_word
- *
- * Purpose: See if the specified word exists in the in-core dictionary
- *
- * Arguments:
- *      word            (input) word to search for.
- *      <return value>  WORD_NOT_FOUND if not in dictionary,
- *                      KADM5_OK if if found word
- *                      errno if init needs to be called and returns an
- *                      error
- *
- * Requires:
- *      word to be a null terminated string.
- *      That word_list and word_block besetup
- *
- * Effects:
- *      finds word in dictionary.
- * Modifies:
- *      nothing.
- *
- */
-
-int
-find_word(const char *word)
-{
-    char    **value;
-
-    if(word_list == NULL || word_block == NULL)
-        return WORD_NOT_FOUND;
-    if ((value = (char **) bsearch(&word, word_list, word_count, sizeof(char *),
-                                   word_compare)) == NULL)
-        return WORD_NOT_FOUND;
-    else
-        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.
- *
- */
-
-void
-destroy_dict(void)
-{
-    if(word_list) {
-        free(word_list);
-        word_list = NULL;
-    }
-    if(word_block) {
-        free(word_block);
-        word_block = NULL;
-    }
-    if(word_count)
-        word_count = 0;
-    return;
-}
index 557ef0ad46839ea68aa575bc10b59e8a50cb85d8..9ebc13eea6e76e363683915a1cbe765dc6bb3c5e 100644 (file)
@@ -317,7 +317,7 @@ kadm5_ret_t kadm5_init(krb5_context context, char *client_name, char *pass,
         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 @@ kadm5_ret_t kadm5_destroy(void *server_handle)
 
     CHECK_HANDLE(server_handle);
 
-    destroy_dict();
+    destroy_pwqual(handle);
 
     adb_policy_close(handle);
     krb5_db_fini(handle->context);
index 1faeb86b16f7d6ba42f235ae66fb7617bbc537dc..38c060733f9451bd506fdd059a5176284d46e34b 100644 (file)
@@ -13,10 +13,6 @@ static char *rcsid = "$Header$";
 #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 @@ adb_policy_close(kadm5_server_handle_t handle)
     return KADM5_OK;
 }
 
-#ifdef HESIOD
-/* stolen from v4sever/kadm_funcs.c */
-static char *
-reverse(str)
-    char    *str;
-{
-    static char newstr[80];
-    char    *p, *q;
-    int     i;
-
-    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--;
-
-    return(newstr);
-}
-#endif /* HESIOD */
-
-#if 0
-static int
-lower(str)
-    char    *str;
+kadm5_ret_t
+init_pwqual(kadm5_server_handle_t handle)
 {
-    register char   *cp;
-    int     effect=0;
-
-    for (cp = str; *cp; cp++) {
-        if (isupper(*cp)) {
-            *cp = tolower(*cp);
-            effect++;
+    krb5_error_code ret;
+    pwqual_handle *list, *h;
+    const char *dict_file = NULL;
+
+    ret = k5_plugin_register(handle->context, PLUGIN_INTERFACE_PWQUAL,
+                             "dict", pwqual_dict_init);
+    if (ret != 0)
+        return ret;
+
+    ret = k5_plugin_register(handle->context, PLUGIN_INTERFACE_PWQUAL,
+                             "policy", pwqual_policy_init);
+    if (ret != 0)
+        return ret;
+
+    ret = k5_pwqual_load(handle->context, &list);
+    if (ret != 0)
+        return ret;
+
+    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;
-
-    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;
+    krb5_error_code ret;
+    pwqual_handle *h;
+
+    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;
 }
index 6b14d3ba6bd212e7f3dd7a7ab6cde93a4e861540..dc164064301e2ee3f8f0e83881993ca1ee48d72b 100644 (file)
@@ -292,7 +292,7 @@ kadm5_create_principal_3(void *server_handle,
         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 @@ kadm5_chpass_principal_3(void *server_handle,
         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,
index e52200d4d95aed6743404c9769ff4c05e714055d..0c013862d539fb890a20b0a1ef01842dbc0dc2de 100644 (file)
@@ -74,6 +74,7 @@ STLIBOBJS= \
        pac.o           \
        pac_sign.o      \
        parse.o         \
+       plugin.o        \
        pr_to_salt.o    \
        preauth2.o      \
        gic_opt_set_pa.o        \
@@ -173,6 +174,7 @@ OBJS=       $(OUTPRE)addr_comp.$(OBJEXT)    \
        $(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 @@ SRCS=       $(srcdir)/addr_comp.c   \
        $(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      \
index e7419f5d4a7c822512765a445186099ca6456e1f..c5975f19a6e5904fe0ed4e004757370335104ddd 100644 (file)
@@ -273,6 +273,8 @@ krb5_free_context(krb5_context ctx)
         ctx->trace_callback(ctx, NULL, ctx->trace_callback_data);
 #endif
 
+    k5_plugin_free_context(ctx);
+
     ctx->magic = 0;
     free(ctx);
 }
diff --git a/src/lib/krb5/krb/plugin.c b/src/lib/krb5/krb/plugin.c
new file mode 100644 (file)
index 0000000..aa9ac89
--- /dev/null
@@ -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;
+    }
+}
index 2bd597204b81c01e80f39f670507a5b827f41315..b375c1be06e19cf8a898201d0da6b6bf36470772 100644 (file)
@@ -104,6 +104,10 @@ initialize_kdb5_error_table
 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