#include "k5-int.h"
#include "cc-int.h"
#include <krb5/ccselect_plugin.h>
#include "../krb/int-proto.h"
struct ccselect_module_handle {
struct krb5_ccselect_vtable_st vt;
krb5_ccselect_moddata data;
int priority;
};
static void
free_handles(krb5_context context, struct ccselect_module_handle **handles)
{
struct ccselect_module_handle *h, **hp;
if (handles == NULL)
return;
for (hp = handles; *hp != NULL; hp++) {
h = *hp;
if (h->vt.fini)
h->vt.fini(context, h->data);
free(h);
}
free(handles);
}
static krb5_error_code
load_modules(krb5_context context)
{
krb5_error_code ret;
struct ccselect_module_handle **list = NULL, *handle;
krb5_plugin_initvt_fn *modules = NULL, *mod;
size_t count;
#ifndef _WIN32
ret = k5_plugin_register(context, PLUGIN_INTERFACE_CCSELECT, "k5identity",
ccselect_k5identity_initvt);
if (ret != 0)
goto cleanup;
#endif
ret = k5_plugin_register(context, PLUGIN_INTERFACE_CCSELECT, "realm",
ccselect_realm_initvt);
if (ret != 0)
goto cleanup;
ret = k5_plugin_register(context, PLUGIN_INTERFACE_CCSELECT, "hostname",
ccselect_hostname_initvt);
if (ret != 0)
goto cleanup;
ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_CCSELECT, &modules);
if (ret != 0)
goto cleanup;
for (count = 0; modules[count] != NULL; count++);
list = k5calloc(count + 1, sizeof(*list), &ret);
if (list == NULL)
goto cleanup;
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) {
TRACE_CCSELECT_VTINIT_FAIL(context, ret);
free(handle);
continue;
}
handle->data = NULL;
ret = handle->vt.init(context, &handle->data, &handle->priority);
if (ret != 0) {
TRACE_CCSELECT_INIT_FAIL(context, handle->vt.name, ret);
free(handle);
continue;
}
list[count++] = handle;
list[count] = NULL;
}
list[count] = NULL;
ret = 0;
context->ccselect_handles = list;
list = NULL;
cleanup:
k5_plugin_free_modules(context, modules);
free_handles(context, list);
return ret;
}
krb5_error_code KRB5_CALLCONV
krb5_cc_select(krb5_context context, krb5_principal server,
krb5_ccache *cache_out, krb5_principal *princ_out)
{
krb5_error_code ret;
int priority;
struct ccselect_module_handle **hp, *h;
krb5_ccache cache;
krb5_principal princ;
krb5_principal srvcp = NULL;
char **fbrealms = NULL;
*cache_out = NULL;
*princ_out = NULL;
if (context->ccselect_handles == NULL) {
ret = load_modules(context);
if (ret)
goto cleanup;
}
if (krb5_is_referral_realm(&server->realm) &&
server->type == KRB5_NT_SRV_HST && server->length == 2) {
ret = krb5_get_fallback_host_realm(context, &server->data[1],
&fbrealms);
if (ret && ret != KRB5_CONFIG_NODEFREALM)
goto cleanup;
if (!ret) {
ret = krb5_copy_principal(context, server, &srvcp);
if (ret)
goto cleanup;
ret = krb5_set_principal_realm(context, srvcp, fbrealms[0]);
if (ret)
goto cleanup;
server = srvcp;
}
}
for (priority = KRB5_CCSELECT_PRIORITY_AUTHORITATIVE;
priority >= KRB5_CCSELECT_PRIORITY_HEURISTIC; priority--) {
for (hp = context->ccselect_handles; *hp != NULL; hp++) {
h = *hp;
if (h->priority != priority)
continue;
ret = h->vt.choose(context, h->data, server, &cache, &princ);
if (ret == 0) {
TRACE_CCSELECT_MODCHOICE(context, h->vt.name, server, cache,
princ);
*cache_out = cache;
*princ_out = princ;
goto cleanup;
} else if (ret == KRB5_CC_NOTFOUND) {
TRACE_CCSELECT_MODNOTFOUND(context, h->vt.name, server, princ);
*princ_out = princ;
goto cleanup;
} else if (ret != KRB5_PLUGIN_NO_HANDLE) {
TRACE_CCSELECT_MODFAIL(context, h->vt.name, ret, server);
goto cleanup;
}
}
}
TRACE_CCSELECT_NOTFOUND(context, server);
ret = KRB5_CC_NOTFOUND;
cleanup:
krb5_free_principal(context, srvcp);
krb5_free_host_realm(context, fbrealms);
return ret;
}
void
k5_ccselect_free_context(krb5_context context)
{
free_handles(context, context->ccselect_handles);
context->ccselect_handles = NULL;
}