Path: blob/main/crypto/krb5/src/lib/gssapi/spnego/negoex_ctx.c
39563 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* Copyright (C) 2011-2018 PADL Software Pty Ltd.3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8*9* * Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* * Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in14* the documentation and/or other materials provided with the15* distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS18* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT19* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS20* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE21* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,22* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES23* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR24* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)25* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,26* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)27* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED28* OF THE POSSIBILITY OF SUCH DAMAGE.29*/3031#include "k5-platform.h"32#include "gssapiP_spnego.h"33#include <generic/gssapiP_generic.h>3435/*36* The initial context token emitted by the initiator is a INITIATOR_NEGO37* message followed by zero or more INITIATOR_META_DATA tokens, and zero38* or one AP_REQUEST tokens.39*40* Upon receiving this, the acceptor computes the list of mutually supported41* authentication mechanisms and performs the metadata exchange. The output42* token is ACCEPTOR_NEGO followed by zero or more ACCEPTOR_META_DATA tokens,43* and zero or one CHALLENGE tokens.44*45* Once the metadata exchange is complete and a mechanism is selected, the46* selected mechanism's context token exchange continues with AP_REQUEST and47* CHALLENGE messages.48*49* Once the context token exchange is complete, VERIFY messages are sent to50* authenticate the entire exchange.51*/5253static void54zero_and_release_buffer_set(gss_buffer_set_t *pbuffers)55{56OM_uint32 tmpmin;57gss_buffer_set_t buffers = *pbuffers;58uint32_t i;5960if (buffers != GSS_C_NO_BUFFER_SET) {61for (i = 0; i < buffers->count; i++)62zap(buffers->elements[i].value, buffers->elements[i].length);6364gss_release_buffer_set(&tmpmin, &buffers);65}6667*pbuffers = GSS_C_NO_BUFFER_SET;68}6970static OM_uint3271buffer_set_to_key(OM_uint32 *minor, gss_buffer_set_t buffers,72krb5_keyblock *key)73{74krb5_error_code ret;7576/* Returned keys must be in two buffers, with the key contents in the first77* and the enctype as a 32-bit little-endian integer in the second. */78if (buffers->count != 2 || buffers->elements[1].length != 4) {79*minor = ERR_NEGOEX_NO_VERIFY_KEY;80return GSS_S_FAILURE;81}8283krb5_free_keyblock_contents(NULL, key);8485key->contents = k5memdup(buffers->elements[0].value,86buffers->elements[0].length, &ret);87if (key->contents == NULL) {88*minor = ret;89return GSS_S_FAILURE;90}91key->length = buffers->elements[0].length;92key->enctype = load_32_le(buffers->elements[1].value);9394return GSS_S_COMPLETE;95}9697static OM_uint3298get_session_keys(OM_uint32 *minor, struct negoex_auth_mech *mech)99{100OM_uint32 major, tmpmin;101gss_buffer_set_t buffers = GSS_C_NO_BUFFER_SET;102103major = gss_inquire_sec_context_by_oid(&tmpmin, mech->mech_context,104GSS_C_INQ_NEGOEX_KEY, &buffers);105if (major == GSS_S_COMPLETE) {106major = buffer_set_to_key(minor, buffers, &mech->key);107zero_and_release_buffer_set(&buffers);108if (major != GSS_S_COMPLETE)109return major;110}111112major = gss_inquire_sec_context_by_oid(&tmpmin, mech->mech_context,113GSS_C_INQ_NEGOEX_VERIFY_KEY,114&buffers);115if (major == GSS_S_COMPLETE) {116major = buffer_set_to_key(minor, buffers, &mech->verify_key);117zero_and_release_buffer_set(&buffers);118if (major != GSS_S_COMPLETE)119return major;120}121122return GSS_S_COMPLETE;123}124125static OM_uint32126emit_initiator_nego(OM_uint32 *minor, spnego_gss_ctx_id_t ctx)127{128OM_uint32 major;129uint8_t random[32];130131major = negoex_random(minor, ctx, random, 32);132if (major != GSS_S_COMPLETE)133return major;134135negoex_add_nego_message(ctx, INITIATOR_NEGO, random);136return GSS_S_COMPLETE;137}138139static OM_uint32140process_initiator_nego(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,141struct negoex_message *messages, size_t nmessages)142{143struct nego_message *msg;144145assert(!ctx->initiate && ctx->negoex_step == 1);146147msg = negoex_locate_nego_message(messages, nmessages, INITIATOR_NEGO);148if (msg == NULL) {149*minor = ERR_NEGOEX_MISSING_NEGO_MESSAGE;150return GSS_S_DEFECTIVE_TOKEN;151}152153negoex_restrict_auth_schemes(ctx, msg->schemes, msg->nschemes);154return GSS_S_COMPLETE;155}156157static OM_uint32158emit_acceptor_nego(OM_uint32 *minor, spnego_gss_ctx_id_t ctx)159{160OM_uint32 major;161uint8_t random[32];162163major = negoex_random(minor, ctx, random, 32);164if (major != GSS_S_COMPLETE)165return major;166167negoex_add_nego_message(ctx, ACCEPTOR_NEGO, random);168return GSS_S_COMPLETE;169}170171static OM_uint32172process_acceptor_nego(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,173struct negoex_message *messages, size_t nmessages)174{175struct nego_message *msg;176177msg = negoex_locate_nego_message(messages, nmessages, ACCEPTOR_NEGO);178if (msg == NULL) {179*minor = ERR_NEGOEX_MISSING_NEGO_MESSAGE;180return GSS_S_DEFECTIVE_TOKEN;181}182183/* Reorder and prune our mech list to match the acceptor's list (or a184* subset of it). */185negoex_common_auth_schemes(ctx, msg->schemes, msg->nschemes);186187return GSS_S_COMPLETE;188}189190static void191query_meta_data(spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,192gss_name_t target, OM_uint32 req_flags)193{194OM_uint32 major, minor;195struct negoex_auth_mech *p, *next;196197K5_TAILQ_FOREACH_SAFE(p, &ctx->negoex_mechs, links, next) {198major = gssspi_query_meta_data(&minor, p->oid, cred, &p->mech_context,199target, req_flags, &p->metadata);200/* GSS_Query_meta_data failure removes mechanism from list. */201if (major != GSS_S_COMPLETE)202negoex_delete_auth_mech(ctx, p);203}204}205206static void207exchange_meta_data(spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,208gss_name_t target, OM_uint32 req_flags,209struct negoex_message *messages, size_t nmessages)210{211OM_uint32 major, minor;212struct negoex_auth_mech *mech;213enum message_type type;214struct exchange_message *msg;215uint32_t i;216217type = ctx->initiate ? ACCEPTOR_META_DATA : INITIATOR_META_DATA;218219for (i = 0; i < nmessages; i++) {220if (messages[i].type != type)221continue;222msg = &messages[i].u.e;223224mech = negoex_locate_auth_scheme(ctx, msg->scheme);225if (mech == NULL)226continue;227228major = gssspi_exchange_meta_data(&minor, mech->oid, cred,229&mech->mech_context, target,230req_flags, &msg->token);231/* GSS_Exchange_meta_data failure removes mechanism from list. */232if (major != GSS_S_COMPLETE)233negoex_delete_auth_mech(ctx, mech);234}235}236237/*238* In the initiator, if we are processing the acceptor's first reply, discard239* the optimistic context if the acceptor ignored the optimistic token. If the240* acceptor continued the optimistic mech, discard all other mechs.241*/242static void243check_optimistic_result(spnego_gss_ctx_id_t ctx,244struct negoex_message *messages, size_t nmessages)245{246struct negoex_auth_mech *mech;247OM_uint32 tmpmin;248249assert(ctx->initiate && ctx->negoex_step == 2);250251/* Do nothing if we didn't make an optimistic context. */252mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);253if (mech == NULL || mech->mech_context == GSS_C_NO_CONTEXT)254return;255256/* If the acceptor used the optimistic token, it will send an acceptor257* token or a checksum (or both) in its first reply. */258if (negoex_locate_exchange_message(messages, nmessages,259CHALLENGE) != NULL ||260negoex_locate_verify_message(messages, nmessages) != NULL) {261/* The acceptor continued the optimistic mech, and metadata exchange262* didn't remove it. Commit to this mechanism. */263negoex_select_auth_mech(ctx, mech);264} else {265/* The acceptor ignored the optimistic token. Restart the mech. */266(void)gss_delete_sec_context(&tmpmin, &mech->mech_context, NULL);267krb5_free_keyblock_contents(NULL, &mech->key);268krb5_free_keyblock_contents(NULL, &mech->verify_key);269mech->complete = mech->sent_checksum = FALSE;270}271}272273/* Perform an initiator step of the underlying mechanism exchange. */274static OM_uint32275mech_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,276gss_name_t target, OM_uint32 req_flags, OM_uint32 time_req,277struct negoex_message *messages, size_t nmessages,278gss_channel_bindings_t bindings, gss_buffer_t output_token,279OM_uint32 *time_rec)280{281OM_uint32 major, first_major = 0, first_minor = 0;282struct negoex_auth_mech *mech = NULL;283gss_buffer_t input_token = GSS_C_NO_BUFFER;284struct exchange_message *msg;285int first_mech;286287output_token->value = NULL;288output_token->length = 0;289290/* Allow disabling of optimistic token for testing. */291if (ctx->negoex_step == 1 &&292secure_getenv("NEGOEX_NO_OPTIMISTIC_TOKEN") != NULL)293return GSS_S_COMPLETE;294295if (K5_TAILQ_EMPTY(&ctx->negoex_mechs)) {296*minor = ERR_NEGOEX_NO_AVAILABLE_MECHS;297return GSS_S_FAILURE;298}299300/*301* Get the input token. The challenge could be for the optimistic mech,302* which we might have discarded in metadata exchange, so ignore the303* challenge if it doesn't match the first auth mech.304*/305mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);306msg = negoex_locate_exchange_message(messages, nmessages, CHALLENGE);307if (msg != NULL && GUID_EQ(msg->scheme, mech->scheme))308input_token = &msg->token;309310if (mech->complete)311return GSS_S_COMPLETE;312313first_mech = TRUE;314315while (!K5_TAILQ_EMPTY(&ctx->negoex_mechs)) {316mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);317318major = gss_init_sec_context(minor, cred, &mech->mech_context, target,319mech->oid, req_flags, time_req, bindings,320input_token, &ctx->actual_mech,321output_token, &ctx->ctx_flags, time_rec);322323if (major == GSS_S_COMPLETE)324mech->complete = 1;325326if (!GSS_ERROR(major))327return get_session_keys(minor, mech);328329/* Remember the error we got from the first mech. */330if (first_mech) {331first_major = major;332first_minor = *minor;333}334335/* If we still have multiple mechs to try, move on to the next one. */336negoex_delete_auth_mech(ctx, mech);337first_mech = FALSE;338input_token = GSS_C_NO_BUFFER;339}340341if (K5_TAILQ_EMPTY(&ctx->negoex_mechs)) {342major = first_major;343*minor = first_minor;344}345346return major;347}348349/* Perform an acceptor step of the underlying mechanism exchange. */350static OM_uint32351mech_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,352gss_cred_id_t cred, struct negoex_message *messages,353size_t nmessages, gss_channel_bindings_t bindings,354gss_buffer_t output_token, OM_uint32 *time_rec)355{356OM_uint32 major, tmpmin;357struct negoex_auth_mech *mech;358struct exchange_message *msg;359360assert(!ctx->initiate && !K5_TAILQ_EMPTY(&ctx->negoex_mechs));361362msg = negoex_locate_exchange_message(messages, nmessages, AP_REQUEST);363if (msg == NULL) {364/* No input token is okay on the first request or if the mech is365* complete. */366if (ctx->negoex_step == 1 ||367K5_TAILQ_FIRST(&ctx->negoex_mechs)->complete)368return GSS_S_COMPLETE;369*minor = ERR_NEGOEX_MISSING_AP_REQUEST_MESSAGE;370return GSS_S_DEFECTIVE_TOKEN;371}372373if (ctx->negoex_step == 1) {374/* Ignore the optimistic token if it isn't for our most preferred375* mech. */376mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);377if (!GUID_EQ(msg->scheme, mech->scheme))378return GSS_S_COMPLETE;379} else {380/* The initiator has selected a mech; discard other entries. */381mech = negoex_locate_auth_scheme(ctx, msg->scheme);382if (mech == NULL) {383*minor = ERR_NEGOEX_NO_AVAILABLE_MECHS;384return GSS_S_FAILURE;385}386negoex_select_auth_mech(ctx, mech);387}388389if (mech->complete)390return GSS_S_COMPLETE;391392if (ctx->internal_name != GSS_C_NO_NAME)393gss_release_name(&tmpmin, &ctx->internal_name);394if (ctx->deleg_cred != GSS_C_NO_CREDENTIAL)395gss_release_cred(&tmpmin, &ctx->deleg_cred);396397major = gss_accept_sec_context(minor, &mech->mech_context, cred,398&msg->token, bindings, &ctx->internal_name,399&ctx->actual_mech, output_token,400&ctx->ctx_flags, time_rec,401&ctx->deleg_cred);402403if (major == GSS_S_COMPLETE)404mech->complete = 1;405406if (!GSS_ERROR(major)) {407major = get_session_keys(minor, mech);408} else if (ctx->negoex_step == 1) {409/* This was an optimistic token; pretend this never happened. */410major = GSS_S_COMPLETE;411*minor = 0;412gss_release_buffer(&tmpmin, output_token);413gss_delete_sec_context(&tmpmin, &mech->mech_context, GSS_C_NO_BUFFER);414}415416return major;417}418419static krb5_keyusage420verify_keyusage(spnego_gss_ctx_id_t ctx, int make_checksum)421{422/* Of course, these are the wrong way around in the spec. */423return (ctx->initiate ^ !make_checksum) ?424NEGOEX_KEYUSAGE_ACCEPTOR_CHECKSUM : NEGOEX_KEYUSAGE_INITIATOR_CHECKSUM;425}426427static OM_uint32428verify_checksum(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,429struct negoex_message *messages, size_t nmessages,430gss_buffer_t input_token, int *send_alert_out)431{432krb5_error_code ret;433struct negoex_auth_mech *mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);434struct verify_message *msg;435krb5_crypto_iov iov[3];436krb5_keyusage usage = verify_keyusage(ctx, FALSE);437krb5_boolean valid;438439*send_alert_out = FALSE;440assert(mech != NULL);441442/* The other party may not be ready to send a verify token yet, or (in the443* first initiator step) may send one for a mechanism we don't support. */444msg = negoex_locate_verify_message(messages, nmessages);445if (msg == NULL || !GUID_EQ(msg->scheme, mech->scheme))446return GSS_S_COMPLETE;447448/* A recoverable error may cause us to be unable to verify a token from the449* other party. In this case we should send an alert. */450if (mech->verify_key.enctype == ENCTYPE_NULL) {451*send_alert_out = TRUE;452return GSS_S_COMPLETE;453}454455/* Verify the checksum over the existing transcript and the portion of the456* input token leading up to the verify message. */457assert(input_token != NULL);458iov[0].flags = KRB5_CRYPTO_TYPE_DATA;459iov[0].data = make_data(ctx->negoex_transcript.data,460ctx->negoex_transcript.len);461iov[1].flags = KRB5_CRYPTO_TYPE_DATA;462iov[1].data = make_data(input_token->value, msg->offset_in_token);463iov[2].flags = KRB5_CRYPTO_TYPE_CHECKSUM;464iov[2].data = make_data((uint8_t *)msg->cksum, msg->cksum_len);465466ret = krb5_c_verify_checksum_iov(ctx->kctx, msg->cksum_type,467&mech->verify_key, usage, iov, 3, &valid);468if (ret) {469*minor = ret;470return GSS_S_FAILURE;471}472if (!valid || !krb5_c_is_keyed_cksum(msg->cksum_type)) {473*minor = ERR_NEGOEX_INVALID_CHECKSUM;474return GSS_S_BAD_SIG;475}476477mech->verified_checksum = TRUE;478return GSS_S_COMPLETE;479}480481static OM_uint32482make_checksum(OM_uint32 *minor, spnego_gss_ctx_id_t ctx)483{484krb5_error_code ret;485krb5_data d;486krb5_keyusage usage = verify_keyusage(ctx, TRUE);487krb5_checksum cksum;488struct negoex_auth_mech *mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);489490assert(mech != NULL);491492if (mech->key.enctype == ENCTYPE_NULL) {493if (mech->complete) {494*minor = ERR_NEGOEX_NO_VERIFY_KEY;495return GSS_S_UNAVAILABLE;496} else {497return GSS_S_COMPLETE;498}499}500501d = make_data(ctx->negoex_transcript.data, ctx->negoex_transcript.len);502ret = krb5_c_make_checksum(ctx->kctx, 0, &mech->key, usage, &d, &cksum);503if (ret) {504*minor = ret;505return GSS_S_FAILURE;506}507508negoex_add_verify_message(ctx, mech->scheme, cksum.checksum_type,509cksum.contents, cksum.length);510511mech->sent_checksum = TRUE;512krb5_free_checksum_contents(ctx->kctx, &cksum);513return GSS_S_COMPLETE;514}515516/* If the other side sent a VERIFY_NO_KEY pulse alert, clear the checksum state517* on the mechanism so that we send another VERIFY message. */518static void519process_alerts(spnego_gss_ctx_id_t ctx,520struct negoex_message *messages, uint32_t nmessages)521{522struct alert_message *msg;523struct negoex_auth_mech *mech;524525msg = negoex_locate_alert_message(messages, nmessages);526if (msg != NULL && msg->verify_no_key) {527mech = negoex_locate_auth_scheme(ctx, msg->scheme);528if (mech != NULL) {529mech->sent_checksum = FALSE;530krb5_free_keyblock_contents(NULL, &mech->key);531krb5_free_keyblock_contents(NULL, &mech->verify_key);532}533}534}535536static OM_uint32537make_output_token(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,538gss_buffer_t mech_output_token, int send_alert,539gss_buffer_t output_token)540{541OM_uint32 major;542struct negoex_auth_mech *mech;543enum message_type type;544size_t old_transcript_len = ctx->negoex_transcript.len;545546output_token->length = 0;547output_token->value = NULL;548549/* If the mech is complete and we previously sent a checksum, we just550* processed the last leg and don't need to send another token. */551if (mech_output_token->length == 0 &&552K5_TAILQ_FIRST(&ctx->negoex_mechs)->sent_checksum)553return GSS_S_COMPLETE;554555if (ctx->negoex_step == 1) {556if (ctx->initiate)557major = emit_initiator_nego(minor, ctx);558else559major = emit_acceptor_nego(minor, ctx);560if (major != GSS_S_COMPLETE)561return major;562563type = ctx->initiate ? INITIATOR_META_DATA : ACCEPTOR_META_DATA;564K5_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links) {565if (mech->metadata.length > 0) {566negoex_add_exchange_message(ctx, type, mech->scheme,567&mech->metadata);568}569}570}571572mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);573574if (mech_output_token->length > 0) {575type = ctx->initiate ? AP_REQUEST : CHALLENGE;576negoex_add_exchange_message(ctx, type, mech->scheme,577mech_output_token);578}579580if (send_alert)581negoex_add_verify_no_key_alert(ctx, mech->scheme);582583/* Try to add a VERIFY message if we haven't already done so. */584if (!mech->sent_checksum) {585major = make_checksum(minor, ctx);586if (major != GSS_S_COMPLETE)587return major;588}589590if (ctx->negoex_transcript.data == NULL) {591*minor = ENOMEM;592return GSS_S_FAILURE;593}594595/* Copy what we added to the transcript into the output token. */596output_token->length = ctx->negoex_transcript.len - old_transcript_len;597output_token->value = gssalloc_malloc(output_token->length);598if (output_token->value == NULL) {599*minor = ENOMEM;600return GSS_S_FAILURE;601}602memcpy(output_token->value,603(uint8_t *)ctx->negoex_transcript.data + old_transcript_len,604output_token->length);605606return GSS_S_COMPLETE;607}608609OM_uint32610negoex_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,611gss_name_t target_name, OM_uint32 req_flags, OM_uint32 time_req,612gss_buffer_t input_token, gss_channel_bindings_t bindings,613gss_buffer_t output_token, OM_uint32 *time_rec)614{615OM_uint32 major, tmpmin;616gss_buffer_desc mech_output_token = GSS_C_EMPTY_BUFFER;617struct negoex_message *messages = NULL;618struct negoex_auth_mech *mech;619size_t nmessages = 0;620int send_alert = FALSE;621622if (ctx->negoex_step == 0 && input_token != GSS_C_NO_BUFFER &&623input_token->length != 0)624return GSS_S_DEFECTIVE_TOKEN;625626major = negoex_prep_context_for_negoex(minor, ctx);627if (major != GSS_S_COMPLETE)628goto cleanup;629630ctx->negoex_step++;631632if (input_token != GSS_C_NO_BUFFER && input_token->length > 0) {633major = negoex_parse_token(minor, ctx, input_token, &messages,634&nmessages);635if (major != GSS_S_COMPLETE)636goto cleanup;637}638639process_alerts(ctx, messages, nmessages);640641if (ctx->negoex_step == 1) {642/* Choose a random conversation ID. */643major = negoex_random(minor, ctx, ctx->negoex_conv_id, GUID_LENGTH);644if (major != GSS_S_COMPLETE)645goto cleanup;646647/* Query each mech for its metadata (this may prune the mech list). */648query_meta_data(ctx, cred, target_name, req_flags);649} else if (ctx->negoex_step == 2) {650/* See if the mech processed the optimistic token. */651check_optimistic_result(ctx, messages, nmessages);652653/* Pass the acceptor metadata to each mech to prune the list. */654exchange_meta_data(ctx, cred, target_name, req_flags,655messages, nmessages);656657/* Process the ACCEPTOR_NEGO message. */658major = process_acceptor_nego(minor, ctx, messages, nmessages);659if (major != GSS_S_COMPLETE)660goto cleanup;661}662663/* Process the input token and/or produce an output token. This may prune664* the mech list, but on success there will be at least one mech entry. */665major = mech_init(minor, ctx, cred, target_name, req_flags, time_req,666messages, nmessages, bindings, &mech_output_token,667time_rec);668if (major != GSS_S_COMPLETE)669goto cleanup;670assert(!K5_TAILQ_EMPTY(&ctx->negoex_mechs));671672/* At this point in step 2 we have performed the metadata exchange and673* chosen a mech we can use, so discard any fallback mech entries. */674if (ctx->negoex_step == 2)675negoex_select_auth_mech(ctx, K5_TAILQ_FIRST(&ctx->negoex_mechs));676677major = verify_checksum(minor, ctx, messages, nmessages, input_token,678&send_alert);679if (major != GSS_S_COMPLETE)680goto cleanup;681682if (input_token != GSS_C_NO_BUFFER) {683k5_buf_add_len(&ctx->negoex_transcript, input_token->value,684input_token->length);685}686687major = make_output_token(minor, ctx, &mech_output_token, send_alert,688output_token);689if (major != GSS_S_COMPLETE)690goto cleanup;691692mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);693major = (mech->complete && mech->verified_checksum) ? GSS_S_COMPLETE :694GSS_S_CONTINUE_NEEDED;695696cleanup:697free(messages);698gss_release_buffer(&tmpmin, &mech_output_token);699negoex_prep_context_for_spnego(ctx);700return major;701}702703OM_uint32704negoex_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,705gss_buffer_t input_token, gss_channel_bindings_t bindings,706gss_buffer_t output_token, OM_uint32 *time_rec)707{708OM_uint32 major, tmpmin;709gss_buffer_desc mech_output_token = GSS_C_EMPTY_BUFFER;710struct negoex_message *messages = NULL;711struct negoex_auth_mech *mech;712size_t nmessages;713int send_alert = FALSE;714715if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {716major = GSS_S_DEFECTIVE_TOKEN;717goto cleanup;718}719720major = negoex_prep_context_for_negoex(minor, ctx);721if (major != GSS_S_COMPLETE)722goto cleanup;723724ctx->negoex_step++;725726major = negoex_parse_token(minor, ctx, input_token, &messages, &nmessages);727if (major != GSS_S_COMPLETE)728goto cleanup;729730process_alerts(ctx, messages, nmessages);731732if (ctx->negoex_step == 1) {733/* Read the INITIATOR_NEGO message to prune the candidate mech list. */734major = process_initiator_nego(minor, ctx, messages, nmessages);735if (major != GSS_S_COMPLETE)736goto cleanup;737738/*739* Pass the initiator metadata to each mech to prune the list, and740* query each mech for its acceptor metadata (which may also prune the741* list).742*/743exchange_meta_data(ctx, cred, GSS_C_NO_NAME, 0, messages, nmessages);744query_meta_data(ctx, cred, GSS_C_NO_NAME, 0);745746if (K5_TAILQ_EMPTY(&ctx->negoex_mechs)) {747*minor = ERR_NEGOEX_NO_AVAILABLE_MECHS;748major = GSS_S_FAILURE;749goto cleanup;750}751}752753/*754* Process the input token and possibly produce an output token. This may755* prune the list to a single mech. Continue on error if an output token756* is generated, so that we send the token to the initiator.757*/758major = mech_accept(minor, ctx, cred, messages, nmessages, bindings,759&mech_output_token, time_rec);760if (major != GSS_S_COMPLETE && mech_output_token.length == 0)761goto cleanup;762763if (major == GSS_S_COMPLETE) {764major = verify_checksum(minor, ctx, messages, nmessages, input_token,765&send_alert);766if (major != GSS_S_COMPLETE)767goto cleanup;768}769770k5_buf_add_len(&ctx->negoex_transcript,771input_token->value, input_token->length);772773major = make_output_token(minor, ctx, &mech_output_token, send_alert,774output_token);775if (major != GSS_S_COMPLETE)776goto cleanup;777778mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);779major = (mech->complete && mech->verified_checksum) ? GSS_S_COMPLETE :780GSS_S_CONTINUE_NEEDED;781782cleanup:783free(messages);784gss_release_buffer(&tmpmin, &mech_output_token);785negoex_prep_context_for_spnego(ctx);786return major;787}788789790