Path: blob/main/crypto/krb5/src/lib/gssapi/spnego/negoex_util.c
39562 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 "gssapiP_spnego.h"32#include <generic/gssapiP_generic.h>33#include "k5-input.h"3435static void36release_auth_mech(struct negoex_auth_mech *mech);3738OM_uint3239negoex_random(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,40uint8_t *data, size_t length)41{42krb5_data d = make_data(data, length);4344*minor = krb5_c_random_make_octets(ctx->kctx, &d);45return *minor ? GSS_S_FAILURE : GSS_S_COMPLETE;46}4748/*49* SPNEGO functions expect to find the active mech context in ctx->ctx_handle,50* but the metadata exchange APIs force us to have one mech context per mech51* entry. To address this mismatch, move the active mech context (if we have52* one) to ctx->ctx_handle at the end of NegoEx processing.53*/54void55negoex_prep_context_for_spnego(spnego_gss_ctx_id_t ctx)56{57struct negoex_auth_mech *mech;5859mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);60if (mech == NULL || mech->mech_context == GSS_C_NO_CONTEXT)61return;6263assert(ctx->ctx_handle == GSS_C_NO_CONTEXT);64ctx->ctx_handle = mech->mech_context;65mech->mech_context = GSS_C_NO_CONTEXT;66}6768OM_uint3269negoex_prep_context_for_negoex(OM_uint32 *minor, spnego_gss_ctx_id_t ctx)70{71krb5_error_code ret;72struct negoex_auth_mech *mech;7374if (ctx->kctx != NULL) {75/* The context is already initialized for NegoEx. Undo what76* negoex_prep_for_spnego() did, if applicable. */77if (ctx->ctx_handle != GSS_C_NO_CONTEXT) {78mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);79assert(mech != NULL && mech->mech_context == GSS_C_NO_CONTEXT);80mech->mech_context = ctx->ctx_handle;81ctx->ctx_handle = GSS_C_NO_CONTEXT;82}83return GSS_S_COMPLETE;84}8586/* Initialize the NegoEX context fields. (negoex_mechs is already set up87* by SPNEGO.) */88ret = krb5_init_context(&ctx->kctx);89if (ret) {90*minor = ret;91return GSS_S_FAILURE;92}9394k5_buf_init_dynamic(&ctx->negoex_transcript);9596return GSS_S_COMPLETE;97}9899static void100release_all_mechs(spnego_gss_ctx_id_t ctx)101{102struct negoex_auth_mech *mech, *next;103104K5_TAILQ_FOREACH_SAFE(mech, &ctx->negoex_mechs, links, next)105release_auth_mech(mech);106K5_TAILQ_INIT(&ctx->negoex_mechs);107}108109void110negoex_release_context(spnego_gss_ctx_id_t ctx)111{112k5_buf_free(&ctx->negoex_transcript);113release_all_mechs(ctx);114krb5_free_context(ctx->kctx);115ctx->kctx = NULL;116}117118static const char *119typestr(enum message_type type)120{121if (type == INITIATOR_NEGO)122return "INITIATOR_NEGO";123else if (type == ACCEPTOR_NEGO)124return "ACCEPTOR_NEGO";125else if (type == INITIATOR_META_DATA)126return "INITIATOR_META_DATA";127else if (type == ACCEPTOR_META_DATA)128return "ACCEPTOR_META_DATA";129else if (type == CHALLENGE)130return "CHALLENGE";131else if (type == AP_REQUEST)132return "AP_REQUEST";133else if (type == VERIFY)134return "VERIFY";135else if (type == ALERT)136return "ALERT";137else138return "UNKNOWN";139}140141static void142add_guid(struct k5buf *buf, const uint8_t guid[GUID_LENGTH])143{144uint32_t data1 = load_32_le(guid);145uint16_t data2 = load_16_le(guid + 4), data3 = load_16_le(guid + 6);146147k5_buf_add_fmt(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",148data1, data2, data3, guid[8], guid[9], guid[10], guid[11],149guid[12], guid[13], guid[14], guid[15]);150}151152static char *153guid_to_string(const uint8_t guid[GUID_LENGTH])154{155struct k5buf buf;156157k5_buf_init_dynamic(&buf);158add_guid(&buf, guid);159return k5_buf_cstring(&buf);160}161162/* Check that the described vector lies within the message, and return a163* pointer to its first element. */164static inline const uint8_t *165vector_base(size_t offset, size_t count, size_t width,166const uint8_t *msg_base, size_t msg_len)167{168if (offset > msg_len || count > (msg_len - offset) / width)169return NULL;170return msg_base + offset;171}172173/* Trace a received message. Call after the context sequence number is174* incremented. */175static void176trace_received_message(spnego_gss_ctx_id_t ctx,177const struct negoex_message *msg)178{179struct k5buf buf;180uint16_t i;181char *info = NULL;182183if (msg->type == INITIATOR_NEGO || msg->type == ACCEPTOR_NEGO) {184k5_buf_init_dynamic(&buf);185for (i = 0; i < msg->u.n.nschemes; i++) {186add_guid(&buf, msg->u.n.schemes + i * GUID_LENGTH);187if (i + 1 < msg->u.n.nschemes)188k5_buf_add(&buf, " ");189}190info = k5_buf_cstring(&buf);191} else if (msg->type == INITIATOR_META_DATA ||192msg->type == ACCEPTOR_META_DATA ||193msg->type == CHALLENGE || msg->type == AP_REQUEST) {194info = guid_to_string(msg->u.e.scheme);195} else if (msg->type == VERIFY) {196info = guid_to_string(msg->u.v.scheme);197} else if (msg->type == ALERT) {198info = guid_to_string(msg->u.a.scheme);199}200201if (info == NULL)202return;203204TRACE_NEGOEX_INCOMING(ctx->kctx, ctx->negoex_seqnum - 1,205typestr(msg->type), info);206free(info);207}208209/* Trace an outgoing message with a GUID info string. Call after the context210* sequence number is incremented. */211static void212trace_outgoing_message(spnego_gss_ctx_id_t ctx, enum message_type type,213const uint8_t guid[GUID_LENGTH])214{215char *info = guid_to_string(guid);216217if (info == NULL)218return;219TRACE_NEGOEX_OUTGOING(ctx->kctx, ctx->negoex_seqnum - 1, typestr(type),220info);221free(info);222}223224static OM_uint32225parse_nego_message(OM_uint32 *minor, struct k5input *in,226const uint8_t *msg_base, size_t msg_len,227struct nego_message *msg)228{229const uint8_t *p;230uint64_t protocol_version;231uint32_t extension_type;232size_t offset, count, i;233234p = k5_input_get_bytes(in, sizeof(msg->random));235if (p != NULL)236memcpy(msg->random, p, sizeof(msg->random));237protocol_version = k5_input_get_uint64_le(in);238if (protocol_version != 0) {239*minor = ERR_NEGOEX_UNSUPPORTED_VERSION;240return GSS_S_UNAVAILABLE;241}242243offset = k5_input_get_uint32_le(in);244count = k5_input_get_uint16_le(in);245msg->schemes = vector_base(offset, count, GUID_LENGTH, msg_base, msg_len);246msg->nschemes = count;247if (msg->schemes == NULL) {248*minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;249return GSS_S_DEFECTIVE_TOKEN;250}251252offset = k5_input_get_uint32_le(in);253count = k5_input_get_uint16_le(in);254p = vector_base(offset, count, EXTENSION_LENGTH, msg_base, msg_len);255for (i = 0; i < count; i++) {256extension_type = load_32_le(p + i * EXTENSION_LENGTH);257if (extension_type & EXTENSION_FLAG_CRITICAL) {258*minor = ERR_NEGOEX_UNSUPPORTED_CRITICAL_EXTENSION;259return GSS_S_UNAVAILABLE;260}261}262263return GSS_S_COMPLETE;264}265266static OM_uint32267parse_exchange_message(OM_uint32 *minor, struct k5input *in,268const uint8_t *msg_base, size_t msg_len,269struct exchange_message *msg)270{271const uint8_t *p;272size_t offset, len;273274p = k5_input_get_bytes(in, GUID_LENGTH);275if (p != NULL)276memcpy(msg->scheme, p, GUID_LENGTH);277278offset = k5_input_get_uint32_le(in);279len = k5_input_get_uint32_le(in);280p = vector_base(offset, len, 1, msg_base, msg_len);281if (p == NULL) {282*minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;283return GSS_S_DEFECTIVE_TOKEN;284}285msg->token.value = (void *)p;286msg->token.length = len;287288return GSS_S_COMPLETE;289}290291static OM_uint32292parse_verify_message(OM_uint32 *minor, struct k5input *in,293const uint8_t *msg_base, size_t msg_len,294size_t token_offset, struct verify_message *msg)295{296const uint8_t *p;297size_t offset, len;298uint32_t hdrlen, cksum_scheme;299300p = k5_input_get_bytes(in, GUID_LENGTH);301if (p != NULL)302memcpy(msg->scheme, p, GUID_LENGTH);303304hdrlen = k5_input_get_uint32_le(in);305if (hdrlen != CHECKSUM_HEADER_LENGTH) {306*minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;307return GSS_S_DEFECTIVE_TOKEN;308}309cksum_scheme = k5_input_get_uint32_le(in);310if (cksum_scheme != CHECKSUM_SCHEME_RFC3961) {311*minor = ERR_NEGOEX_UNKNOWN_CHECKSUM_SCHEME;312return GSS_S_UNAVAILABLE;313}314msg->cksum_type = k5_input_get_uint32_le(in);315316offset = k5_input_get_uint32_le(in);317len = k5_input_get_uint32_le(in);318msg->cksum = vector_base(offset, len, 1, msg_base, msg_len);319msg->cksum_len = len;320if (msg->cksum == NULL) {321*minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;322return GSS_S_DEFECTIVE_TOKEN;323}324325msg->offset_in_token = token_offset;326return GSS_S_COMPLETE;327}328329static OM_uint32330parse_alert_message(OM_uint32 *minor, struct k5input *in,331const uint8_t *msg_base, size_t msg_len,332struct alert_message *msg)333{334const uint8_t *p;335uint32_t atype, reason;336size_t alerts_offset, nalerts, value_offset, value_len, i;337struct k5input alerts_in, pulse_in;338339p = k5_input_get_bytes(in, GUID_LENGTH);340if (p != NULL)341memcpy(msg->scheme, p, GUID_LENGTH);342(void)k5_input_get_uint32_le(in); /* skip over ErrorCode */343alerts_offset = k5_input_get_uint32_le(in);344nalerts = k5_input_get_uint32_le(in);345p = vector_base(alerts_offset, nalerts, ALERT_LENGTH, msg_base, msg_len);346if (p == NULL) {347*minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;348return GSS_S_DEFECTIVE_TOKEN;349}350351/* Look for a VERIFY_NO_KEY pulse alert in the alerts vector. */352msg->verify_no_key = FALSE;353k5_input_init(&alerts_in, p, nalerts * ALERT_LENGTH);354for (i = 0; i < nalerts; i++) {355atype = k5_input_get_uint32_le(&alerts_in);356value_offset = k5_input_get_uint32_le(&alerts_in);357value_len = k5_input_get_uint32_le(&alerts_in);358p = vector_base(value_offset, value_len, 1, msg_base, msg_len);359if (p == NULL) {360*minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;361return GSS_S_DEFECTIVE_TOKEN;362}363364if (atype == ALERT_TYPE_PULSE && value_len >= ALERT_PULSE_LENGTH) {365k5_input_init(&pulse_in, p, value_len);366(void)k5_input_get_uint32_le(&pulse_in); /* skip header length */367reason = k5_input_get_uint32_le(&pulse_in);368if (reason == ALERT_VERIFY_NO_KEY)369msg->verify_no_key = TRUE;370}371}372373return GSS_S_COMPLETE;374}375376static OM_uint32377parse_message(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, struct k5input *in,378const uint8_t *token_base, struct negoex_message *msg)379{380OM_uint32 major;381const uint8_t *msg_base = in->ptr, *conv_id;382size_t token_remaining = in->len, header_len, msg_len;383uint64_t signature;384uint32_t type, seqnum;385386signature = k5_input_get_uint64_le(in);387type = k5_input_get_uint32_le(in);388seqnum = k5_input_get_uint32_le(in);389header_len = k5_input_get_uint32_le(in);390msg_len = k5_input_get_uint32_le(in);391conv_id = k5_input_get_bytes(in, GUID_LENGTH);392393if (in->status || msg_len > token_remaining || header_len > msg_len) {394*minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;395return GSS_S_DEFECTIVE_TOKEN;396}397if (signature != MESSAGE_SIGNATURE) {398*minor = ERR_NEGOEX_INVALID_MESSAGE_SIGNATURE;399return GSS_S_DEFECTIVE_TOKEN;400}401if (seqnum != ctx->negoex_seqnum) {402*minor = ERR_NEGOEX_MESSAGE_OUT_OF_SEQUENCE;403return GSS_S_DEFECTIVE_TOKEN;404}405if (seqnum == 0) {406memcpy(ctx->negoex_conv_id, conv_id, GUID_LENGTH);407} else if (!GUID_EQ(conv_id, ctx->negoex_conv_id)) {408*minor = ERR_NEGOEX_INVALID_CONVERSATION_ID;409return GSS_S_DEFECTIVE_TOKEN;410}411412/* Restrict the input region to the header. */413in->len = header_len - (in->ptr - msg_base);414415msg->type = type;416if (type == INITIATOR_NEGO || type == ACCEPTOR_NEGO) {417major = parse_nego_message(minor, in, msg_base, msg_len, &msg->u.n);418} else if (type == INITIATOR_META_DATA || type == ACCEPTOR_META_DATA ||419type == CHALLENGE || type == AP_REQUEST) {420major = parse_exchange_message(minor, in, msg_base, msg_len,421&msg->u.e);422} else if (type == VERIFY) {423major = parse_verify_message(minor, in, msg_base, msg_len,424msg_base - token_base, &msg->u.v);425} else if (type == ALERT) {426major = parse_alert_message(minor, in, msg_base, msg_len, &msg->u.a);427} else {428*minor = ERR_NEGOEX_INVALID_MESSAGE_TYPE;429return GSS_S_DEFECTIVE_TOKEN;430}431if (major != GSS_S_COMPLETE)432return major;433434/* Reset the input buffer to the remainder of the token. */435if (!in->status)436k5_input_init(in, msg_base + msg_len, token_remaining - msg_len);437438ctx->negoex_seqnum++;439trace_received_message(ctx, msg);440return GSS_S_COMPLETE;441}442443/*444* Parse token into an array of negoex_message structures. All pointer fields445* within the parsed messages are aliases into token, so the result can be446* freed with free(). An unknown protocol version, a critical extension, or an447* unknown checksum scheme will cause a parsing failure. Increment the448* sequence number in ctx for each message, and record and check the449* conversation ID in ctx as appropriate.450*/451OM_uint32452negoex_parse_token(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,453gss_const_buffer_t token,454struct negoex_message **messages_out, size_t *count_out)455{456OM_uint32 major = GSS_S_COMPLETE;457size_t count = 0;458struct k5input in;459struct negoex_message *messages = NULL, *newptr;460461*messages_out = NULL;462*count_out = 0;463assert(token != GSS_C_NO_BUFFER);464k5_input_init(&in, token->value, token->length);465466while (in.status == 0 && in.len > 0) {467newptr = realloc(messages, (count + 1) * sizeof(*newptr));468if (newptr == NULL) {469free(messages);470*minor = ENOMEM;471return GSS_S_FAILURE;472}473messages = newptr;474475major = parse_message(minor, ctx, &in, token->value, &messages[count]);476if (major != GSS_S_COMPLETE)477break;478479count++;480}481482if (in.status) {483*minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;484major = GSS_S_DEFECTIVE_TOKEN;485}486if (major != GSS_S_COMPLETE) {487free(messages);488return major;489}490491*messages_out = messages;492*count_out = count;493return GSS_S_COMPLETE;494}495496static struct negoex_message *497locate_message(struct negoex_message *messages, size_t nmessages,498enum message_type type)499{500uint32_t i;501502for (i = 0; i < nmessages; i++) {503if (messages[i].type == type)504return &messages[i];505}506507return NULL;508}509510struct nego_message *511negoex_locate_nego_message(struct negoex_message *messages, size_t nmessages,512enum message_type type)513{514struct negoex_message *msg = locate_message(messages, nmessages, type);515516return (msg == NULL) ? NULL : &msg->u.n;517}518519struct exchange_message *520negoex_locate_exchange_message(struct negoex_message *messages,521size_t nmessages, enum message_type type)522{523struct negoex_message *msg = locate_message(messages, nmessages, type);524525return (msg == NULL) ? NULL : &msg->u.e;526}527528struct verify_message *529negoex_locate_verify_message(struct negoex_message *messages,530size_t nmessages)531{532struct negoex_message *msg = locate_message(messages, nmessages, VERIFY);533534return (msg == NULL) ? NULL : &msg->u.v;535}536537struct alert_message *538negoex_locate_alert_message(struct negoex_message *messages, size_t nmessages)539{540struct negoex_message *msg = locate_message(messages, nmessages, ALERT);541542return (msg == NULL) ? NULL : &msg->u.a;543}544545/*546* Add the encoding of a MESSAGE_HEADER structure to buf, given the number of547* bytes of the payload following the full header. Increment the sequence548* number in ctx. Set *payload_start_out to the position of the payload within549* the message.550*/551static void552put_message_header(spnego_gss_ctx_id_t ctx, enum message_type type,553uint32_t payload_len, uint32_t *payload_start_out)554{555size_t header_len;556557if (type == INITIATOR_NEGO || type == ACCEPTOR_NEGO)558header_len = NEGO_MESSAGE_HEADER_LENGTH;559else if (type == INITIATOR_META_DATA || type == ACCEPTOR_META_DATA ||560type == CHALLENGE || type == AP_REQUEST)561header_len = EXCHANGE_MESSAGE_HEADER_LENGTH;562else if (type == VERIFY)563header_len = VERIFY_MESSAGE_HEADER_LENGTH;564else if (type == ALERT)565header_len = ALERT_MESSAGE_HEADER_LENGTH;566else567abort();568569k5_buf_add_uint64_le(&ctx->negoex_transcript, MESSAGE_SIGNATURE);570k5_buf_add_uint32_le(&ctx->negoex_transcript, type);571k5_buf_add_uint32_le(&ctx->negoex_transcript, ctx->negoex_seqnum++);572k5_buf_add_uint32_le(&ctx->negoex_transcript, header_len);573k5_buf_add_uint32_le(&ctx->negoex_transcript, header_len + payload_len);574k5_buf_add_len(&ctx->negoex_transcript, ctx->negoex_conv_id, GUID_LENGTH);575576*payload_start_out = header_len;577}578579void580negoex_add_nego_message(spnego_gss_ctx_id_t ctx, enum message_type type,581uint8_t random[32])582{583struct negoex_auth_mech *mech;584uint32_t payload_start, seqnum = ctx->negoex_seqnum;585uint16_t nschemes;586struct k5buf buf;587588nschemes = 0;589K5_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links)590nschemes++;591592put_message_header(ctx, type, nschemes * GUID_LENGTH, &payload_start);593k5_buf_add_len(&ctx->negoex_transcript, random, 32);594/* ProtocolVersion */595k5_buf_add_uint64_le(&ctx->negoex_transcript, 0);596/* AuthSchemes vector */597k5_buf_add_uint32_le(&ctx->negoex_transcript, payload_start);598k5_buf_add_uint16_le(&ctx->negoex_transcript, nschemes);599/* Extensions vector */600k5_buf_add_uint32_le(&ctx->negoex_transcript, payload_start);601k5_buf_add_uint16_le(&ctx->negoex_transcript, 0);602/* Four bytes of padding to reach a multiple of 8 bytes. */603k5_buf_add_len(&ctx->negoex_transcript, "\0\0\0\0", 4);604605/* Payload (auth schemes); also build guid string for tracing. */606k5_buf_init_dynamic(&buf);607K5_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links) {608k5_buf_add_len(&ctx->negoex_transcript, mech->scheme, GUID_LENGTH);609add_guid(&buf, mech->scheme);610k5_buf_add(&buf, " ");611}612613if (buf.len > 0) {614k5_buf_truncate(&buf, buf.len - 1);615TRACE_NEGOEX_OUTGOING(ctx->kctx, seqnum, typestr(type),616k5_buf_cstring(&buf));617k5_buf_free(&buf);618}619}620621void622negoex_add_exchange_message(spnego_gss_ctx_id_t ctx, enum message_type type,623const auth_scheme scheme, gss_buffer_t token)624{625uint32_t payload_start;626627put_message_header(ctx, type, token->length, &payload_start);628k5_buf_add_len(&ctx->negoex_transcript, scheme, GUID_LENGTH);629/* Exchange byte vector */630k5_buf_add_uint32_le(&ctx->negoex_transcript, payload_start);631k5_buf_add_uint32_le(&ctx->negoex_transcript, token->length);632/* Payload (token) */633k5_buf_add_len(&ctx->negoex_transcript, token->value, token->length);634635trace_outgoing_message(ctx, type, scheme);636}637638void639negoex_add_verify_message(spnego_gss_ctx_id_t ctx, const auth_scheme scheme,640uint32_t cksum_type, const uint8_t *cksum,641uint32_t cksum_len)642{643uint32_t payload_start;644645put_message_header(ctx, VERIFY, cksum_len, &payload_start);646k5_buf_add_len(&ctx->negoex_transcript, scheme, GUID_LENGTH);647k5_buf_add_uint32_le(&ctx->negoex_transcript, CHECKSUM_HEADER_LENGTH);648k5_buf_add_uint32_le(&ctx->negoex_transcript, CHECKSUM_SCHEME_RFC3961);649k5_buf_add_uint32_le(&ctx->negoex_transcript, cksum_type);650/* ChecksumValue vector */651k5_buf_add_uint32_le(&ctx->negoex_transcript, payload_start);652k5_buf_add_uint32_le(&ctx->negoex_transcript, cksum_len);653/* Four bytes of padding to reach a multiple of 8 bytes. */654k5_buf_add_len(&ctx->negoex_transcript, "\0\0\0\0", 4);655/* Payload (checksum contents) */656k5_buf_add_len(&ctx->negoex_transcript, cksum, cksum_len);657658trace_outgoing_message(ctx, VERIFY, scheme);659}660661/* Add an ALERT_MESSAGE containing a single ALERT_TYPE_PULSE alert with the662* reason ALERT_VERIFY_NO_KEY. */663void664negoex_add_verify_no_key_alert(spnego_gss_ctx_id_t ctx,665const auth_scheme scheme)666{667uint32_t payload_start;668669put_message_header(ctx, ALERT, ALERT_LENGTH + ALERT_PULSE_LENGTH,670&payload_start);671k5_buf_add_len(&ctx->negoex_transcript, scheme, GUID_LENGTH);672/* ErrorCode */673k5_buf_add_uint32_le(&ctx->negoex_transcript, 0);674/* Alerts vector */675k5_buf_add_uint32_le(&ctx->negoex_transcript, payload_start);676k5_buf_add_uint16_le(&ctx->negoex_transcript, 1);677/* Six bytes of padding to reach a multiple of 8 bytes. */678k5_buf_add_len(&ctx->negoex_transcript, "\0\0\0\0\0\0", 6);679/* Payload part 1: a single ALERT element */680k5_buf_add_uint32_le(&ctx->negoex_transcript, ALERT_TYPE_PULSE);681k5_buf_add_uint32_le(&ctx->negoex_transcript,682payload_start + ALERT_LENGTH);683k5_buf_add_uint32_le(&ctx->negoex_transcript, ALERT_PULSE_LENGTH);684/* Payload part 2: ALERT_PULSE */685k5_buf_add_uint32_le(&ctx->negoex_transcript, ALERT_PULSE_LENGTH);686k5_buf_add_uint32_le(&ctx->negoex_transcript, ALERT_VERIFY_NO_KEY);687688trace_outgoing_message(ctx, ALERT, scheme);689}690691static void692release_auth_mech(struct negoex_auth_mech *mech)693{694OM_uint32 tmpmin;695696if (mech == NULL)697return;698699gss_delete_sec_context(&tmpmin, &mech->mech_context, NULL);700generic_gss_release_oid(&tmpmin, &mech->oid);701gss_release_buffer(&tmpmin, &mech->metadata);702krb5_free_keyblock_contents(NULL, &mech->key);703krb5_free_keyblock_contents(NULL, &mech->verify_key);704705free(mech);706}707708void709negoex_delete_auth_mech(spnego_gss_ctx_id_t ctx,710struct negoex_auth_mech *mech)711{712K5_TAILQ_REMOVE(&ctx->negoex_mechs, mech, links);713release_auth_mech(mech);714}715716/* Remove all auth mech entries except for mech from ctx->mechs. */717void718negoex_select_auth_mech(spnego_gss_ctx_id_t ctx,719struct negoex_auth_mech *mech)720{721assert(mech != NULL);722K5_TAILQ_REMOVE(&ctx->negoex_mechs, mech, links);723release_all_mechs(ctx);724K5_TAILQ_INSERT_HEAD(&ctx->negoex_mechs, mech, links);725}726727OM_uint32728negoex_add_auth_mech(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,729gss_const_OID oid, auth_scheme scheme)730{731OM_uint32 major;732struct negoex_auth_mech *mech;733734mech = calloc(1, sizeof(*mech));735if (mech == NULL) {736*minor = ENOMEM;737return GSS_S_FAILURE;738}739740major = generic_gss_copy_oid(minor, (gss_OID)oid, &mech->oid);741if (major != GSS_S_COMPLETE) {742free(mech);743return major;744}745746memcpy(mech->scheme, scheme, GUID_LENGTH);747748K5_TAILQ_INSERT_TAIL(&ctx->negoex_mechs, mech, links);749750*minor = 0;751return GSS_S_COMPLETE;752}753754struct negoex_auth_mech *755negoex_locate_auth_scheme(spnego_gss_ctx_id_t ctx, const auth_scheme scheme)756{757struct negoex_auth_mech *mech;758759K5_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links) {760if (GUID_EQ(mech->scheme, scheme))761return mech;762}763764return NULL;765}766767/* Prune ctx->mechs to the schemes present in schemes, and reorder them to768* match its order. */769void770negoex_common_auth_schemes(spnego_gss_ctx_id_t ctx,771const uint8_t *schemes, uint16_t nschemes)772{773struct negoex_mech_list list;774struct negoex_auth_mech *mech;775uint16_t i;776777/* Construct a new list in the order of schemes. */778K5_TAILQ_INIT(&list);779for (i = 0; i < nschemes; i++) {780mech = negoex_locate_auth_scheme(ctx, schemes + i * GUID_LENGTH);781if (mech == NULL)782continue;783K5_TAILQ_REMOVE(&ctx->negoex_mechs, mech, links);784K5_TAILQ_INSERT_TAIL(&list, mech, links);785}786787/* Release any leftover entries and replace the context list. */788release_all_mechs(ctx);789K5_TAILQ_CONCAT(&ctx->negoex_mechs, &list, links);790}791792/* Prune ctx->mechs to the schemes present in schemes, but do not change793* their order. */794void795negoex_restrict_auth_schemes(spnego_gss_ctx_id_t ctx,796const uint8_t *schemes, uint16_t nschemes)797{798struct negoex_auth_mech *mech, *next;799uint16_t i;800int found;801802K5_TAILQ_FOREACH_SAFE(mech, &ctx->negoex_mechs, links, next) {803found = FALSE;804for (i = 0; i < nschemes && !found; i++) {805if (GUID_EQ(mech->scheme, schemes + i * GUID_LENGTH))806found = TRUE;807}808809if (!found)810negoex_delete_auth_mech(ctx, mech);811}812}813814815