New plugin infrastructure
authorGreg Hudson <ghudson@mit.edu>
Fri, 27 Aug 2010 04:29:11 +0000 (04:29 +0000)
committerGreg Hudson <ghudson@mit.edu>
Fri, 27 Aug 2010 04:29:11 +0000 (04:29 +0000)
Merge domain-independent plugin framework code from branches/plugins2,
leaving out the password quality interface.

ticket: 6763

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24263 dc483132-0cff-0310-8789-dd5450dbe970

src/include/Makefile.in
src/include/k5-int.h
src/include/krb5/plugin.h [new file with mode: 0644]
src/lib/krb5/error_tables/k5e1_err.et
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

index 651bd3df8343ed9857bb1f021dcaaf53686349c3..930712d18e9cf0cab2441b85fe456bdd0dd42a2c 100644 (file)
@@ -137,6 +137,7 @@ 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) profile.h $(DESTDIR)$(KRB5_INCDIR)$(S)profile.h
        $(INSTALL_DATA) $(srcdir)/gssapi.h $(DESTDIR)$(KRB5_INCDIR)$(S)gssapi.h
 
index bb078c0705ca18238f5f03e8c95102177557a490..ec701c1dfe9cbbf54c67b7287d3aafe36a66d42c 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"
@@ -1426,6 +1431,118 @@ krb5_authdata_free_internal(krb5_context kcontext,
                             krb5_authdata_context context, const char *module,
                             void *ptr);
 
+/*** Plugin framework ***/
+
+/*
+ * This framework can be used to create pluggable interfaces.  Not all existing
+ * pluggable interface use this framework, but new ones should.  A new
+ * pluggable interface entails:
+ *
+ * - An interface ID definition in the list of #defines below.
+ *
+ * - A name in the interface_names array in lib/krb5/krb/plugins.c.
+ *
+ * - An installed public header file in include/krb5.  The public header should
+ *   include <krb5/plugin.h> and should declare a vtable structure for each
+ *   supported major version of the interface.
+ *
+ * - A consumer API implementation, located within the code unit which makes
+ *   use of the pluggable interface.  The consumer API should consist of:
+ *
+ *   . An interface-specific handle type which contains a vtable structure for
+ *     the module (or a union of several such structures, if there are multiple
+ *     supported major versions) and, optionally, resource data bound to the
+ *     handle.
+ *
+ *   . An interface-specific loader function which creates a handle or list of
+ *     handles.  A list of handles would be created if the interface is a
+ *     one-to-many interface where the consumer wants to consult all available
+ *     modules; a single handle would be created for an interface where the
+ *     consumer wants to consult a specific module.  The loader function should
+ *     use k5_plugin_load or k5_plugin_load_all to produce one or a list of
+ *     vtable initializer functions, and should use those functions to fill in
+ *     the vtable structure for the module (if necessary, trying each supported
+ *     major version starting from the most recent).  The loader function can
+ *     also bind resource data into the handle based on caller arguments, if
+ *     appropriate.
+ *
+ *   . For each plugin method, a wrapper function which accepts a krb5_context,
+ *     a plugin handle, and the method arguments.  Wrapper functions should
+ *     invoke the method function contained in the handle's vtable.
+ *
+ * - Possibly, built-in implementations of the interface, also located within
+ *   the code unit which makes use of the interface.  Built-in implementations
+ *   must be registered with k5_plugin_register before the first call to
+ *   k5_plugin_load or k5_plugin_load_all.
+ *
+ * A pluggable interface should have one or more currently supported major
+ * versions, starting at 1.  Each major version should have a current minor
+ * version, also starting at 1.  If new methods are added to a vtable, the
+ * minor version should be incremented and the vtable stucture should document
+ * where each minor vtable version ends.  If method signatures for a vtable are
+ * changed, the major version should be incremented.
+ *
+ * Plugin module implementations (either built-in or dynamically loaded) should
+ * define a function named <interfacename>_<modulename>_initvt, matching the
+ * signature of krb5_plugin_initvt_fn as declared in include/krb5/plugin.h.
+ * The initvt function should check the given maj_ver argument against its own
+ * supported major versions, cast the vtable pointer to the appropriate
+ * interface-specific vtable type, and fill in the vtable methods, stopping as
+ * appropriate for the given min_ver.  Memory for the vtable structure is
+ * allocated by the caller, not by the module.
+ *
+ * Dynamic plugin modules are registered with the framework through the
+ * [plugins] section of the profile, as described in the admin documentation
+ * and krb5.conf man page.
+ */
+
+/*
+ * A linked list entry mapping a module name to a module initvt 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_initvt_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_NUM_INTERFACES   0
+
+/* 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_initvt_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_initvt_fn **modules);
+
+/* Release a module list allocated by k5_plugin_load_all. */
+void
+k5_plugin_free_modules(krb5_context context, krb5_plugin_initvt_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_initvt_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;
@@ -1480,6 +1597,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..f43406a
--- /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 initvt functions will cast
+ * it to the appropriate interface-specific vtable type. */
+typedef struct krb5_plugin_vtable_st *krb5_plugin_vtable;
+
+/*
+ * krb5_plugin_initvt_fn is the type of all module initvt functions.  Based on
+ * the maj_ver argument, the initvt 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_initvt_fn)(krb5_context context, int maj_ver, int min_ver,
+                         krb5_plugin_vtable vtable);
+
+#endif /* KRB5_PLUGIN_H */
index 6d5621001af7471d7fb9bc5d97027e4ede176e48..7fe4cc6fc4479156cf2126043844a074f6b98981 100644 (file)
@@ -30,4 +30,8 @@
 #
 error_table k5e1
 
+error_code KRB5_PLUGIN_VER_NOTSUPP, "Plugin does not support interface version"
+error_code KRB5_PLUGIN_BAD_MODULE_SPEC, "Invalid module specifier"
+error_code KRB5_PLUGIN_NAME_NOTFOUND, "Plugin module name not found"
+
 end
index 895d44478aa9e0032e5a8eb0914971ff51117def..1b4d879677e494cd1dc98af4dd12c12f08c8e6db 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..aa7452e
--- /dev/null
@@ -0,0 +1,368 @@
+/* -*- 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] = {
+};
+
+/* 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_initvt_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, KRB5_PLUGIN_BAD_MODULE_SPEC,
+                               "Invalid module specifier %s", modstr);
+        return KRB5_PLUGIN_BAD_MODULE_SPEC;
+    }
+
+    /* 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;
+}
+
+/* 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;
+}
+
+/* Return true if module is not filtered out by enable or disable lists. */
+static krb5_boolean
+module_enabled(const char *modname, char **enable, char **disable)
+{
+    return ((enable == NULL || find_in_list(enable, modname)) &&
+            (disable == NULL || !find_in_list(disable, modname)));
+}
+
+/* Remove any registered modules whose names are filtered out. */
+static void
+filter_builtins(krb5_context context, struct plugin_interface *interface,
+                char **enable, char **disable)
+{
+    struct plugin_mapping *map, **pmap;
+
+    pmap = &interface->modules;
+    while (*pmap != NULL) {
+        map = *pmap;
+        if (!module_enabled(map->modname, enable, disable)) {
+            *pmap = map->next;
+            free_plugin_mapping(map);
+        } else
+            pmap = &map->next;
+    }
+}
+
+/* Register the plugin module given by the profile string mod. */
+static krb5_error_code
+register_dyn_module(krb5_context context, struct plugin_interface *interface,
+                    const char *iname, const char *modstr, char **enable,
+                    char **disable)
+{
+    krb5_error_code ret;
+    char *modname = NULL, *modpath = NULL, *symname = NULL;
+    struct plugin_file_handle *handle = NULL;
+    void (*initvt_fn)();
+
+    /* Parse out the module name and path, and make sure it is enabled. */
+    ret = parse_modstr(context, modstr, &modname, &modpath);
+    if (ret != 0)
+        goto cleanup;
+    if (!module_enabled(modname, enable, disable))
+        goto cleanup;
+
+    /* Construct the initvt symbol name for this interface and module. */
+    if (asprintf(&symname, "%s_%s_initvt", iname, modname) < 0) {
+        symname = NULL;
+        ret = ENOMEM;
+        goto cleanup;
+    }
+
+    /* Open the plugin and resolve the initvt symbol. */
+    ret = krb5int_open_plugin(modpath, &handle, &context->err);
+    if (ret != 0)
+        goto cleanup;
+    ret = krb5int_get_plugin_func(handle, symname, &initvt_fn, &context->err);
+    if (ret != 0)
+        goto cleanup;
+
+    /* Create a mapping for the module. */
+    ret = register_module(context, interface, modname,
+                          (krb5_plugin_initvt_fn)initvt_fn, handle);
+    if (ret != 0)
+        goto cleanup;
+    handle = NULL;              /* Now owned by the module mapping. */
+
+cleanup:
+    free(modname);
+    free(modpath);
+    free(symname);
+    if (handle != NULL)
+        krb5int_close_plugin(handle);
+    return ret;
+}
+
+/* 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, **mod;
+    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;
+
+    /* Remove built-in modules which are filtered out by configuration. */
+    filter_builtins(context, interface, enable, disable);
+
+    /* Create mappings for dynamic modules which aren't filtered out. */
+    for (mod = modules; mod && *mod; mod++) {
+        ret = register_dyn_module(context, interface, iname, *mod,
+                                  enable, disable);
+        if (ret != 0)
+            return ret;
+    }
+
+    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_initvt_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;
+        }
+    }
+    krb5_set_error_message(context, KRB5_PLUGIN_NAME_NOTFOUND,
+                           "Could not find %s plugin module named '%s'",
+                           interface_names[interface_id], modname);
+    return KRB5_PLUGIN_NAME_NOTFOUND;
+}
+
+krb5_error_code
+k5_plugin_load_all(krb5_context context, int interface_id,
+                   krb5_plugin_initvt_fn **modules)
+{
+    krb5_error_code ret;
+    struct plugin_interface *interface = get_interface(context, interface_id);
+    struct plugin_mapping *map;
+    krb5_plugin_initvt_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 initvt 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_initvt_fn *modules)
+{
+    free(modules);
+}
+
+krb5_error_code
+k5_plugin_register(krb5_context context, int interface_id, const char *modname,
+                   krb5_plugin_initvt_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 af661edcc586a511a9f8a6d7e975e6468c32dfb8..57e5c45de2769d09f389c3b1b3ab9017a4ff653a 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