summaryrefslogtreecommitdiff
path: root/source4/heimdal/lib/krb5
diff options
context:
space:
mode:
Diffstat (limited to 'source4/heimdal/lib/krb5')
-rw-r--r--source4/heimdal/lib/krb5/krb5-private.h54
-rw-r--r--source4/heimdal/lib/krb5/krb5-protos.h27
-rw-r--r--source4/heimdal/lib/krb5/krb5.h4
-rw-r--r--source4/heimdal/lib/krb5/krb5_locl.h6
-rw-r--r--source4/heimdal/lib/krb5/krbhst.c97
-rw-r--r--source4/heimdal/lib/krb5/locate_plugin.h64
-rwxr-xr-xsource4/heimdal/lib/krb5/mit_glue.c10
-rw-r--r--source4/heimdal/lib/krb5/plugin.c242
8 files changed, 491 insertions, 13 deletions
diff --git a/source4/heimdal/lib/krb5/krb5-private.h b/source4/heimdal/lib/krb5/krb5-private.h
index 0bf184a530..ba2f75ad22 100644
--- a/source4/heimdal/lib/krb5/krb5-private.h
+++ b/source4/heimdal/lib/krb5/krb5-private.h
@@ -299,6 +299,37 @@ _krb5_oid_to_enctype (
const heim_oid */*oid*/,
krb5_enctype */*etype*/);
+void
+_krb5_pac_free (
+ krb5_context /*context*/,
+ struct krb5_pac */*pac*/);
+
+krb5_error_code
+_krb5_pac_parse (
+ krb5_context /*context*/,
+ const void */*ptr*/,
+ size_t /*len*/,
+ struct krb5_pac **/*pac*/);
+
+krb5_error_code
+_krb5_pac_sign (
+ krb5_context /*context*/,
+ struct krb5_pac */*p*/,
+ time_t /*authtime*/,
+ krb5_principal /*principal*/,
+ krb5_keyblock */*server_key*/,
+ krb5_keyblock */*priv_key*/,
+ krb5_data */*data*/);
+
+krb5_error_code
+_krb5_pac_verify (
+ krb5_context /*context*/,
+ struct krb5_pac */*pac*/,
+ time_t /*authtime*/,
+ krb5_principal /*principal*/,
+ krb5_keyblock */*server*/,
+ krb5_keyblock */*privsvr*/);
+
krb5_error_code
_krb5_parse_moduli (
krb5_context /*context*/,
@@ -380,6 +411,29 @@ _krb5_pk_verify_sign (
krb5_data */*content*/,
struct krb5_pk_cert **/*signer*/);
+krb5_error_code
+_krb5_plugin_find (
+ krb5_context /*context*/,
+ enum plugin_type /*type*/,
+ const char */*name*/,
+ struct krb5_plugin **/*list*/);
+
+void
+_krb5_plugin_free (struct krb5_plugin */*list*/);
+
+struct krb5_plugin *
+_krb5_plugin_get_next (struct krb5_plugin */*p*/);
+
+void *
+_krb5_plugin_get_symbol (struct krb5_plugin */*p*/);
+
+krb5_error_code
+_krb5_plugin_register (
+ krb5_context /*context*/,
+ enum plugin_type /*type*/,
+ const char */*name*/,
+ void */*symbol*/);
+
krb5_error_code KRB5_LIB_FUNCTION
_krb5_principal2principalname (
PrincipalName */*p*/,
diff --git a/source4/heimdal/lib/krb5/krb5-protos.h b/source4/heimdal/lib/krb5/krb5-protos.h
index 104f10bdf2..8b61e8d7d2 100644
--- a/source4/heimdal/lib/krb5/krb5-protos.h
+++ b/source4/heimdal/lib/krb5/krb5-protos.h
@@ -499,6 +499,12 @@ krb5_boolean KRB5_LIB_FUNCTION
krb5_c_is_keyed_cksum (krb5_cksumtype /*ctype*/);
krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_keylength (
+ krb5_context /*context*/,
+ krb5_enctype /*enctype*/,
+ size_t */*len*/);
+
+krb5_error_code KRB5_LIB_FUNCTION
krb5_c_make_checksum (
krb5_context /*context*/,
krb5_cksumtype /*cksumtype*/,
@@ -2165,6 +2171,9 @@ krb5_get_krbhst (
const krb5_realm */*realm*/,
char ***/*hostlist*/);
+time_t KRB5_LIB_FUNCTION
+krb5_get_max_time_skew (krb5_context /*context*/);
+
krb5_error_code KRB5_LIB_FUNCTION
krb5_get_pw_salt (
krb5_context /*context*/,
@@ -2177,9 +2186,6 @@ krb5_get_server_rcache (
const krb5_data */*piece*/,
krb5_rcache */*id*/);
-time_t KRB5_LIB_FUNCTION
-krb5_get_time_wrap (krb5_context /*context*/);
-
krb5_boolean KRB5_LIB_FUNCTION
krb5_get_use_admin_kdc (krb5_context /*context*/);
@@ -3165,6 +3171,11 @@ krb5_set_ignore_addresses (
krb5_context /*context*/,
const krb5_addresses */*addresses*/);
+void KRB5_LIB_FUNCTION
+krb5_set_max_time_skew (
+ krb5_context /*context*/,
+ time_t /*t*/);
+
krb5_error_code KRB5_LIB_FUNCTION
krb5_set_password (
krb5_context /*context*/,
@@ -3198,11 +3209,6 @@ krb5_set_send_to_kdc_func (
void */*data*/);
void KRB5_LIB_FUNCTION
-krb5_set_time_wrap (
- krb5_context /*context*/,
- time_t /*t*/);
-
-void KRB5_LIB_FUNCTION
krb5_set_use_admin_kdc (
krb5_context /*context*/,
krb5_boolean /*flag*/);
@@ -3271,6 +3277,11 @@ krb5_storage_from_mem (
void */*buf*/,
size_t /*len*/);
+krb5_storage * KRB5_LIB_FUNCTION
+krb5_storage_from_readonly_mem (
+ const void */*buf*/,
+ size_t /*len*/);
+
krb5_flags KRB5_LIB_FUNCTION
krb5_storage_get_byteorder (
krb5_storage */*sp*/,
diff --git a/source4/heimdal/lib/krb5/krb5.h b/source4/heimdal/lib/krb5/krb5.h
index f5c8b069de..1b26e8b3e7 100644
--- a/source4/heimdal/lib/krb5/krb5.h
+++ b/source4/heimdal/lib/krb5/krb5.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*/
-/* $Id: krb5.h,v 1.254 2006/11/07 00:17:42 lha Exp $ */
+/* $Id: krb5.h,v 1.255 2006/11/12 08:33:07 lha Exp $ */
#ifndef __KRB5_H__
#define __KRB5_H__
@@ -78,6 +78,8 @@ typedef struct krb5_get_creds_opt_data *krb5_get_creds_opt;
struct krb5_digest;
typedef struct krb5_digest *krb5_digest;
+struct krb5_pac;
+
typedef struct krb5_rd_req_in_ctx *krb5_rd_req_in_ctx;
typedef struct krb5_rd_req_out_ctx *krb5_rd_req_out_ctx;
diff --git a/source4/heimdal/lib/krb5/krb5_locl.h b/source4/heimdal/lib/krb5/krb5_locl.h
index 89b3c6ad40..3fb5461b3c 100644
--- a/source4/heimdal/lib/krb5/krb5_locl.h
+++ b/source4/heimdal/lib/krb5/krb5_locl.h
@@ -148,6 +148,12 @@ struct krb5_dh_moduli;
/* v4 glue */
struct _krb5_krb_auth_data;
+struct krb5_plugin;
+enum plugin_type {
+ PLUGIN_TYPE_DATA = 1,
+ PLUGIN_TYPE_FUNC
+};
+
#include <der.h>
#include <krb5.h>
diff --git a/source4/heimdal/lib/krb5/krbhst.c b/source4/heimdal/lib/krb5/krbhst.c
index e7b2579229..f395f0d0c3 100644
--- a/source4/heimdal/lib/krb5/krbhst.c
+++ b/source4/heimdal/lib/krb5/krbhst.c
@@ -33,8 +33,9 @@
#include "krb5_locl.h"
#include <resolve.h>
+#include "locate_plugin.h"
-RCSID("$Id: krbhst.c,v 1.57 2006/10/06 17:11:02 lha Exp $");
+RCSID("$Id: krbhst.c,v 1.58 2006/11/12 20:05:20 lha Exp $");
static int
string_to_proto(const char *string)
@@ -147,6 +148,7 @@ struct krb5_krbhst_data {
#define KD_FALLBACK 16
#define KD_CONFIG_EXISTS 32
#define KD_LARGE_MSG 64
+#define KD_PLUGIN 128
krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *,
krb5_krbhst_info**);
@@ -460,8 +462,8 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
hi->proto = proto;
hi->port = hi->def_port = port;
hi->ai = ai;
- memmove(hi->hostname, host, hostlen - 1);
- hi->hostname[hostlen - 1] = '\0';
+ memmove(hi->hostname, host, hostlen);
+ hi->hostname[hostlen] = '\0';
free(host);
append_host_hostinfo(kd, hi);
kd->fallback_count++;
@@ -469,6 +471,88 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
return 0;
}
+/*
+ * Fetch hosts from plugin
+ */
+
+static krb5_error_code
+add_locate(void *ctx, int type, struct sockaddr *addr)
+{
+ struct krb5_krbhst_info *hi;
+ struct krb5_krbhst_data *kd = ctx;
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ struct addrinfo hints, *ai;
+ socklen_t socklen;
+ size_t hostlen;
+ int ret;
+
+ socklen = socket_sockaddr_size(addr);
+
+ ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ if (ret != 0)
+ return 0;
+
+ memset(&hints, 0, sizeof(hints));
+ ret = getaddrinfo(host, port, &hints, &ai);
+ if (ret)
+ return 0;
+
+ hostlen = strlen(host);
+
+ hi = calloc(1, sizeof(*hi) + hostlen);
+ if(hi == NULL) {
+ free(host);
+ return ENOMEM;
+ }
+
+ hi->proto = krbhst_get_default_proto(kd);
+ hi->port = hi->def_port = socket_get_port(addr);
+ hi->ai = ai;
+ memmove(hi->hostname, host, hostlen);
+ hi->hostname[hostlen] = '\0';
+ append_host_hostinfo(kd, hi);
+
+ return 0;
+}
+
+static void
+plugin_get_hosts(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ enum locate_service_type type)
+{
+ struct krb5_plugin *list, *e;
+ krb5_error_code ret;
+
+ ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "resolve", &list);
+ if(ret != 0 || list == NULL)
+ return;
+
+ kd->flags |= KD_CONFIG_EXISTS;
+
+ for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
+ krb5plugin_service_locate_ftable *service;
+ void *ctx;
+
+ service = _krb5_plugin_get_symbol(e);
+ if (service->minor_version != 0)
+ continue;
+
+ (*service->init)(context, &ctx);
+ ret = (*service->lookup)(ctx, type, kd->realm, 0, 0, add_locate, kd);
+ (*service->fini)(ctx);
+ if (ret) {
+ krb5_set_error_string(context, "Plugin failed to lookup");
+ break;
+ }
+ }
+ _krb5_plugin_free(list);
+}
+
+/*
+ *
+ */
+
static krb5_error_code
kdc_get_next(krb5_context context,
struct krb5_krbhst_data *kd,
@@ -476,6 +560,13 @@ kdc_get_next(krb5_context context,
{
krb5_error_code ret;
+ if ((kd->flags & KD_PLUGIN) == 0) {
+ plugin_get_hosts(context, kd, locate_service_kdc);
+ kd->flags |= KD_PLUGIN;
+ if(get_next(kd, host))
+ return 0;
+ }
+
if((kd->flags & KD_CONFIG) == 0) {
config_get_hosts(context, kd, "kdc");
kd->flags |= KD_CONFIG;
diff --git a/source4/heimdal/lib/krb5/locate_plugin.h b/source4/heimdal/lib/krb5/locate_plugin.h
new file mode 100644
index 0000000000..ec06d362cf
--- /dev/null
+++ b/source4/heimdal/lib/krb5/locate_plugin.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id: locate_plugin.h,v 1.1 2006/11/12 19:00:03 lha Exp $ */
+
+#ifndef HEIMDAL_KRB5_LOCATE_PLUGIN_H
+#define HEIMDAL_KRB5_LOCATE_PLUGIN_H 1
+
+#include <krb5.h>
+
+enum locate_service_type {
+ locate_service_kdc = 1,
+ locate_service_master_kdc,
+ locate_service_kadmin,
+ locate_service_krb524,
+ locate_service_kpasswd
+};
+
+typedef krb5_error_code
+(*krb5plugin_service_locate_lookup) (void *, enum locate_service_type,
+ const char *, int, int,
+ int (*)(void *,int,struct sockaddr *),
+ void *);
+
+
+typedef struct krb5plugin_service_locate_ftable {
+ int minor_version;
+ krb5_error_code (*init)(krb5_context, void **);
+ void (*fini)(void *);
+ krb5plugin_service_locate_lookup lookup;
+} krb5plugin_service_locate_ftable;
+
+#endif /* HEIMDAL_KRB5_LOCATE_PLUGIN_H */
+
diff --git a/source4/heimdal/lib/krb5/mit_glue.c b/source4/heimdal/lib/krb5/mit_glue.c
index b9075b3079..493c4cd845 100755
--- a/source4/heimdal/lib/krb5/mit_glue.c
+++ b/source4/heimdal/lib/krb5/mit_glue.c
@@ -32,7 +32,7 @@
*/
#include "krb5_locl.h"
-RCSID("$Id: mit_glue.c,v 1.8 2006/10/14 09:51:02 lha Exp $");
+RCSID("$Id: mit_glue.c,v 1.9 2006/11/09 21:24:16 lha Exp $");
/*
* Glue for MIT API
@@ -325,3 +325,11 @@ krb5_c_make_random_key(krb5_context context,
{
return krb5_generate_random_keyblock(context, enctype, random_key);
}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_keylength(krb5_context context,
+ krb5_enctype enctype,
+ size_t *len)
+{
+ return krb5_enctype_keysize(context, enctype, len);
+}
diff --git a/source4/heimdal/lib/krb5/plugin.c b/source4/heimdal/lib/krb5/plugin.c
new file mode 100644
index 0000000000..294807faab
--- /dev/null
+++ b/source4/heimdal/lib/krb5/plugin.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id: plugin.c,v 1.2 2006/11/12 21:39:43 lha Exp $");
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+#include <dirent.h>
+
+struct krb5_plugin {
+ void *symbol;
+ void *dsohandle;
+ struct krb5_plugin *next;
+};
+
+struct plugin {
+ enum plugin_type type;
+ void *name;
+ void *symbol;
+ struct plugin *next;
+};
+
+static HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER;
+static struct plugin *registered = NULL;
+
+static const char *plugin_dir = LIBDIR "/plugin/krb5";
+
+/*
+ *
+ */
+
+void *
+_krb5_plugin_get_symbol(struct krb5_plugin *p)
+{
+ return p->symbol;
+}
+
+struct krb5_plugin *
+_krb5_plugin_get_next(struct krb5_plugin *p)
+{
+ return p->next;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+loadlib(krb5_context context,
+ enum plugin_type type,
+ const char *name,
+ const char *lib,
+ struct krb5_plugin **e)
+{
+ *e = calloc(1, sizeof(**e));
+ if (*e == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ return ENOMEM;
+ }
+
+ (*e)->dsohandle = dlopen(lib, 0);
+ if ((*e)->dsohandle == NULL) {
+ free(*e);
+ krb5_set_error_string(context, "Failed to load %s: %s",
+ lib, dlerror());
+ return ENOMEM;
+ }
+
+ /* dlsym doesn't care about the type */
+ (*e)->symbol = dlsym((*e)->dsohandle, name);
+ if ((*e)->symbol == NULL) {
+ dlclose((*e)->dsohandle);
+ free(*e);
+ krb5_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_plugin_register(krb5_context context,
+ enum plugin_type type,
+ const char *name,
+ void *symbol)
+{
+ struct plugin *e;
+
+ e = calloc(1, sizeof(*e));
+ if (e == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ return ENOMEM;
+ }
+ e->type = type;
+ e->name = strdup(name);
+ if (e->name == NULL) {
+ free(e);
+ krb5_set_error_string(context, "out of memory");
+ return ENOMEM;
+ }
+ e->symbol = symbol;
+
+ HEIMDAL_MUTEX_lock(&plugin_mutex);
+ e->next = registered;
+ registered = e;
+ HEIMDAL_MUTEX_unlock(&plugin_mutex);
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_plugin_find(krb5_context context,
+ enum plugin_type type,
+ const char *name,
+ struct krb5_plugin **list)
+{
+ struct krb5_plugin *e;
+ struct plugin *p;
+ krb5_error_code ret;
+ char *sysdirs[2] = { NULL, NULL };
+ char **dirs = NULL, **di;
+ struct dirent *entry;
+ char *path;
+ DIR *d = NULL;
+
+ *list = NULL;
+
+ HEIMDAL_MUTEX_lock(&plugin_mutex);
+
+ for (p = registered; p != NULL; p = p->next) {
+ if (p->type != type || strcmp(p->name, name) != 0)
+ continue;
+
+ e = calloc(1, sizeof(*e));
+ if (e == NULL) {
+ HEIMDAL_MUTEX_unlock(&plugin_mutex);
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+ e->symbol = p->symbol;
+ e->dsohandle = NULL;
+ e->next = *list;
+ *list = e;
+ }
+ HEIMDAL_MUTEX_unlock(&plugin_mutex);
+
+ dirs = krb5_config_get_strings(context, NULL, "libdefaults",
+ "plugin_dir", NULL);
+ if (dirs == NULL) {
+ sysdirs[0] = rk_UNCONST(plugin_dir);
+ dirs = sysdirs;
+ }
+
+ for (di = dirs; *di != NULL; di++) {
+
+ d = opendir(*di);
+ if (d == NULL)
+ continue;
+
+ while ((entry = readdir(d)) != NULL) {
+ asprintf(&path, "%s/%s", *di, entry->d_name);
+ if (path == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+ ret = loadlib(context, type, name, path, &e);
+ free(path);
+ if (ret)
+ continue;
+
+ e->next = *list;
+ *list = e;
+ }
+ closedir(d);
+ }
+ if (dirs != sysdirs)
+ krb5_config_free_strings(dirs);
+
+ if (*list == NULL) {
+ krb5_set_error_string(context, "Did not find a plugin for %s", name);
+ return ENOENT;
+ }
+
+ return 0;
+
+out:
+ if (dirs && dirs != sysdirs)
+ krb5_config_free_strings(dirs);
+ if (d)
+ closedir(d);
+ _krb5_plugin_free(*list);
+ *list = NULL;
+
+ return ret;
+}
+
+void
+_krb5_plugin_free(struct krb5_plugin *list)
+{
+ struct krb5_plugin *next;
+ while (list) {
+ next = list->next;
+ if (list->dsohandle)
+ dlclose(list->dsohandle);
+ free(list);
+ list = next;
+ }
+}
+