Path: blob/main/crypto/krb5/src/plugins/gssapi/negoextest/main.c
34907 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* plugins/gssapi/negoextest/main.c - GSS test module for NegoEx */2/*3* Copyright (C) 2019 by the Massachusetts Institute of Technology.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9*10* * Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in15* the documentation and/or other materials provided with the16* distribution.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS19* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT20* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS21* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE22* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,23* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES24* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR25* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,27* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)28* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED29* OF THE POSSIBILITY OF SUCH DAMAGE.30*/3132#include "k5-int.h"33#include <gssapi/gssapi.h>34#include <gssapi/gssapi_ext.h>35#include <gssapi/gssapi_alloc.h>3637struct test_context {38int initiator;39uint8_t hops; /* hops remaining; 0 means established */40};4142OM_uint32 KRB5_CALLCONV43gss_init_sec_context(OM_uint32 *minor_status,44gss_cred_id_t claimant_cred_handle,45gss_ctx_id_t *context_handle, gss_name_t target_name,46gss_OID mech_type, OM_uint32 req_flags,47OM_uint32 time_req,48gss_channel_bindings_t input_chan_bindings,49gss_buffer_t input_token, gss_OID *actual_mech,50gss_buffer_t output_token, OM_uint32 *ret_flags,51OM_uint32 *time_rec)52{53struct test_context *ctx = (struct test_context *)*context_handle;54OM_uint32 major;55gss_buffer_desc tok;56const char *envstr;57uint8_t hops, mech_last_octet;5859envstr = getenv("GSS_INIT_BINDING");60if (envstr != NULL) {61assert(strlen(envstr) > 0);62assert(input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS);63assert(strlen(envstr) == input_chan_bindings->application_data.length);64assert(strcmp((char *)input_chan_bindings->application_data.value,65envstr) == 0);66}6768if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {69envstr = getenv("HOPS");70hops = (envstr != NULL) ? atoi(envstr) : 1;71assert(hops > 0);72} else if (input_token->length == 4 &&73memcmp(input_token->value, "fail", 4) == 0) {74*minor_status = 12345;75return GSS_S_FAILURE;76} else {77hops = ((uint8_t *)input_token->value)[0];78}7980mech_last_octet = ((uint8_t *)mech_type->elements)[mech_type->length - 1];81envstr = getenv("INIT_FAIL");82if (envstr != NULL && atoi(envstr) == mech_last_octet)83return GSS_S_FAILURE;8485if (ctx == NULL) {86ctx = malloc(sizeof(*ctx));87assert(ctx != NULL);88ctx->initiator = 1;89ctx->hops = hops;90*context_handle = (gss_ctx_id_t)ctx;91} else if (ctx != NULL) {92assert(ctx->initiator);93ctx->hops--;94assert(ctx->hops == hops);95}9697if (ctx->hops > 0) {98/* Generate a token containing the remaining hop count. */99ctx->hops--;100tok.value = &ctx->hops;101tok.length = 1;102major = gss_encapsulate_token(&tok, mech_type, output_token);103assert(major == GSS_S_COMPLETE);104}105106return (ctx->hops > 0) ? GSS_S_CONTINUE_NEEDED : GSS_S_COMPLETE;107}108109OM_uint32 KRB5_CALLCONV110gss_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,111gss_cred_id_t verifier_cred_handle,112gss_buffer_t input_token,113gss_channel_bindings_t input_chan_bindings,114gss_name_t *src_name, gss_OID *mech_type,115gss_buffer_t output_token, OM_uint32 *ret_flags,116OM_uint32 *time_rec,117gss_cred_id_t *delegated_cred_handle)118{119struct test_context *ctx = (struct test_context *)*context_handle;120uint8_t hops, mech_last_octet;121const char *envstr;122123envstr = getenv("GSS_ACCEPT_BINDING");124if (envstr != NULL) {125assert(strlen(envstr) > 0);126assert(input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS);127assert(strlen(envstr) == input_chan_bindings->application_data.length);128assert(strcmp((char *)input_chan_bindings->application_data.value,129envstr) == 0);130}131132/*133* The unwrapped token sits at the end and is just one byte giving the134* remaining number of hops. The final octet of the mech encoding should135* be just prior to it.136*/137assert(input_token->length >= 2);138hops = ((uint8_t *)input_token->value)[input_token->length - 1];139mech_last_octet = ((uint8_t *)input_token->value)[input_token->length - 2];140141envstr = getenv("ACCEPT_FAIL");142if (envstr != NULL && atoi(envstr) == mech_last_octet) {143output_token->value = gssalloc_strdup("fail");144assert(output_token->value != NULL);145output_token->length = 4;146return GSS_S_FAILURE;147}148149if (*context_handle == GSS_C_NO_CONTEXT) {150ctx = malloc(sizeof(*ctx));151assert(ctx != NULL);152ctx->initiator = 0;153ctx->hops = hops;154*context_handle = (gss_ctx_id_t)ctx;155} else {156assert(!ctx->initiator);157ctx->hops--;158assert(ctx->hops == hops);159}160161if (ctx->hops > 0) {162/* Generate a token containing the remaining hop count. */163ctx->hops--;164output_token->value = gssalloc_malloc(1);165assert(output_token->value != NULL);166memcpy(output_token->value, &ctx->hops, 1);167output_token->length = 1;168}169170return (ctx->hops > 0) ? GSS_S_CONTINUE_NEEDED : GSS_S_COMPLETE;171}172173OM_uint32 KRB5_CALLCONV174gss_delete_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,175gss_buffer_t output_token)176{177free(*context_handle);178*context_handle = GSS_C_NO_CONTEXT;179return GSS_S_COMPLETE;180}181182OM_uint32 KRB5_CALLCONV183gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,184OM_uint32 time_req, gss_OID_set desired_mechs,185gss_cred_usage_t cred_usage,186gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs,187OM_uint32 *time_rec)188{189return GSS_S_COMPLETE;190}191192OM_uint32 KRB5_CALLCONV193gss_acquire_cred_with_password(OM_uint32 *minor_status,194const gss_name_t desired_name,195const gss_buffer_t password, OM_uint32 time_req,196const gss_OID_set desired_mechs,197gss_cred_usage_t cred_usage,198gss_cred_id_t *output_cred_handle,199gss_OID_set *actual_mechs, OM_uint32 *time_rec)200{201return GSS_S_COMPLETE;202}203204OM_uint32 KRB5_CALLCONV205gss_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle)206{207return GSS_S_COMPLETE;208}209210OM_uint32 KRB5_CALLCONV211gss_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer,212gss_OID input_name_type, gss_name_t *output_name)213{214static int dummy;215216/*217* We don't need to remember anything about names, but we do need to218* distinguish them from GSS_C_NO_NAME (to determine the direction of219* gss_query_meta_data() and gss_exchange_meta_data()), so assign an220* arbitrary data pointer.221*/222*output_name = (gss_name_t)&dummy;223return GSS_S_COMPLETE;224}225226OM_uint32 KRB5_CALLCONV227gss_release_name(OM_uint32 *minor_status, gss_name_t *input_name)228{229return GSS_S_COMPLETE;230}231232OM_uint32 KRB5_CALLCONV233gss_display_status(OM_uint32 *minor_status, OM_uint32 status_value,234int status_type, gss_OID mech_type,235OM_uint32 *message_context, gss_buffer_t status_string)236{237if (status_type == GSS_C_MECH_CODE && status_value == 12345) {238status_string->value = gssalloc_strdup("failure from acceptor");239assert(status_string->value != NULL);240status_string->length = strlen(status_string->value);241return GSS_S_COMPLETE;242}243return GSS_S_BAD_STATUS;244}245246OM_uint32 KRB5_CALLCONV247gssspi_query_meta_data(OM_uint32 *minor_status, gss_const_OID mech_oid,248gss_cred_id_t cred_handle, gss_ctx_id_t *context_handle,249const gss_name_t targ_name, OM_uint32 req_flags,250gss_buffer_t meta_data)251{252const char *envstr;253uint8_t mech_last_octet;254int initiator = (targ_name != GSS_C_NO_NAME);255256mech_last_octet = ((uint8_t *)mech_oid->elements)[mech_oid->length - 1];257envstr = getenv(initiator ? "INIT_QUERY_FAIL" : "ACCEPT_QUERY_FAIL");258if (envstr != NULL && atoi(envstr) == mech_last_octet)259return GSS_S_FAILURE;260envstr = getenv(initiator ? "INIT_QUERY_NONE" : "ACCEPT_QUERY_NONE");261if (envstr != NULL && atoi(envstr) == mech_last_octet)262return GSS_S_COMPLETE;263264meta_data->value = gssalloc_strdup("X");265meta_data->length = 1;266return GSS_S_COMPLETE;267}268269OM_uint32 KRB5_CALLCONV270gssspi_exchange_meta_data(OM_uint32 *minor_status, gss_const_OID mech_oid,271gss_cred_id_t cred_handle,272gss_ctx_id_t *context_handle,273const gss_name_t targ_name, OM_uint32 req_flags,274gss_const_buffer_t meta_data)275{276const char *envstr;277uint8_t mech_last_octet;278int initiator = (targ_name != GSS_C_NO_NAME);279280mech_last_octet = ((uint8_t *)mech_oid->elements)[mech_oid->length - 1];281envstr = getenv(initiator ? "INIT_EXCHANGE_FAIL" : "ACCEPT_EXCHANGE_FAIL");282if (envstr != NULL && atoi(envstr) == mech_last_octet)283return GSS_S_FAILURE;284285assert(meta_data->length == 1 && memcmp(meta_data->value, "X", 1) == 0);286return GSS_S_COMPLETE;287}288289OM_uint32 KRB5_CALLCONV290gssspi_query_mechanism_info(OM_uint32 *minor_status, gss_const_OID mech_oid,291unsigned char auth_scheme[16])292{293/* Copy the mech OID encoding and right-pad it with zeros. */294memset(auth_scheme, 0, 16);295assert(mech_oid->length <= 16);296memcpy(auth_scheme, mech_oid->elements, mech_oid->length);297return GSS_S_COMPLETE;298}299300OM_uint32 KRB5_CALLCONV301gss_inquire_sec_context_by_oid(OM_uint32 *minor_status,302const gss_ctx_id_t context_handle,303const gss_OID desired_object,304gss_buffer_set_t *data_set)305{306struct test_context *ctx = (struct test_context *)context_handle;307OM_uint32 major;308uint8_t keybytes[32] = { 0 };309uint8_t typebytes[4];310gss_buffer_desc key, type;311const char *envstr;312int ask_verify;313314if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_KEY))315ask_verify = 0;316else if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_VERIFY_KEY))317ask_verify = 1;318else319return GSS_S_UNAVAILABLE;320321/*322* By default, make a key available only if the context is established.323* This can be overridden to "always", "init-always", "accept-always",324* or "never".325*/326envstr = getenv("KEY");327if (envstr != NULL && strcmp(envstr, "never") == 0) {328return GSS_S_UNAVAILABLE;329} else if (ctx->hops > 0) {330if (envstr == NULL)331return GSS_S_UNAVAILABLE;332else if (strcmp(envstr, "init-always") == 0 && !ctx->initiator)333return GSS_S_UNAVAILABLE;334else if (strcmp(envstr, "accept-always") == 0 && ctx->initiator)335return GSS_S_UNAVAILABLE;336}337338/* Perturb the key so that each side's verifier key is equal to the other's339* checksum key. */340keybytes[0] = ask_verify ^ ctx->initiator;341342/* Supply an all-zeros aes256-sha1 negoex key. */343if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_KEY) ||344gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_VERIFY_KEY)) {345store_32_le(ENCTYPE_AES256_CTS_HMAC_SHA1_96, typebytes);346key.value = keybytes;347key.length = sizeof(keybytes);348type.value = typebytes;349type.length = sizeof(typebytes);350major = gss_add_buffer_set_member(minor_status, &key, data_set);351if (major != GSS_S_COMPLETE)352return major;353return gss_add_buffer_set_member(minor_status, &type, data_set);354}355356return GSS_S_UNAVAILABLE;357}358359360