/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */
#include "gss_plugin.h"
static OM_uint32 init_ctx_local(OM_uint32 *minor_status,
struct gpp_cred_handle *cred_handle,
struct gpp_context_handle *ctx_handle,
struct gpp_name_handle *name,
gss_OID mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
gss_channel_bindings_t input_cb,
gss_buffer_t input_token,
gss_OID *actual_mech_type,
gss_buffer_t output_token,
OM_uint32 *ret_flags,
OM_uint32 *time_rec)
{
OM_uint32 maj, min;
if (name->remote && !name->local) {
maj = gpp_name_to_local(&min, name->remote,
mech_type, &name->local);
if (maj) {
goto done;
}
}
maj = gss_init_sec_context(&min,
cred_handle->local,
&ctx_handle->local,
name->local,
gpp_special_mech(mech_type),
req_flags,
time_req,
input_cb,
input_token,
actual_mech_type,
output_token,
ret_flags,
time_rec);
done:
*minor_status = min;
return maj;
}
OM_uint32 gssi_init_sec_context(OM_uint32 *minor_status,
gss_cred_id_t claimant_cred_handle,
gss_ctx_id_t *context_handle,
gss_name_t target_name,
gss_OID mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
gss_channel_bindings_t input_cb,
gss_buffer_t input_token,
gss_OID *actual_mech_type,
gss_buffer_t output_token,
OM_uint32 *ret_flags,
OM_uint32 *time_rec)
{
enum gpp_behavior behavior = GPP_UNINITIALIZED;
struct gpp_context_handle *ctx_handle = NULL;
struct gpp_cred_handle *cred_handle = NULL;
gssx_cred *out_cred = NULL;
struct gpp_name_handle *name;
OM_uint32 tmaj, tmin;
OM_uint32 maj, min;
GSSI_TRACE();
*minor_status = 0;
if (target_name == GSS_C_NO_NAME) {
return GSS_S_CALL_INACCESSIBLE_READ;
}
if (mech_type == GSS_C_NO_OID || gpp_is_special_oid(mech_type)) {
return GSS_S_BAD_MECH;
}
tmaj = GSS_S_COMPLETE;
tmin = 0;
if (*context_handle) {
ctx_handle = (struct gpp_context_handle *)*context_handle;
if (ctx_handle->local) {
/* ok this means a previous call decided to use the local mech,
* so let's just re-enter the mechglue here and keep at it */
behavior = GPP_LOCAL_ONLY;
}
} else {
ctx_handle = calloc(1, sizeof(struct gpp_context_handle));
if (!ctx_handle) {
maj = GSS_S_FAILURE;
min = ENOMEM;
goto done;
}
}
if (claimant_cred_handle != GSS_C_NO_CREDENTIAL) {
cred_handle = (struct gpp_cred_handle *)claimant_cred_handle;
if (cred_handle->local) {
/* ok this means a previous call decided to short circuit to the
* local mech, so let's just re-enter the mechglue here, as we
* have no way to export creds yet. */
behavior = GPP_LOCAL_ONLY;
} else if (behavior == GPP_LOCAL_ONLY) {
maj = GSS_S_DEFECTIVE_CREDENTIAL;
min = 0;
goto done;
}
} else {
maj = gpp_cred_handle_init(&min, true, NULL, &cred_handle);
if (maj) {
goto done;
}
}
name = (struct gpp_name_handle *)target_name;
if (behavior == GPP_UNINITIALIZED) {
behavior = gpp_get_behavior();
}
/* See if we should try local first */
if (behavior == GPP_LOCAL_ONLY || behavior == GPP_LOCAL_FIRST) {
maj = init_ctx_local(&min, cred_handle, ctx_handle, name,
mech_type, req_flags, time_req, input_cb,
input_token, actual_mech_type, output_token,
ret_flags, time_rec);
if (maj == GSS_S_COMPLETE || maj == GSS_S_CONTINUE_NEEDED ||
behavior == GPP_LOCAL_ONLY) {
goto done;
}
/* not successful, save actual local error if remote fallback fails */
tmaj = maj;
tmin = min;
}
/* Then try with remote */
if (behavior != GPP_LOCAL_ONLY) {
if (name->local && !name->remote) {
maj = gpp_local_to_name(&min, name->local, &name->remote);
if (maj) {
goto done;
}
}
if (!cred_handle->remote) {
/* we ignore failures here */
(void)gppint_get_def_creds(&min, GPP_REMOTE_ONLY, NULL,
GSS_C_INITIATE, &cred_handle);
}
maj = gpm_init_sec_context(&min,
cred_handle->remote,
&ctx_handle->remote,
name->remote,
mech_type,
req_flags,
time_req,
input_cb,
input_token,
actual_mech_type,
output_token,
ret_flags,
time_rec,
&out_cred);
if (maj == GSS_S_COMPLETE || maj == GSS_S_CONTINUE_NEEDED) {
if (out_cred) {
xdr_free((xdrproc_t)xdr_gssx_cred,
(char *)cred_handle->remote);
free(cred_handle->remote);
cred_handle->remote = out_cred;
out_cred = NULL;
/* failuire is not fatal */
(void)gpp_store_remote_creds(&tmin,
cred_handle->default_creds,
&cred_handle->store,
cred_handle->remote);
}
goto done;
}
if (behavior == GPP_REMOTE_FIRST) {
/* So remote failed, but we can fallback to local, try that */
maj = init_ctx_local(&min, cred_handle, ctx_handle, name,
mech_type, req_flags, time_req, input_cb,
input_token, actual_mech_type, output_token,
ret_flags, time_rec);
}
}
done:
if (maj != GSS_S_COMPLETE &&
maj != GSS_S_CONTINUE_NEEDED &&
tmaj != GSS_S_COMPLETE) {
maj = tmaj;
min = tmin;
}
if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) {
if (ctx_handle &&
ctx_handle->local == GSS_C_NO_CONTEXT &&
ctx_handle->remote == NULL) {
free(ctx_handle);
ctx_handle = NULL;
}
*minor_status = gpp_map_error(min);
}
/* always replace the provided context handle to avoid
* dangling pointers when a context has been passed in */
*context_handle = (gss_ctx_id_t)ctx_handle;
if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
free(cred_handle);
}
return maj;
}