#include "k5-int.h"
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_ext.h>
#include <gssapi/gssapi_alloc.h>
struct test_context {
int initiator;
uint8_t hops;
};
OM_uint32 KRB5_CALLCONV
gss_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_chan_bindings,
gss_buffer_t input_token, gss_OID *actual_mech,
gss_buffer_t output_token, OM_uint32 *ret_flags,
OM_uint32 *time_rec)
{
struct test_context *ctx = (struct test_context *)*context_handle;
OM_uint32 major;
gss_buffer_desc tok;
const char *envstr;
uint8_t hops, mech_last_octet;
envstr = getenv("GSS_INIT_BINDING");
if (envstr != NULL) {
assert(strlen(envstr) > 0);
assert(input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS);
assert(strlen(envstr) == input_chan_bindings->application_data.length);
assert(strcmp((char *)input_chan_bindings->application_data.value,
envstr) == 0);
}
if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
envstr = getenv("HOPS");
hops = (envstr != NULL) ? atoi(envstr) : 1;
assert(hops > 0);
} else if (input_token->length == 4 &&
memcmp(input_token->value, "fail", 4) == 0) {
*minor_status = 12345;
return GSS_S_FAILURE;
} else {
hops = ((uint8_t *)input_token->value)[0];
}
mech_last_octet = ((uint8_t *)mech_type->elements)[mech_type->length - 1];
envstr = getenv("INIT_FAIL");
if (envstr != NULL && atoi(envstr) == mech_last_octet)
return GSS_S_FAILURE;
if (ctx == NULL) {
ctx = malloc(sizeof(*ctx));
assert(ctx != NULL);
ctx->initiator = 1;
ctx->hops = hops;
*context_handle = (gss_ctx_id_t)ctx;
} else if (ctx != NULL) {
assert(ctx->initiator);
ctx->hops--;
assert(ctx->hops == hops);
}
if (ctx->hops > 0) {
ctx->hops--;
tok.value = &ctx->hops;
tok.length = 1;
major = gss_encapsulate_token(&tok, mech_type, output_token);
assert(major == GSS_S_COMPLETE);
}
return (ctx->hops > 0) ? GSS_S_CONTINUE_NEEDED : GSS_S_COMPLETE;
}
OM_uint32 KRB5_CALLCONV
gss_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
gss_cred_id_t verifier_cred_handle,
gss_buffer_t input_token,
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)
{
struct test_context *ctx = (struct test_context *)*context_handle;
uint8_t hops, mech_last_octet;
const char *envstr;
envstr = getenv("GSS_ACCEPT_BINDING");
if (envstr != NULL) {
assert(strlen(envstr) > 0);
assert(input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS);
assert(strlen(envstr) == input_chan_bindings->application_data.length);
assert(strcmp((char *)input_chan_bindings->application_data.value,
envstr) == 0);
}
assert(input_token->length >= 2);
hops = ((uint8_t *)input_token->value)[input_token->length - 1];
mech_last_octet = ((uint8_t *)input_token->value)[input_token->length - 2];
envstr = getenv("ACCEPT_FAIL");
if (envstr != NULL && atoi(envstr) == mech_last_octet) {
output_token->value = gssalloc_strdup("fail");
assert(output_token->value != NULL);
output_token->length = 4;
return GSS_S_FAILURE;
}
if (*context_handle == GSS_C_NO_CONTEXT) {
ctx = malloc(sizeof(*ctx));
assert(ctx != NULL);
ctx->initiator = 0;
ctx->hops = hops;
*context_handle = (gss_ctx_id_t)ctx;
} else {
assert(!ctx->initiator);
ctx->hops--;
assert(ctx->hops == hops);
}
if (ctx->hops > 0) {
ctx->hops--;
output_token->value = gssalloc_malloc(1);
assert(output_token->value != NULL);
memcpy(output_token->value, &ctx->hops, 1);
output_token->length = 1;
}
return (ctx->hops > 0) ? GSS_S_CONTINUE_NEEDED : GSS_S_COMPLETE;
}
OM_uint32 KRB5_CALLCONV
gss_delete_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
gss_buffer_t output_token)
{
free(*context_handle);
*context_handle = GSS_C_NO_CONTEXT;
return GSS_S_COMPLETE;
}
OM_uint32 KRB5_CALLCONV
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 GSS_S_COMPLETE;
}
OM_uint32 KRB5_CALLCONV
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,
gss_cred_usage_t cred_usage,
gss_cred_id_t *output_cred_handle,
gss_OID_set *actual_mechs, OM_uint32 *time_rec)
{
return GSS_S_COMPLETE;
}
OM_uint32 KRB5_CALLCONV
gss_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle)
{
return GSS_S_COMPLETE;
}
OM_uint32 KRB5_CALLCONV
gss_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer,
gss_OID input_name_type, gss_name_t *output_name)
{
static int dummy;
*output_name = (gss_name_t)&dummy;
return GSS_S_COMPLETE;
}
OM_uint32 KRB5_CALLCONV
gss_release_name(OM_uint32 *minor_status, gss_name_t *input_name)
{
return GSS_S_COMPLETE;
}
OM_uint32 KRB5_CALLCONV
gss_display_status(OM_uint32 *minor_status, OM_uint32 status_value,
int status_type, gss_OID mech_type,
OM_uint32 *message_context, gss_buffer_t status_string)
{
if (status_type == GSS_C_MECH_CODE && status_value == 12345) {
status_string->value = gssalloc_strdup("failure from acceptor");
assert(status_string->value != NULL);
status_string->length = strlen(status_string->value);
return GSS_S_COMPLETE;
}
return GSS_S_BAD_STATUS;
}
OM_uint32 KRB5_CALLCONV
gssspi_query_meta_data(OM_uint32 *minor_status, gss_const_OID mech_oid,
gss_cred_id_t cred_handle, gss_ctx_id_t *context_handle,
const gss_name_t targ_name, OM_uint32 req_flags,
gss_buffer_t meta_data)
{
const char *envstr;
uint8_t mech_last_octet;
int initiator = (targ_name != GSS_C_NO_NAME);
mech_last_octet = ((uint8_t *)mech_oid->elements)[mech_oid->length - 1];
envstr = getenv(initiator ? "INIT_QUERY_FAIL" : "ACCEPT_QUERY_FAIL");
if (envstr != NULL && atoi(envstr) == mech_last_octet)
return GSS_S_FAILURE;
envstr = getenv(initiator ? "INIT_QUERY_NONE" : "ACCEPT_QUERY_NONE");
if (envstr != NULL && atoi(envstr) == mech_last_octet)
return GSS_S_COMPLETE;
meta_data->value = gssalloc_strdup("X");
meta_data->length = 1;
return GSS_S_COMPLETE;
}
OM_uint32 KRB5_CALLCONV
gssspi_exchange_meta_data(OM_uint32 *minor_status, gss_const_OID mech_oid,
gss_cred_id_t cred_handle,
gss_ctx_id_t *context_handle,
const gss_name_t targ_name, OM_uint32 req_flags,
gss_const_buffer_t meta_data)
{
const char *envstr;
uint8_t mech_last_octet;
int initiator = (targ_name != GSS_C_NO_NAME);
mech_last_octet = ((uint8_t *)mech_oid->elements)[mech_oid->length - 1];
envstr = getenv(initiator ? "INIT_EXCHANGE_FAIL" : "ACCEPT_EXCHANGE_FAIL");
if (envstr != NULL && atoi(envstr) == mech_last_octet)
return GSS_S_FAILURE;
assert(meta_data->length == 1 && memcmp(meta_data->value, "X", 1) == 0);
return GSS_S_COMPLETE;
}
OM_uint32 KRB5_CALLCONV
gssspi_query_mechanism_info(OM_uint32 *minor_status, gss_const_OID mech_oid,
unsigned char auth_scheme[16])
{
memset(auth_scheme, 0, 16);
assert(mech_oid->length <= 16);
memcpy(auth_scheme, mech_oid->elements, mech_oid->length);
return GSS_S_COMPLETE;
}
OM_uint32 KRB5_CALLCONV
gss_inquire_sec_context_by_oid(OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set)
{
struct test_context *ctx = (struct test_context *)context_handle;
OM_uint32 major;
uint8_t keybytes[32] = { 0 };
uint8_t typebytes[4];
gss_buffer_desc key, type;
const char *envstr;
int ask_verify;
if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_KEY))
ask_verify = 0;
else if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_VERIFY_KEY))
ask_verify = 1;
else
return GSS_S_UNAVAILABLE;
envstr = getenv("KEY");
if (envstr != NULL && strcmp(envstr, "never") == 0) {
return GSS_S_UNAVAILABLE;
} else if (ctx->hops > 0) {
if (envstr == NULL)
return GSS_S_UNAVAILABLE;
else if (strcmp(envstr, "init-always") == 0 && !ctx->initiator)
return GSS_S_UNAVAILABLE;
else if (strcmp(envstr, "accept-always") == 0 && ctx->initiator)
return GSS_S_UNAVAILABLE;
}
keybytes[0] = ask_verify ^ ctx->initiator;
if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_KEY) ||
gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_VERIFY_KEY)) {
store_32_le(ENCTYPE_AES256_CTS_HMAC_SHA1_96, typebytes);
key.value = keybytes;
key.length = sizeof(keybytes);
type.value = typebytes;
type.length = sizeof(typebytes);
major = gss_add_buffer_set_member(minor_status, &key, data_set);
if (major != GSS_S_COMPLETE)
return major;
return gss_add_buffer_set_member(minor_status, &type, data_set);
}
return GSS_S_UNAVAILABLE;
}