/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */
#include "gss_plugin.h"
OM_uint32 gssi_accept_sec_context(OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_cred_id_t acceptor_cred_handle,
gss_buffer_t input_token_buffer,
gss_channel_bindings_t input_chan_bindings,
gss_name_t *src_name,
gss_OID *mech_type,
gss_buffer_t output_token,
OM_uint32 *ret_flags,
OM_uint32 *time_rec,
gss_cred_id_t *delegated_cred_handle)
{
enum gpp_behavior behavior;
struct gpp_context_handle *ctx_handle = NULL;
struct gpp_cred_handle *cred_handle = NULL;
struct gpp_cred_handle *deleg_cred = NULL;
struct gpp_name_handle *name = NULL;
OM_uint32 maj, min;
GSSI_TRACE();
behavior = gpp_get_behavior();
if (*context_handle) {
ctx_handle = (struct gpp_context_handle *)*context_handle;
if (ctx_handle->local) {
/* if we already have a local context it means this is
* a continuation, force local only behavior, nothing else
* makes sense */
behavior = GPP_LOCAL_ONLY;
} else if (ctx_handle->remote) {
behavior = GPP_REMOTE_ONLY;
}
} else {
ctx_handle = calloc(1, sizeof(struct gpp_context_handle));
if (!ctx_handle) {
maj = GSS_S_FAILURE;
min = ENOMEM;
goto done;
}
}
if (acceptor_cred_handle != GSS_C_NO_CREDENTIAL) {
cred_handle = (struct gpp_cred_handle *)acceptor_cred_handle;
} else {
maj = gppint_get_def_creds(&min, behavior, NULL,
GSS_C_ACCEPT, &cred_handle);
if (maj != GSS_S_COMPLETE) {
goto done;
}
}
if (cred_handle->local) {
if (behavior == GPP_REMOTE_ONLY) {
min = 0;
maj = GSS_S_DEFECTIVE_CREDENTIAL;
goto done;
}
behavior = GPP_LOCAL_ONLY;
} else if (cred_handle->remote) {
if (behavior == GPP_LOCAL_ONLY) {
min = 0;
maj = GSS_S_DEFECTIVE_CREDENTIAL;
goto done;
}
behavior = GPP_REMOTE_ONLY;
}
if (src_name) {
name = calloc(1, sizeof(struct gpp_name_handle));
if (!name) {
maj = GSS_S_FAILURE;
min = ENOMEM;
goto done;
}
}
if (delegated_cred_handle) {
maj = gpp_cred_handle_init(&min, false, NULL, &deleg_cred);
if (maj != GSS_S_COMPLETE) {
goto done;
}
}
/* behavior has been set to local only or remote only by context or
* credential handler inspection, so we only have those 2 cases,
* anything else is an error at this point. */
if (behavior == GPP_LOCAL_ONLY) {
maj = gss_accept_sec_context(&min, &ctx_handle->local,
cred_handle->local, input_token_buffer,
input_chan_bindings,
name ? &name->local : NULL, mech_type,
output_token, ret_flags, time_rec,
deleg_cred ? &deleg_cred->local : NULL);
} else if (behavior == GPP_REMOTE_ONLY) {
maj = gpm_accept_sec_context(&min, &ctx_handle->remote,
cred_handle->remote, input_token_buffer,
input_chan_bindings,
name ? &name->remote : NULL, mech_type,
output_token, ret_flags, time_rec,
deleg_cred ? &deleg_cred->remote : NULL);
} else {
min = 0;
maj = GSS_S_FAILURE;
}
done:
*minor_status = gpp_map_error(min);
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;
}
free(deleg_cred);
free(name);
} else {
if (src_name) {
*src_name = (gss_name_t)name;
}
if (delegated_cred_handle) {
*delegated_cred_handle = (gss_cred_id_t)deleg_cred;
}
}
/* 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 (acceptor_cred_handle == GSS_C_NO_CREDENTIAL) {
(void)gssi_release_cred(&min, (gss_cred_id_t *)&cred_handle);
}
return maj;
}