Exemple #1
0
void krb5int_rc_terminate(void)
{
    struct krb5_rc_typelist *t, *t_next;
    struct mem_data *tgr = (struct mem_data *)grcache.data;
    struct authlist *q, *qt;
    int i;

    k5_mutex_destroy(&grcache.lock);

    if (tgr != NULL) {
    	if (tgr->name)
		free(tgr->name);
    	for (i = 0; i < tgr->hsize; i++)
		for (q = tgr->h[i]; q; q = qt) {
			qt = q->nh;
			free(q->rep.server);
			free(q->rep.client);
			free(q);
		}
    	if (tgr->h)
		free(tgr->h);
    	free(tgr);
    }

    k5_mutex_destroy(&rc_typelist_lock);
    for (t = typehead; t != &krb5_rc_typelist_dfl; t = t_next) {
	t_next = t->next;
	free(t);
    }
}
void
gssint_mechglue_fini(void)
{
	k5_mutex_destroy(&g_mechSetLock);
	k5_mutex_destroy(&g_mechListLock);
	free_mechSet();
	freeMechList();
}
Exemple #3
0
void krb5int_lib_fini(void)
{
    if (!INITIALIZER_RAN(krb5int_lib_init) || PROGRAM_EXITING()) {
#ifdef SHOW_INITFINI_FUNCS
        printf("krb5int_lib_fini: skipping\n");
#endif
        return;
    }

#ifdef SHOW_INITFINI_FUNCS
    printf("krb5int_lib_fini\n");
#endif

    k5_mutex_destroy(&krb5int_us_time_mutex);

    krb5int_cc_finalize();
    krb5int_kt_finalize();
    krb5int_rc_terminate();

#if defined(_WIN32) || defined(USE_CCAPI)
    krb5_stdcc_shutdown();
#endif

#if !USE_BUNDLE_ERROR_STRINGS
    remove_error_table(&et_krb5_error_table);
    remove_error_table(&et_kv5m_error_table);
    remove_error_table(&et_kdb5_error_table);
    remove_error_table(&et_asn1_error_table);
    remove_error_table(&et_k524_error_table);
#endif
    krb5int_set_error_info_callout_fn (0);
}
Exemple #4
0
static void kim_os_library_thread_fini (void)
{
    if (!INITIALIZER_RAN (kim_os_library_thread_init) || PROGRAM_EXITING ()) {
	return;
    }
    k5_mutex_destroy (&g_bundle_lookup_mutex);
}
Exemple #5
0
/* Call with mutex locked!  */
static void profile_free_file_data(prf_data_t data)
{
    scan_shared_trees_locked();
    if (data->flags & PROFILE_FILE_SHARED) {
	/* Remove from linked list.  */
	if (g_shared_trees == data)
	    g_shared_trees = data->next;
	else {
	    prf_data_t prev, next;
	    prev = g_shared_trees;
	    next = prev->next;
	    while (next) {
		if (next == data) {
		    prev->next = next->next;
		    break;
		}
		prev = next;
		next = next->next;
	    }
	}
    }
    if (data->root)
	profile_free_node(data->root);
    if (data->comment)
	free(data->comment);
    data->magic = 0;
    k5_mutex_destroy(&data->lock);
    free(data);
    scan_shared_trees_locked();
}
Exemple #6
0
void
k5_prng_cleanup(void)
{
    have_entropy = FALSE;
    zap(&main_state, sizeof(main_state));
    k5_mutex_destroy(&fortuna_lock);
}
Exemple #7
0
void krb5int_thread_support_fini (void)
{
    if (! INITIALIZER_RAN (krb5int_thread_support_init))
        return;

#ifdef SHOW_INITFINI_FUNCS
    printf("krb5int_thread_support_fini\n");
#endif

#ifndef ENABLE_THREADS

    /* Do nothing.  */

#elif defined(_WIN32)

    /* ... free stuff ... */
    TlsFree(tls_idx);
    DeleteCriticalSection(&key_lock);

#else /* POSIX */

    if (! INITIALIZER_RAN(krb5int_thread_support_init))
        return;
    if (K5_PTHREADS_LOADED)
        pthread_key_delete(key);
    /* ... delete stuff ... */
    k5_mutex_destroy(&key_lock);

#endif

    krb5int_fini_fac();
}
Exemple #8
0
void KRB5_CALLCONV
profile_abandon(profile_t profile)
{
    prf_file_t      p, next;
    errcode_t       err;

    if (!profile || profile->magic != PROF_MAGIC_PROFILE)
        return;

    if (profile->vt) {
        if (profile->vt->cleanup)
            profile->vt->cleanup(profile->cbdata);
        if (profile->lib_handle) {
            /* Decrement the refcount on the handle and maybe free it. */
            err = k5_mutex_lock(&profile->lib_handle->lock);
            if (!err && --profile->lib_handle->refcount == 0) {
                krb5int_close_plugin(profile->lib_handle->plugin_handle);
                k5_mutex_unlock(&profile->lib_handle->lock);
                k5_mutex_destroy(&profile->lib_handle->lock);
                free(profile->lib_handle);
            } else if (!err)
                k5_mutex_unlock(&profile->lib_handle->lock);
        }
        free(profile->vt);
    } else {
        for (p = profile->first_file; p; p = next) {
            next = p->next;
            profile_free_file(p);
        }
    }
    profile->magic = 0;
    free(profile);
}
Exemple #9
0
krb5_error_code
krb5_rc_default(krb5_context context, krb5_rcache *id)
{
    krb5_error_code retval;

    if (!(*id = (krb5_rcache )malloc(sizeof(**id))))
	return KRB5_RC_MALLOC;

    retval = krb5_rc_resolve_type(context, id, krb5_rc_default_type(context));
    if (retval != 0) {
	/*
	 * k5_mutex_destroy() is not called here, because the mutex had
	 * not been successfully initialized by krb5_rc_resolve_type().
	 */
	FREE_RC(*id);
	return (retval);
    }
    retval = krb5_rc_resolve(context, *id, krb5_rc_default_name(context));
    if (retval) {
        k5_mutex_destroy(&(*id)->lock);
	FREE_RC(*id);
	return retval;
    }
    (*id)->magic = KV5M_RCACHE;
    return retval;
}
Exemple #10
0
void
krb5_ldap_free_server_params(krb5_ldap_context *ctx)
{
    if (ctx == NULL)
        return;
    krb5_ldap_free_server_context_params(ctx);
    k5_mutex_destroy(&ctx->hndl_lock);
    free(ctx);
}
Exemple #11
0
void kim_error_terminate (void)
{
    if (!INITIALIZER_RAN (kim_error_initialize) || PROGRAM_EXITING ()) {
	return;
    }

    k5_key_delete (K5_KEY_KIM_ERROR_MESSAGE);
    k5_mutex_destroy (&kim_error_lock);
}
Exemple #12
0
krb5_error_code KRB5_CALLCONV
krb5_rc_dfl_close(krb5_context context, krb5_rcache id)
{
    k5_mutex_lock(&id->lock);
    krb5_rc_dfl_close_no_free(context, id);
    k5_mutex_unlock(&id->lock);
    k5_mutex_destroy(&id->lock);
    free(id);
    return 0;
}
Exemple #13
0
void
krb5int_kt_finalize(void)
{
    const struct krb5_kt_typelist *t, *t_next;

    k5_mutex_destroy(&kt_typehead_lock);
    for (t = kt_typehead; t != &krb5_kt_typelist_file; t = t_next) {
	t_next = t->next;
	free((struct krb5_kt_typelist *)t);
    }

    krb5int_mkt_finalize();
}
Exemple #14
0
void com_err_terminate(void)
{
    struct et_list *e, *enext;
    if (! INITIALIZER_RAN(com_err_initialize) || PROGRAM_EXITING()) {
#ifdef SHOW_INITFINI_FUNCS
        printf("com_err_terminate: skipping\n");
#endif
        return;
    }
#ifdef SHOW_INITFINI_FUNCS
    printf("com_err_terminate\n");
#endif
    k5_key_delete(K5_KEY_COM_ERR);
    k5_mutex_destroy(&com_err_hook_lock);
    k5_mutex_lock(&et_list_lock);
    for (e = et_list; e; e = enext) {
        enext = e->next;
        free(e);
    }
    k5_mutex_unlock(&et_list_lock);
    k5_mutex_destroy(&et_list_lock);
    terminated = 1;
}
Exemple #15
0
void
gssint_mechglue_fini(void)
{
	if (!INITIALIZER_RAN(gssint_mechglue_init) || PROGRAM_EXITING()) {
#ifdef SHOW_INITFINI_FUNCS
		printf("gssint_mechglue_fini: skipping\n");
#endif
		return;
	}

#ifdef SHOW_INITFINI_FUNCS
	printf("gssint_mechglue_fini\n");
#endif
#ifdef _GSS_STATIC_LINK
	gss_spnegoint_lib_fini();
	gss_krb5int_lib_fini();
#endif
	k5_mutex_destroy(&g_mechSetLock);
	k5_mutex_destroy(&g_mechListLock);
	free_mechSet();
	freeMechList();
	remove_error_table(&et_ggss_error_table);
	gssint_mecherrmap_destroy();
}
Exemple #16
0
void krb5int_mkt_finalize(void) {
    krb5_mkt_list_node *node, *next_node;
    krb5_mkt_cursor cursor, next_cursor;

    k5_mutex_destroy(&krb5int_mkt_mutex);

    for (node = krb5int_mkt_list; node; node = next_node) {
	next_node = node->next;

	/* destroy the contents of node->keytab */
	free(KTNAME(node->keytab));

	/* free the keytab entries */
	for (cursor = KTLINK(node->keytab); cursor; cursor = next_cursor) {
	    next_cursor = cursor->next;
	    /* the call to krb5_kt_free_entry uses a NULL in place of the
	     * krb5_context since we know that the context isn't used by
	     * krb5_kt_free_entry or krb5_free_principal. */
	    krb5_kt_free_entry(NULL, cursor->entry);
	    free(cursor->entry);
	    free(cursor);
	}

	/* destroy the lock */
	k5_mutex_destroy(&(((krb5_mkt_data *)node->keytab->data)->lock));

	/* free the private data */
	free(node->keytab->data);

	/* and the keytab */
	free(node->keytab);

	/* and finally the node */
	free(node);
    }
}
Exemple #17
0
void profile_library_finalizer(void)
{
    if (! INITIALIZER_RAN(profile_library_initializer) || PROGRAM_EXITING()) {
#ifdef SHOW_INITFINI_FUNCS
	printf("profile_library_finalizer: skipping\n");
#endif
	return;
    }
#ifdef SHOW_INITFINI_FUNCS
    printf("profile_library_finalizer\n");
#endif
    k5_mutex_destroy(&g_shared_trees_mutex);

    remove_error_table(&et_prof_error_table);
}
Exemple #18
0
krb5_error_code
kg_release_name(krb5_context context,
                krb5_gss_name_t *name)
{
    if (*name != NULL) {
        krb5_free_principal(context, (*name)->princ);
        free((*name)->service);
        free((*name)->host);
        krb5_authdata_context_free(context, (*name)->ad_context);
        k5_mutex_destroy(&(*name)->lock);
        free(*name);
        *name = NULL;
    }

    return 0;
}
Exemple #19
0
krb5_error_code
kg_release_name(krb5_context context,
                krb5_flags flags,
                krb5_gss_name_t *name)
{
    if (*name != NULL) {
        if (flags & KG_INIT_NAME_INTERN)
            kg_delete_name((gss_name_t)*name);
        krb5_free_principal(context, (*name)->princ);
        krb5_authdata_context_free(context, (*name)->ad_context);
        k5_mutex_destroy(&(*name)->lock);
        free(*name);
        *name = NULL;
    }

    return 0;
}
Exemple #20
0
void
krb5int_cc_finalize(void)
{
    struct krb5_cc_typelist *t, *t_next;
    k5_cccol_force_unlock();
    k5_cc_mutex_destroy(&cccol_lock);
    k5_mutex_destroy(&cc_typelist_lock);
#ifndef NO_FILE_CCACHE
    k5_cc_mutex_destroy(&krb5int_cc_file_mutex);
#endif
    k5_cc_mutex_destroy(&krb5int_mcc_mutex);
#ifdef USE_KEYRING_CCACHE
    k5_cc_mutex_destroy(&krb5int_krcc_mutex);
#endif
    for (t = cc_typehead; t != INITIAL_TYPEHEAD; t = t_next) {
        t_next = t->next;
        free(t);
    }
}
Exemple #21
0
krb5_error_code krb5_rc_resolve_full(krb5_context context, krb5_rcache *id, char *string_name)
{
    char *type;
    char *residual;
    krb5_error_code retval;
    unsigned int diff;

    if (!(residual = strchr(string_name,':')))
	return KRB5_RC_PARSE;

    diff = residual - string_name;
    if (!(type = malloc(diff + 1)))
	return KRB5_RC_MALLOC;
    (void) strncpy(type, string_name, diff);
    type[residual - string_name] = '\0';

    if (!(*id = (krb5_rcache) malloc(sizeof(**id)))) {
	FREE_RC(type);
	return KRB5_RC_MALLOC;
    }

    retval = krb5_rc_resolve_type(context, id, type);
    if (retval != 0) {
	/*
	 * k5_mutex_destroy() is not called here, because the mutex had
	 * not been successfully initialized by krb5_rc_resolve_type().
	 */
	FREE_RC(type);
	FREE_RC(*id);
	return retval;
    }
    FREE_RC(type);
    retval = krb5_rc_resolve(context, *id, residual + 1);
    if (retval) {
        k5_mutex_destroy(&(*id)->lock);
	FREE_RC(*id);
	return retval;
    }
    (*id)->magic = KV5M_RCACHE;
    return retval;
}
static void k5_cli_ipc_thread_fini (void)
{    
    int err = 0;
    
    err = k5_mutex_lock (&g_service_ports_mutex);

    if (!err) {
        int i;
        
        for (i = 0; i < KIPC_SERVICE_COUNT; i++) {
            if (MACH_PORT_VALID (g_service_ports[i].service_port)) {
                mach_port_destroy (mach_task_self (), 
                                   g_service_ports[i].service_port); 
                g_service_ports[i].service_port = MACH_PORT_NULL;
            }
        }
        k5_mutex_unlock (&g_service_ports_mutex);
    }
    
    k5_key_delete (K5_KEY_IPC_CONNECTION_INFO);
    k5_mutex_destroy (&g_service_ports_mutex);
}
Exemple #23
0
void krb5int_thread_support_fini (void)
{
    if (! INITIALIZER_RAN (krb5int_thread_support_init))
	return;

#ifdef SHOW_INITFINI_FUNCS
    printf("krb5int_thread_support_fini\n");
#endif

#ifndef ENABLE_THREADS

    /* Do nothing.  */

#elif defined(_WIN32)

    /* ... free stuff ... */
    TlsFree(tls_idx);
    DeleteCriticalSection(&key_lock);

#else /* POSIX */

    if (! INITIALIZER_RAN(krb5int_thread_support_init))
	return;
    if (K5_PTHREADS_LOADED)
	pthread_key_delete(key);
    /* ... delete stuff ... */
    k5_mutex_destroy(&key_lock);

#endif

#ifdef DEBUG_THREADS_STATS
    fflush(stats_logfile);
    /* XXX Should close if not stderr, in case unloading library but
       not exiting.  */
#endif

    krb5int_fini_fac();
}
Exemple #24
0
void KRB5_CALLCONV
krb5int_mutex_free (k5_mutex_t *m)
{
    (void) k5_mutex_destroy (m);
    free (m);
}
Exemple #25
0
void
kdb_fini_lock_list(void)
{
    if (INITIALIZER_RAN(kdb_init_lock_list))
	k5_mutex_destroy(&db_lock);
}
Exemple #26
0
OM_uint32
kg_compose_deleg_cred(OM_uint32 *minor_status,
                      krb5_gss_cred_id_t impersonator_cred,
                      krb5_creds *subject_creds,
                      OM_uint32 time_req,
                      krb5_gss_cred_id_t *output_cred,
                      OM_uint32 *time_rec,
                      krb5_context context)
{
    OM_uint32 major_status;
    krb5_error_code code;
    krb5_gss_cred_id_t cred = NULL;

    *output_cred = NULL;
    k5_mutex_assert_locked(&impersonator_cred->lock);

    if (!kg_is_initiator_cred(impersonator_cred) ||
        impersonator_cred->name == NULL ||
        impersonator_cred->impersonator != NULL) {
        code = G_BAD_USAGE;
        goto cleanup;
    }

    assert(impersonator_cred->name->princ != NULL);

    assert(subject_creds != NULL);
    assert(subject_creds->client != NULL);

    cred = xmalloc(sizeof(*cred));
    if (cred == NULL) {
        code = ENOMEM;
        goto cleanup;
    }
    memset(cred, 0, sizeof(*cred));

    code = k5_mutex_init(&cred->lock);
    if (code != 0)
        goto cleanup;

    cred->usage = GSS_C_INITIATE;

    cred->tgt_expire = subject_creds->times.endtime;

    code = kg_init_name(context, subject_creds->client, NULL, NULL, NULL, 0,
                        &cred->name);
    if (code != 0)
        goto cleanup;

    code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache);
    if (code != 0)
        goto cleanup;
    cred->destroy_ccache = 1;

    code = krb5_cc_initialize(context, cred->ccache, subject_creds->client);
    if (code != 0)
        goto cleanup;

    /*
     * Only return a "proxy" credential for use with constrained
     * delegation if the subject credentials are forwardable.
     * Submitting non-forwardable credentials to the KDC for use
     * with constrained delegation will only return an error.
     */
    if (subject_creds->ticket_flags & TKT_FLG_FORWARDABLE) {
        code = make_proxy_cred(context, cred, impersonator_cred);
        if (code != 0)
            goto cleanup;
    }

    code = krb5_cc_store_cred(context, cred->ccache, subject_creds);
    if (code != 0)
        goto cleanup;

    if (time_rec != NULL) {
        krb5_timestamp now;

        code = krb5_timeofday(context, &now);
        if (code != 0)
            goto cleanup;

        *time_rec = cred->tgt_expire - now;
    }

    major_status = GSS_S_COMPLETE;
    *minor_status = 0;
    *output_cred = cred;

cleanup:
    if (code != 0) {
        *minor_status = code;
        major_status = GSS_S_FAILURE;
    }

    if (GSS_ERROR(major_status) && cred != NULL) {
        k5_mutex_destroy(&cred->lock);
        krb5_cc_destroy(context, cred->ccache);
        kg_release_name(context, &cred->name);
        xfree(cred);
    }

    return major_status;
}
Exemple #27
0
krb5_error_code KRB5_CALLCONV
krb5_mkt_close(krb5_context context, krb5_keytab id)
{
    krb5_mkt_list_node **listp;
#ifdef HEIMDAL_COMPATIBLE
    krb5_mkt_list_node *node;
    krb5_mkt_data * data;
#endif
    krb5_error_code err = 0;

    /* First determine if a memory keytab of this name already exists */
    err = KTGLOCK;
    if (err)
	return(err);

    for (listp = &krb5int_mkt_list; *listp; listp = &((*listp)->next))
    {
	if (id == (*listp)->keytab) {
	    /* Found */
	    break;
	}
    }

    if (*listp == NULL) {
	/* The specified keytab could not be found */
	err = KRB5_KT_NOTFOUND;
	goto done;
    }

    /* reduce the refcount and return */
    err = KTLOCK(id);
    if (err)
	goto done;

    KTREFCNT(id)--;
    KTUNLOCK(id);

#ifdef HEIMDAL_COMPATIBLE
    /* In Heimdal if the refcount hits 0, the MEMORY keytab is
     * destroyed since there is no krb5_kt_destroy function.
     * There is no need to lock the entry while performing
     * these operations as the refcount will be 0 and we are
     * holding the global lock.
     */
    data = (krb5_mkt_data *)id->data;
    if (data->refcount == 0) {
	krb5_mkt_cursor cursor, next_cursor;

	node = *listp;
	*listp = node->next;

	/* destroy the contents of node->keytab (aka id) */
	free(data->name);

	/* free the keytab entries */
	for (cursor = KTLINK(node->keytab); cursor; cursor = next_cursor) {
	    next_cursor = cursor->next;

	    krb5_kt_free_entry(context, cursor->entry);
	    free(cursor->entry);
	    free(cursor);
	}

	/* destroy the lock */
	k5_mutex_destroy(&(data->lock));

	/* free the private data */
	free(data);

	/* and the keytab */
	free(node->keytab);

	/* and finally the node */
	free(node);
    }
#endif /* HEIMDAL_COMPATIBLE */

  done:
    KTGUNLOCK;
    return(err);
}
Exemple #28
0
krb5_error_code KRB5_CALLCONV
krb5_mkt_resolve(krb5_context context, const char *name, krb5_keytab *id)
{
    krb5_mkt_data *data = 0;
    krb5_mkt_list_node *list;
    krb5_error_code err = 0;

    /* First determine if a memory keytab of this name already exists */
    err = KTGLOCK;
    if (err)
	return(err);

    for (list = krb5int_mkt_list; list; list = list->next)
    {
	if (strcmp(name,KTNAME(list->keytab)) == 0) {
	    /* Found */
	    *id = list->keytab;
	    goto done;
	}
    }

    /* We will now create the new key table with the specified name.
     * We do not drop the global lock, therefore the name will indeed
     * be unique when we add it.
     */

    if ((list = (krb5_mkt_list_node *)malloc(sizeof(krb5_mkt_list_node))) == NULL) {
	err = ENOMEM;
	goto done;
    }

    if ((list->keytab = (krb5_keytab)malloc(sizeof(struct _krb5_kt))) == NULL) {
	free(list);
	err = ENOMEM;
	goto done;
    }

    list->keytab->ops = &krb5_mkt_ops;
    if ((data = (krb5_mkt_data *)malloc(sizeof(krb5_mkt_data))) == NULL) {
	free(list->keytab);
	free(list);
	err = ENOMEM;
	goto done;
    }
    data->name = NULL;

    err = k5_mutex_init(&data->lock);
    if (err) {
	free(data);
	free(list->keytab);
	free(list);
	goto done;
    }

    if ((data->name = strdup(name)) == NULL) {
	k5_mutex_destroy(&data->lock);
	free(data);
	free(list->keytab);
	free(list);
	err = ENOMEM;
	goto done;
    }

    data->link = NULL;
    data->refcount = 0;
    list->keytab->data = (krb5_pointer)data;
    list->keytab->magic = KV5M_KEYTAB;

    list->next = krb5int_mkt_list;
    krb5int_mkt_list = list;

    *id = list->keytab;

  done:
    err = KTLOCK(*id);
    if (err) {
	k5_mutex_destroy(&data->lock);
	if (data && data->name)
		free(data->name);
	free(data);
	if (list && list->keytab)
		free(list->keytab);
	free(list);
    } else {
	KTREFCNT(*id)++;
	KTUNLOCK(*id);
    }

    KTGUNLOCK;
    return(err);
}
Exemple #29
0
/* Load a dynamic profile module as specified by modspec and create a vtable
 * profile for it in *ret_profile. */
static errcode_t
init_load_module(const char *modspec, profile_t *ret_profile)
{
    char *modpath = NULL, *residual = NULL;
    struct errinfo einfo = { 0 };
    prf_lib_handle_t lib_handle = NULL;
    struct plugin_file_handle *plhandle = NULL;
    void *cbdata = NULL, (*fptr)();
    int have_lock = 0, have_cbdata = 0;
    struct profile_vtable vtable = { 1 };  /* Set minor_ver to 1, rest null. */
    errcode_t err;
    profile_module_init_fn initfn;

    err = parse_modspec(modspec, &modpath, &residual);
    if (err)
        goto cleanup;

    /* Allocate a reference-counted library handle container. */
    lib_handle = malloc(sizeof(*lib_handle));
    if (lib_handle == NULL)
        goto cleanup;
    err = k5_mutex_init(&lib_handle->lock);
    if (err)
        goto cleanup;
    have_lock = 1;

    /* Open the module and get its initializer. */
    err = krb5int_open_plugin(modpath, &plhandle, &einfo);
    if (err)
        goto cleanup;
    err = krb5int_get_plugin_func(plhandle, "profile_module_init", &fptr,
                                  &einfo);
    if (err == ENOENT)
        err = PROF_MODULE_INVALID;
    if (err)
        goto cleanup;

    /* Get the profile vtable and callback data pointer. */
    initfn = (profile_module_init_fn)fptr;
    err = (*initfn)(residual, &vtable, &cbdata);
    if (err)
        goto cleanup;
    have_cbdata = 1;

    /* Create a vtable profile with the information obtained. */
    lib_handle->plugin_handle = plhandle;
    lib_handle->refcount = 1;
    err = init_module(&vtable, cbdata, lib_handle, ret_profile);

cleanup:
    free(modpath);
    free(residual);
    k5_clear_error(&einfo);
    if (err) {
        if (have_cbdata && vtable.cleanup)
            vtable.cleanup(cbdata);
        if (have_lock)
            k5_mutex_destroy(&lib_handle->lock);
        free(lib_handle);
        if (plhandle)
            krb5int_close_plugin(plhandle);
    }
    return err;
}