#include "k5-int.h"
#include "gssapiP_krb5.h"
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef USE_LEASH
#ifdef _WIN64
#define LEASH_DLL "leashw64.dll"
#else
#define LEASH_DLL "leashw32.dll"
#endif
static void (*pLeash_AcquireInitialTicketsIfNeeded)(krb5_context,krb5_principal,char*,int) = NULL;
static HANDLE hLeashDLL = INVALID_HANDLE_VALUE;
#endif
#ifndef LEAN_CLIENT
k5_mutex_t gssint_krb5_keytab_lock = K5_MUTEX_PARTIAL_INITIALIZER;
static char *krb5_gss_keytab = NULL;
OM_uint32
gss_krb5int_register_acceptor_identity(OM_uint32 *minor_status,
const gss_OID desired_mech,
const gss_OID desired_object,
gss_buffer_t value)
{
char *new = NULL, *old;
int err;
err = gss_krb5int_initialize_library();
if (err != 0)
return GSS_S_FAILURE;
if (value->value != NULL) {
new = strdup((char *)value->value);
if (new == NULL)
return GSS_S_FAILURE;
}
k5_mutex_lock(&gssint_krb5_keytab_lock);
old = krb5_gss_keytab;
krb5_gss_keytab = new;
k5_mutex_unlock(&gssint_krb5_keytab_lock);
free(old);
return GSS_S_COMPLETE;
}
static krb5_error_code
check_keytab(krb5_context context, krb5_keytab kt, krb5_gss_name_t name,
krb5_principal mprinc)
{
krb5_error_code code;
krb5_keytab_entry ent;
char *princname;
if (name->service == NULL) {
code = krb5_kt_get_entry(context, kt, name->princ, 0, 0, &ent);
if (code == 0)
krb5_kt_free_entry(context, &ent);
return code;
}
if (kt->ops->start_seq_get == NULL)
return 0;
code = k5_kt_have_match(context, kt, mprinc);
if (code == KRB5_KT_NOTFOUND) {
if (krb5_unparse_name(context, mprinc, &princname) == 0) {
k5_setmsg(context, code, _("No key table entry found matching %s"),
princname);
free(princname);
}
}
return code;
}
static OM_uint32
acquire_accept_cred(krb5_context context, OM_uint32 *minor_status,
krb5_keytab req_keytab, const char *rcname,
krb5_gss_cred_id_rec *cred)
{
OM_uint32 major;
krb5_error_code code;
krb5_keytab kt = NULL;
krb5_rcache rc = NULL;
assert(cred->keytab == NULL);
if (rcname != NULL) {
code = k5_rc_resolve(context, rcname, &rc);
if (code) {
major = GSS_S_FAILURE;
goto cleanup;
}
}
if (req_keytab != NULL) {
code = krb5_kt_dup(context, req_keytab, &kt);
} else {
k5_mutex_lock(&gssint_krb5_keytab_lock);
if (krb5_gss_keytab != NULL) {
code = krb5_kt_resolve(context, krb5_gss_keytab, &kt);
k5_mutex_unlock(&gssint_krb5_keytab_lock);
} else {
k5_mutex_unlock(&gssint_krb5_keytab_lock);
code = krb5_kt_default(context, &kt);
}
}
if (code) {
major = GSS_S_NO_CRED;
goto cleanup;
}
if (cred->name != NULL) {
code = kg_acceptor_princ(context, cred->name, &cred->acceptor_mprinc);
if (code) {
major = GSS_S_FAILURE;
goto cleanup;
}
code = check_keytab(context, kt, cred->name, cred->acceptor_mprinc);
if (code) {
if (code == KRB5_KT_NOTFOUND) {
k5_change_error_message_code(context, code, KG_KEYTAB_NOMATCH);
code = KG_KEYTAB_NOMATCH;
}
major = GSS_S_NO_CRED;
goto cleanup;
}
if (rc == NULL) {
code = krb5_get_server_rcache(context, &cred->name->princ->data[0],
&rc);
if (code) {
major = GSS_S_FAILURE;
goto cleanup;
}
}
} else {
code = krb5_kt_have_content(context, kt);
if (code) {
major = GSS_S_NO_CRED;
goto cleanup;
}
}
cred->keytab = kt;
kt = NULL;
cred->rcache = rc;
rc = NULL;
major = GSS_S_COMPLETE;
cleanup:
if (kt != NULL)
krb5_kt_close(context, kt);
if (rc != NULL)
k5_rc_close(context, rc);
*minor_status = code;
return major;
}
#endif
#ifdef USE_LEASH
static krb5_error_code
get_ccache_leash(krb5_context context, krb5_principal desired_princ,
krb5_ccache *ccache_out)
{
krb5_error_code code;
krb5_ccache ccache;
char ccname[256] = "";
*ccache_out = NULL;
if (hLeashDLL == INVALID_HANDLE_VALUE) {
hLeashDLL = LoadLibrary(LEASH_DLL);
if (hLeashDLL != INVALID_HANDLE_VALUE) {
(FARPROC) pLeash_AcquireInitialTicketsIfNeeded =
GetProcAddress(hLeashDLL, "not_an_API_Leash_AcquireInitialTicketsIfNeeded");
}
}
if (pLeash_AcquireInitialTicketsIfNeeded) {
pLeash_AcquireInitialTicketsIfNeeded(context, desired_princ, ccname,
sizeof(ccname));
if (!ccname[0])
return KRB5_CC_NOTFOUND;
code = krb5_cc_resolve(context, ccname, &ccache);
if (code)
return code;
} else {
code = krb5int_cc_default(context, &ccache);
if (code)
return code;
}
*ccache_out = ccache;
return 0;
}
#endif
static krb5_error_code
scan_cc_config(krb5_context context, krb5_gss_cred_id_rec *cred,
krb5_const_principal config_princ, const krb5_data *value)
{
krb5_error_code code;
krb5_data data0 = empty_data();
if (config_princ->length != 2)
return 0;
if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_PROXY_IMPERSONATOR)
&& cred->impersonator == NULL) {
code = krb5int_copy_data_contents_add0(context, value, &data0);
if (code)
return code;
code = krb5_parse_name(context, data0.data, &cred->impersonator);
krb5_free_data_contents(context, &data0);
if (code)
return code;
} else if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_REFRESH_TIME)
&& cred->refresh_time == 0) {
code = krb5int_copy_data_contents_add0(context, value, &data0);
if (code)
return code;
cred->refresh_time = atol(data0.data);
krb5_free_data_contents(context, &data0);
}
return 0;
}
static krb5_boolean
can_get_initial_creds(krb5_context context, krb5_gss_cred_id_rec *cred)
{
krb5_error_code code;
if (cred->password != NULL)
return TRUE;
if (cred->client_keytab == NULL)
return FALSE;
if (cred->name == NULL)
return !krb5_kt_have_content(context, cred->client_keytab);
code = k5_kt_have_match(context, cred->client_keytab, cred->name->princ);
return code == 0;
}
static krb5_error_code
scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred,
krb5_boolean check_name)
{
krb5_error_code code;
krb5_ccache ccache = cred->ccache;
krb5_principal ccache_princ = NULL, tgt_princ = NULL;
krb5_data *realm;
krb5_cc_cursor cursor;
krb5_creds creds;
krb5_timestamp endtime;
krb5_boolean is_tgt;
code = krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET);
if (code)
return code;
code = krb5_cc_get_principal(context, ccache, &ccache_princ);
if (code != 0)
goto cleanup;
if (cred->name == NULL) {
code = kg_init_name(context, ccache_princ, NULL, NULL, NULL,
KG_INIT_NAME_NO_COPY, &cred->name);
if (code)
goto cleanup;
ccache_princ = NULL;
} else {
if (check_name) {
if (!k5_sname_compare(context, cred->name->princ, ccache_princ)) {
code = KG_CCACHE_NOMATCH;
goto cleanup;
}
}
krb5_free_principal(context, cred->name->princ);
cred->name->princ = ccache_princ;
ccache_princ = NULL;
}
assert(cred->name->princ != NULL);
realm = krb5_princ_realm(context, cred->name->princ);
code = krb5_build_principal_ext(context, &tgt_princ,
realm->length, realm->data,
KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
realm->length, realm->data,
0);
if (code)
return code;
code = krb5_cc_start_seq_get(context, ccache, &cursor);
if (code) {
krb5_free_principal(context, tgt_princ);
return code;
}
while (!(code = krb5_cc_next_cred(context, ccache, &cursor, &creds))) {
if (krb5_is_config_principal(context, creds.server)) {
code = scan_cc_config(context, cred, creds.server, &creds.ticket);
krb5_free_cred_contents(context, &creds);
if (code)
break;
continue;
}
is_tgt = krb5_principal_compare(context, tgt_princ, creds.server);
endtime = creds.times.endtime;
krb5_free_cred_contents(context, &creds);
if (is_tgt)
cred->have_tgt = TRUE;
if (is_tgt || cred->expire == 0)
cred->expire = endtime;
}
krb5_cc_end_seq_get(context, ccache, &cursor);
if (code && code != KRB5_CC_END)
goto cleanup;
code = 0;
if (cred->expire == 0 && !can_get_initial_creds(context, cred)) {
code = KG_EMPTY_CCACHE;
goto cleanup;
}
cleanup:
(void)krb5_cc_set_flags(context, ccache, 0);
krb5_free_principal(context, ccache_princ);
krb5_free_principal(context, tgt_princ);
return code;
}
static krb5_error_code
get_cache_for_name(krb5_context context, krb5_gss_cred_id_rec *cred)
{
krb5_error_code code;
krb5_boolean can_get, have_collection;
krb5_ccache defcc = NULL;
krb5_principal princ = NULL;
const char *cctype;
assert(cred->name != NULL && cred->ccache == NULL);
#ifdef USE_LEASH
code = get_ccache_leash(context, cred->name->princ, &cred->ccache);
return code ? code : scan_ccache(context, cred, TRUE);
#else
can_get = can_get_initial_creds(context, cred);
code = krb5_cc_cache_match(context, cred->name->princ, &cred->ccache);
if (code == 0)
return scan_ccache(context, cred, FALSE);
if (code != KRB5_CC_NOTFOUND || !can_get)
return code;
krb5_clear_error_message(context);
code = krb5_cc_default(context, &defcc);
if (code)
return code;
cctype = krb5_cc_get_type(context, defcc);
have_collection = krb5_cc_support_switch(context, cctype);
if (cred->password != NULL || !have_collection) {
if (krb5_cc_get_principal(context, defcc, &princ) == KRB5_FCC_NOFILE) {
cred->ccache = defcc;
defcc = NULL;
}
krb5_clear_error_message(context);
}
if (cred->ccache == NULL) {
if (!have_collection) {
code = KG_CCACHE_NOMATCH;
goto cleanup;
}
code = krb5_cc_new_unique(context, cctype, NULL, &cred->ccache);
if (code)
goto cleanup;
}
cleanup:
krb5_free_principal(context, princ);
if (defcc != NULL)
krb5_cc_close(context, defcc);
return code;
#endif
}
static krb5_error_code
get_name_from_client_keytab(krb5_context context, krb5_gss_cred_id_rec *cred)
{
krb5_error_code code;
krb5_principal princ;
assert(cred->name == NULL);
if (cred->client_keytab == NULL)
return KRB5_KT_NOTFOUND;
code = k5_kt_get_principal(context, cred->client_keytab, &princ);
if (code)
return code;
code = kg_init_name(context, princ, NULL, NULL, NULL, KG_INIT_NAME_NO_COPY,
&cred->name);
if (code) {
krb5_free_principal(context, princ);
return code;
}
return 0;
}
static void
set_refresh_time(krb5_context context, krb5_ccache ccache,
krb5_timestamp refresh_time)
{
char buf[128];
krb5_data d;
snprintf(buf, sizeof(buf), "%u", (unsigned int)ts2tt(refresh_time));
d = string2data(buf);
(void)krb5_cc_set_config(context, ccache, NULL, KRB5_CC_CONF_REFRESH_TIME,
&d);
krb5_clear_error_message(context);
}
krb5_boolean
kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred)
{
krb5_timestamp now, soon;
if (krb5_timeofday(context, &now))
return FALSE;
soon = ts_incr(now, 30);
if (cred->refresh_time != 0 && !ts_after(cred->refresh_time, now)) {
set_refresh_time(context, cred->ccache, soon);
return TRUE;
}
if (ts_after(soon, cred->expire))
return TRUE;
return FALSE;
}
void
kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred,
krb5_ticket_times *times)
{
krb5_timestamp refresh;
if (cred->password != NULL)
return;
refresh = ts_incr(times->starttime,
ts_delta(times->endtime, times->starttime) / 2);
set_refresh_time(context, cred->ccache, refresh);
}
struct verify_params {
krb5_principal princ;
krb5_keytab keytab;
};
static krb5_error_code
verify_initial_cred(krb5_context context, krb5_creds *creds,
const struct verify_params *verify)
{
krb5_verify_init_creds_opt vopts;
krb5_verify_init_creds_opt_init(&vopts);
krb5_verify_init_creds_opt_set_ap_req_nofail(&vopts, TRUE);
return krb5_verify_init_creds(context, creds, verify->princ,
verify->keytab, NULL, &vopts);
}
static krb5_error_code
get_initial_cred(krb5_context context, const struct verify_params *verify,
krb5_gss_cred_id_rec *cred)
{
krb5_error_code code;
krb5_get_init_creds_opt *opt = NULL;
krb5_creds creds;
code = krb5_get_init_creds_opt_alloc(context, &opt);
if (code)
return code;
code = krb5_get_init_creds_opt_set_out_ccache(context, opt, cred->ccache);
if (code)
goto cleanup;
if (cred->password != NULL) {
code = krb5_get_init_creds_password(context, &creds, cred->name->princ,
cred->password, NULL, NULL, 0,
NULL, opt);
} else if (cred->client_keytab != NULL) {
code = krb5_get_init_creds_keytab(context, &creds, cred->name->princ,
cred->client_keytab, 0, NULL, opt);
} else {
code = KRB5_KT_NOTFOUND;
}
if (code)
goto cleanup;
if (cred->password != NULL && verify != NULL) {
code = verify_initial_cred(context, &creds, verify);
if (code)
goto cleanup;
}
kg_cred_set_initial_refresh(context, cred, &creds.times);
cred->have_tgt = TRUE;
cred->expire = creds.times.endtime;
krb5_free_principal(context, cred->name->princ);
cred->name->princ = creds.client;
creds.client = NULL;
krb5_free_cred_contents(context, &creds);
cleanup:
krb5_get_init_creds_opt_free(context, opt);
return code;
}
static krb5_error_code
maybe_get_initial_cred(krb5_context context,
const struct verify_params *verify,
krb5_gss_cred_id_rec *cred)
{
krb5_error_code code;
if (cred->name == NULL || cred->iakerb_mech)
return 0;
if (cred->expire == 0 || kg_cred_time_to_refresh(context, cred)) {
code = get_initial_cred(context, verify, cred);
if (code && cred->expire == 0)
return code;
krb5_clear_error_message(context);
}
return 0;
}
static OM_uint32
acquire_init_cred(krb5_context context, OM_uint32 *minor_status,
krb5_ccache req_ccache, gss_buffer_t password,
krb5_keytab client_keytab,
const struct verify_params *verify,
krb5_gss_cred_id_rec *cred)
{
krb5_error_code code;
krb5_data pwdata, pwcopy;
int caller_ccname = 0;
if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
return GSS_S_FAILURE;
if (GSS_ERROR(kg_caller_provided_ccache_name(minor_status,
&caller_ccname)))
return GSS_S_FAILURE;
if (password != GSS_C_NO_BUFFER) {
pwdata = make_data(password->value, password->length);
code = krb5int_copy_data_contents_add0(context, &pwdata, &pwcopy);
if (code)
goto error;
cred->password = pwcopy.data;
assert(req_ccache == NULL);
code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache);
if (code)
goto error;
cred->destroy_ccache = 1;
} else if (req_ccache != NULL) {
code = krb5_cc_dup(context, req_ccache, &cred->ccache);
if (code)
goto error;
} else if (caller_ccname) {
code = krb5int_cc_default(context, &cred->ccache);
if (code)
goto error;
}
if (client_keytab != NULL) {
code = krb5_kt_dup(context, client_keytab, &cred->client_keytab);
} else {
code = krb5_kt_client_default(context, &cred->client_keytab);
if (code) {
TRACE_GSS_CLIENT_KEYTAB_FAIL(context, code);
krb5_clear_error_message(context);
code = 0;
}
}
if (code)
goto error;
if (cred->ccache != NULL) {
code = scan_ccache(context, cred, TRUE);
if (code == KRB5_FCC_NOFILE) {
if (cred->name == NULL) {
if (!get_name_from_client_keytab(context, cred))
code = 0;
} else if (can_get_initial_creds(context, cred)) {
code = 0;
}
}
if (code)
goto error;
} else if (cred->name != NULL) {
code = get_cache_for_name(context, cred);
if (code)
goto error;
}
#ifndef USE_LEASH
if (cred->name == NULL && !can_get_initial_creds(context, cred)) {
code = krb5_cccol_have_content(context);
if (code)
goto error;
}
#endif
code = maybe_get_initial_cred(context, verify, cred);
if (code)
goto error;
*minor_status = 0;
return GSS_S_COMPLETE;
error:
*minor_status = code;
return GSS_S_NO_CRED;
}
static OM_uint32
acquire_cred_context(krb5_context context, OM_uint32 *minor_status,
gss_name_t desired_name, gss_buffer_t password,
OM_uint32 time_req, gss_cred_usage_t cred_usage,
krb5_ccache ccache, krb5_keytab client_keytab,
krb5_keytab keytab, const char *rcname,
const struct verify_params *verify,
krb5_boolean iakerb, gss_cred_id_t *output_cred_handle,
OM_uint32 *time_rec)
{
krb5_gss_cred_id_t cred = NULL;
krb5_gss_name_t name = (krb5_gss_name_t)desired_name;
OM_uint32 ret;
krb5_error_code code = 0;
*output_cred_handle = GSS_C_NO_CREDENTIAL;
if (time_rec)
*time_rec = 0;
cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code);
if (cred == NULL)
goto krb_error_out;
cred->usage = cred_usage;
cred->name = NULL;
cred->impersonator = NULL;
cred->iakerb_mech = iakerb;
cred->default_identity = (name == NULL);
#ifndef LEAN_CLIENT
cred->keytab = NULL;
#endif
cred->destroy_ccache = 0;
cred->suppress_ci_flags = 0;
cred->ccache = NULL;
code = k5_mutex_init(&cred->lock);
if (code)
goto krb_error_out;
switch (cred_usage) {
case GSS_C_INITIATE:
case GSS_C_ACCEPT:
case GSS_C_BOTH:
break;
default:
ret = GSS_S_FAILURE;
*minor_status = (OM_uint32) G_BAD_USAGE;
goto error_out;
}
if (name != NULL) {
code = kg_duplicate_name(context, name, &cred->name);
if (code)
goto krb_error_out;
}
#ifndef LEAN_CLIENT
if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
ret = acquire_accept_cred(context, minor_status, keytab, rcname, cred);
if (ret != GSS_S_COMPLETE)
goto error_out;
}
#endif
if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
ret = acquire_init_cred(context, minor_status, ccache, password,
client_keytab, verify, cred);
if (ret != GSS_S_COMPLETE)
goto error_out;
}
assert(cred->default_identity || cred->name != NULL);
if (cred_usage == GSS_C_ACCEPT) {
if (time_rec)
*time_rec = GSS_C_INDEFINITE;
} else {
krb5_timestamp now;
code = krb5_timeofday(context, &now);
if (code != 0)
goto krb_error_out;
if (time_rec) {
ret = kg_cred_resolve(minor_status, context, (gss_cred_id_t)cred,
GSS_C_NO_NAME);
if (GSS_ERROR(ret))
goto error_out;
*time_rec = ts_interval(now, cred->expire);
k5_mutex_unlock(&cred->lock);
}
}
*minor_status = 0;
*output_cred_handle = (gss_cred_id_t) cred;
return GSS_S_COMPLETE;
krb_error_out:
*minor_status = code;
ret = GSS_S_FAILURE;
error_out:
if (cred != NULL) {
if (cred->ccache) {
if (cred->destroy_ccache)
krb5_cc_destroy(context, cred->ccache);
else
krb5_cc_close(context, cred->ccache);
}
if (cred->client_keytab)
krb5_kt_close(context, cred->client_keytab);
#ifndef LEAN_CLIENT
if (cred->keytab)
krb5_kt_close(context, cred->keytab);
#endif
if (cred->rcache)
k5_rc_close(context, cred->rcache);
if (cred->name)
kg_release_name(context, &cred->name);
krb5_free_principal(context, cred->impersonator);
zapfreestr(cred->password);
k5_mutex_destroy(&cred->lock);
xfree(cred);
}
save_error_info(*minor_status, context);
return ret;
}
static OM_uint32
acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
gss_buffer_t password, OM_uint32 time_req,
gss_cred_usage_t cred_usage, krb5_ccache ccache,
krb5_keytab keytab, krb5_boolean iakerb,
gss_cred_id_t *output_cred_handle, OM_uint32 *time_rec)
{
krb5_context context = NULL;
krb5_error_code code = 0;
OM_uint32 ret;
code = gss_krb5int_initialize_library();
if (code) {
*minor_status = code;
ret = GSS_S_FAILURE;
goto out;
}
code = krb5_gss_init_context(&context);
if (code) {
*minor_status = code;
ret = GSS_S_FAILURE;
goto out;
}
ret = acquire_cred_context(context, minor_status, desired_name, password,
time_req, cred_usage, ccache, NULL, keytab,
NULL, NULL, iakerb, output_cred_handle,
time_rec);
out:
krb5_free_context(context);
return ret;
}
OM_uint32
kg_cred_resolve(OM_uint32 *minor_status, krb5_context context,
gss_cred_id_t cred_handle, gss_name_t target_name)
{
OM_uint32 maj;
krb5_error_code code;
krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)cred_handle;
krb5_gss_name_t tname = (krb5_gss_name_t)target_name;
krb5_principal client_princ;
*minor_status = 0;
maj = krb5_gss_validate_cred_1(minor_status, cred_handle, context);
if (maj != 0)
return maj;
k5_mutex_assert_locked(&cred->lock);
if (cred->usage == GSS_C_ACCEPT || cred->name != NULL)
return GSS_S_COMPLETE;
assert(cred->ccache == NULL);
if (tname != NULL) {
code = krb5_cc_select(context, tname->princ, &cred->ccache,
&client_princ);
if (code && code != KRB5_CC_NOTFOUND)
goto kerr;
if (client_princ != NULL) {
code = kg_init_name(context, client_princ, NULL, NULL, NULL,
KG_INIT_NAME_NO_COPY, &cred->name);
if (code) {
krb5_free_principal(context, client_princ);
goto kerr;
}
}
if (cred->ccache != NULL) {
code = scan_ccache(context, cred, FALSE);
if (code)
goto kerr;
}
}
if (cred->name == NULL) {
code = krb5int_cc_default(context, &cred->ccache);
if (code)
goto kerr;
code = scan_ccache(context, cred, FALSE);
if (code == KRB5_FCC_NOFILE) {
krb5_cc_close(context, cred->ccache);
cred->ccache = NULL;
} else if (code) {
goto kerr;
}
}
if (cred->name == NULL) {
code = get_name_from_client_keytab(context, cred);
if (code) {
code = KG_EMPTY_CCACHE;
goto kerr;
}
}
if (cred->name != NULL && cred->ccache == NULL) {
code = get_cache_for_name(context, cred);
if (code)
goto kerr;
}
code = maybe_get_initial_cred(context, NULL, cred);
if (code)
goto kerr;
return GSS_S_COMPLETE;
kerr:
k5_mutex_unlock(&cred->lock);
save_error_info(code, context);
*minor_status = code;
return GSS_S_NO_CRED;
}
OM_uint32
gss_krb5int_set_cred_rcache(OM_uint32 *minor_status,
gss_cred_id_t *cred_handle,
const gss_OID desired_oid,
const gss_buffer_t value)
{
krb5_gss_cred_id_t cred;
krb5_error_code code;
krb5_context context;
krb5_rcache rcache;
assert(value->length == sizeof(rcache));
if (value->length != sizeof(rcache))
return GSS_S_FAILURE;
rcache = (krb5_rcache)value->value;
cred = (krb5_gss_cred_id_t)*cred_handle;
code = krb5_gss_init_context(&context);
if (code) {
*minor_status = code;
return GSS_S_FAILURE;
}
if (cred->rcache != NULL)
k5_rc_close(context, cred->rcache);
cred->rcache = rcache;
krb5_free_context(context);
*minor_status = 0;
return GSS_S_COMPLETE;
}
OM_uint32 KRB5_CALLCONV
krb5_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
OM_uint32 time_req, gss_OID_set desired_mechs,
gss_cred_usage_t cred_usage,
gss_cred_id_t *output_cred_handle,
gss_OID_set *actual_mechs, OM_uint32 *time_rec)
{
return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage,
NULL, NULL, FALSE, output_cred_handle, time_rec);
}
OM_uint32 KRB5_CALLCONV
iakerb_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
OM_uint32 time_req, gss_OID_set desired_mechs,
gss_cred_usage_t cred_usage,
gss_cred_id_t *output_cred_handle,
gss_OID_set *actual_mechs, OM_uint32 *time_rec)
{
return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage,
NULL, NULL, TRUE, output_cred_handle, time_rec);
}
OM_uint32 KRB5_CALLCONV
krb5_gss_acquire_cred_with_password(OM_uint32 *minor_status,
const gss_name_t desired_name,
const gss_buffer_t password,
OM_uint32 time_req,
const gss_OID_set desired_mechs,
int cred_usage,
gss_cred_id_t *output_cred_handle,
gss_OID_set *actual_mechs,
OM_uint32 *time_rec)
{
return acquire_cred(minor_status, desired_name, password, time_req,
cred_usage, NULL, NULL, FALSE, output_cred_handle,
time_rec);
}
OM_uint32 KRB5_CALLCONV
iakerb_gss_acquire_cred_with_password(OM_uint32 *minor_status,
const gss_name_t desired_name,
const gss_buffer_t password,
OM_uint32 time_req,
const gss_OID_set desired_mechs,
int cred_usage,
gss_cred_id_t *output_cred_handle,
gss_OID_set *actual_mechs,
OM_uint32 *time_rec)
{
return acquire_cred(minor_status, desired_name, password, time_req,
cred_usage, NULL, NULL, TRUE, output_cred_handle,
time_rec);
}
OM_uint32
gss_krb5int_import_cred(OM_uint32 *minor_status,
gss_cred_id_t *cred_handle,
const gss_OID desired_oid,
const gss_buffer_t value)
{
struct krb5_gss_import_cred_req *req;
krb5_gss_name_rec name;
OM_uint32 time_rec;
krb5_error_code code;
gss_cred_usage_t usage;
gss_name_t desired_name = GSS_C_NO_NAME;
assert(value->length == sizeof(*req));
if (value->length != sizeof(*req))
return GSS_S_FAILURE;
req = (struct krb5_gss_import_cred_req *)value->value;
if (req->id != NULL) {
usage = (req->keytab != NULL) ? GSS_C_BOTH : GSS_C_INITIATE;
} else if (req->keytab != NULL) {
usage = GSS_C_ACCEPT;
} else {
*minor_status = EINVAL;
return GSS_S_FAILURE;
}
if (req->keytab_principal != NULL) {
memset(&name, 0, sizeof(name));
code = k5_mutex_init(&name.lock);
if (code != 0) {
*minor_status = code;
return GSS_S_FAILURE;
}
name.princ = req->keytab_principal;
desired_name = (gss_name_t)&name;
}
code = acquire_cred(minor_status, desired_name, NULL, GSS_C_INDEFINITE,
usage, req->id, req->keytab, FALSE, cred_handle,
&time_rec);
if (req->keytab_principal != NULL)
k5_mutex_destroy(&name.lock);
return code;
}
static OM_uint32
acquire_cred_from(OM_uint32 *minor_status, const gss_name_t desired_name,
OM_uint32 time_req, const gss_OID_set desired_mechs,
gss_cred_usage_t cred_usage,
gss_const_key_value_set_t cred_store, krb5_boolean iakerb,
gss_cred_id_t *output_cred_handle,
gss_OID_set *actual_mechs, OM_uint32 *time_rec)
{
krb5_context context = NULL;
krb5_error_code code = 0;
krb5_keytab client_keytab = NULL;
krb5_keytab keytab = NULL;
krb5_ccache ccache = NULL;
krb5_principal verify_princ = NULL;
const char *rcname, *value;
struct verify_params vparams = { NULL };
const struct verify_params *verify = NULL;
gss_buffer_desc pwbuf;
gss_buffer_t password = NULL;
OM_uint32 ret;
code = gss_krb5int_initialize_library();
if (code) {
*minor_status = code;
ret = GSS_S_FAILURE;
goto out;
}
code = krb5_gss_init_context(&context);
if (code) {
*minor_status = code;
ret = GSS_S_FAILURE;
goto out;
}
ret = kg_value_from_cred_store(cred_store, KRB5_CS_CCACHE_URN, &value);
if (GSS_ERROR(ret))
goto out;
if (value) {
code = krb5_cc_resolve(context, value, &ccache);
if (code != 0) {
*minor_status = code;
ret = GSS_S_NO_CRED;
goto out;
}
}
ret = kg_value_from_cred_store(cred_store, KRB5_CS_CLI_KEYTAB_URN, &value);
if (GSS_ERROR(ret))
goto out;
if (value) {
code = krb5_kt_resolve(context, value, &client_keytab);
if (code != 0) {
*minor_status = code;
ret = GSS_S_NO_CRED;
goto out;
}
}
ret = kg_value_from_cred_store(cred_store, KRB5_CS_KEYTAB_URN, &value);
if (GSS_ERROR(ret))
goto out;
if (value) {
code = krb5_kt_resolve(context, value, &keytab);
if (code != 0) {
*minor_status = code;
ret = GSS_S_NO_CRED;
goto out;
}
}
ret = kg_value_from_cred_store(cred_store, KRB5_CS_RCACHE_URN, &rcname);
if (GSS_ERROR(ret))
goto out;
ret = kg_value_from_cred_store(cred_store, KRB5_CS_PASSWORD_URN, &value);
if (GSS_ERROR(ret))
goto out;
if (value) {
if (desired_name == GSS_C_NO_NAME) {
ret = GSS_S_BAD_NAME;
goto out;
}
if (cred_usage == GSS_C_ACCEPT || ccache != NULL ||
client_keytab != NULL) {
*minor_status = (OM_uint32)G_BAD_USAGE;
ret = GSS_S_FAILURE;
goto out;
}
pwbuf.length = strlen(value);
pwbuf.value = (void *)value;
password = &pwbuf;
}
ret = kg_value_from_cred_store(cred_store, KRB5_CS_VERIFY_URN, &value);
if (GSS_ERROR(ret))
goto out;
if (value != NULL) {
if (iakerb || password == NULL) {
*minor_status = G_BAD_USAGE;
ret = GSS_S_FAILURE;
goto out;
}
if (*value != '\0') {
code = krb5_parse_name(context, value, &verify_princ);
if (code != 0) {
*minor_status = code;
ret = GSS_S_FAILURE;
goto out;
}
}
vparams.princ = verify_princ;
vparams.keytab = keytab;
verify = &vparams;
}
ret = acquire_cred_context(context, minor_status, desired_name, password,
time_req, cred_usage, ccache, client_keytab,
keytab, rcname, verify, iakerb,
output_cred_handle, time_rec);
out:
if (ccache != NULL)
krb5_cc_close(context, ccache);
if (client_keytab != NULL)
krb5_kt_close(context, client_keytab);
if (keytab != NULL)
krb5_kt_close(context, keytab);
krb5_free_principal(context, verify_princ);
krb5_free_context(context);
return ret;
}
OM_uint32 KRB5_CALLCONV
krb5_gss_acquire_cred_from(OM_uint32 *minor_status,
const gss_name_t desired_name,
OM_uint32 time_req,
const gss_OID_set desired_mechs,
gss_cred_usage_t cred_usage,
gss_const_key_value_set_t cred_store,
gss_cred_id_t *output_cred_handle,
gss_OID_set *actual_mechs,
OM_uint32 *time_rec)
{
return acquire_cred_from(minor_status, desired_name, time_req,
desired_mechs, cred_usage, cred_store,
FALSE, output_cred_handle, actual_mechs,
time_rec);
}
OM_uint32 KRB5_CALLCONV
iakerb_gss_acquire_cred_from(OM_uint32 *minor_status,
const gss_name_t desired_name,
OM_uint32 time_req,
const gss_OID_set desired_mechs,
gss_cred_usage_t cred_usage,
gss_const_key_value_set_t cred_store,
gss_cred_id_t *output_cred_handle,
gss_OID_set *actual_mechs,
OM_uint32 *time_rec)
{
return acquire_cred_from(minor_status, desired_name, time_req,
desired_mechs, cred_usage, cred_store,
TRUE, output_cred_handle, actual_mechs,
time_rec);
}