|
Packit |
fd8b60 |
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
Packit |
fd8b60 |
/*
|
|
Packit |
fd8b60 |
* Copyright (C) 2011-2018 PADL Software Pty Ltd.
|
|
Packit |
fd8b60 |
* All rights reserved.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* Redistribution and use in source and binary forms, with or without
|
|
Packit |
fd8b60 |
* modification, are permitted provided that the following conditions
|
|
Packit |
fd8b60 |
* are met:
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* * Redistributions of source code must retain the above copyright
|
|
Packit |
fd8b60 |
* notice, this list of conditions and the following disclaimer.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* * Redistributions in binary form must reproduce the above copyright
|
|
Packit |
fd8b60 |
* notice, this list of conditions and the following disclaimer in
|
|
Packit |
fd8b60 |
* the documentation and/or other materials provided with the
|
|
Packit |
fd8b60 |
* distribution.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
Packit |
fd8b60 |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
Packit |
fd8b60 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
Packit |
fd8b60 |
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
Packit |
fd8b60 |
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
Packit |
fd8b60 |
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
Packit |
fd8b60 |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
Packit |
fd8b60 |
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
Packit |
fd8b60 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
Packit |
fd8b60 |
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
Packit |
fd8b60 |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
Packit |
fd8b60 |
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
Packit |
fd8b60 |
*/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
#include "k5-platform.h"
|
|
Packit |
fd8b60 |
#include "gssapiP_spnego.h"
|
|
Packit |
fd8b60 |
#include <generic/gssapiP_generic.h>
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*
|
|
Packit |
fd8b60 |
* The initial context token emitted by the initiator is a INITIATOR_NEGO
|
|
Packit |
fd8b60 |
* message followed by zero or more INITIATOR_META_DATA tokens, and zero
|
|
Packit |
fd8b60 |
* or one AP_REQUEST tokens.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* Upon receiving this, the acceptor computes the list of mutually supported
|
|
Packit |
fd8b60 |
* authentication mechanisms and performs the metadata exchange. The output
|
|
Packit |
fd8b60 |
* token is ACCEPTOR_NEGO followed by zero or more ACCEPTOR_META_DATA tokens,
|
|
Packit |
fd8b60 |
* and zero or one CHALLENGE tokens.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* Once the metadata exchange is complete and a mechanism is selected, the
|
|
Packit |
fd8b60 |
* selected mechanism's context token exchange continues with AP_REQUEST and
|
|
Packit |
fd8b60 |
* CHALLENGE messages.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* Once the context token exchange is complete, VERIFY messages are sent to
|
|
Packit |
fd8b60 |
* authenticate the entire exchange.
|
|
Packit |
fd8b60 |
*/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static void
|
|
Packit |
fd8b60 |
zero_and_release_buffer_set(gss_buffer_set_t *pbuffers)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
OM_uint32 tmpmin;
|
|
Packit |
fd8b60 |
gss_buffer_set_t buffers = *pbuffers;
|
|
Packit |
fd8b60 |
uint32_t i;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (buffers != GSS_C_NO_BUFFER_SET) {
|
|
Packit |
fd8b60 |
for (i = 0; i < buffers->count; i++)
|
|
Packit |
fd8b60 |
zap(buffers->elements[i].value, buffers->elements[i].length);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
gss_release_buffer_set(&tmpmin, &buffers);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*pbuffers = GSS_C_NO_BUFFER_SET;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static OM_uint32
|
|
Packit |
fd8b60 |
buffer_set_to_key(OM_uint32 *minor, gss_buffer_set_t buffers,
|
|
Packit |
fd8b60 |
krb5_keyblock *key)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
krb5_error_code ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Returned keys must be in two buffers, with the key contents in the first
|
|
Packit |
fd8b60 |
* and the enctype as a 32-bit little-endian integer in the second. */
|
|
Packit |
fd8b60 |
if (buffers->count != 2 || buffers->elements[1].length != 4) {
|
|
Packit |
fd8b60 |
*minor = ERR_NEGOEX_NO_VERIFY_KEY;
|
|
Packit |
fd8b60 |
return GSS_S_FAILURE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
krb5_free_keyblock_contents(NULL, key);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
key->contents = k5memdup(buffers->elements[0].value,
|
|
Packit |
fd8b60 |
buffers->elements[0].length, &ret;;
|
|
Packit |
fd8b60 |
if (key->contents == NULL) {
|
|
Packit |
fd8b60 |
*minor = ret;
|
|
Packit |
fd8b60 |
return GSS_S_FAILURE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
key->length = buffers->elements[0].length;
|
|
Packit |
fd8b60 |
key->enctype = load_32_le(buffers->elements[1].value);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static OM_uint32
|
|
Packit |
fd8b60 |
get_session_keys(OM_uint32 *minor, struct negoex_auth_mech *mech)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
OM_uint32 major, tmpmin;
|
|
Packit |
fd8b60 |
gss_buffer_set_t buffers = GSS_C_NO_BUFFER_SET;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = gss_inquire_sec_context_by_oid(&tmpmin, mech->mech_context,
|
|
Packit |
fd8b60 |
GSS_C_INQ_NEGOEX_KEY, &buffers);
|
|
Packit |
fd8b60 |
if (major == GSS_S_COMPLETE) {
|
|
Packit |
fd8b60 |
major = buffer_set_to_key(minor, buffers, &mech->key);
|
|
Packit |
fd8b60 |
zero_and_release_buffer_set(&buffers);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
return major;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = gss_inquire_sec_context_by_oid(&tmpmin, mech->mech_context,
|
|
Packit |
fd8b60 |
GSS_C_INQ_NEGOEX_VERIFY_KEY,
|
|
Packit |
fd8b60 |
&buffers);
|
|
Packit |
fd8b60 |
if (major == GSS_S_COMPLETE) {
|
|
Packit |
fd8b60 |
major = buffer_set_to_key(minor, buffers, &mech->verify_key);
|
|
Packit |
fd8b60 |
zero_and_release_buffer_set(&buffers);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
return major;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static OM_uint32
|
|
Packit |
fd8b60 |
emit_initiator_nego(OM_uint32 *minor, spnego_gss_ctx_id_t ctx)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
OM_uint32 major;
|
|
Packit |
fd8b60 |
uint8_t random[32];
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = negoex_random(minor, ctx, random, 32);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
return major;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
negoex_add_nego_message(ctx, INITIATOR_NEGO, random);
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static OM_uint32
|
|
Packit |
fd8b60 |
process_initiator_nego(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
|
|
Packit |
fd8b60 |
struct negoex_message *messages, size_t nmessages)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct nego_message *msg;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
assert(!ctx->initiate && ctx->negoex_step == 1);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
msg = negoex_locate_nego_message(messages, nmessages, INITIATOR_NEGO);
|
|
Packit |
fd8b60 |
if (msg == NULL) {
|
|
Packit |
fd8b60 |
*minor = ERR_NEGOEX_MISSING_NEGO_MESSAGE;
|
|
Packit |
fd8b60 |
return GSS_S_DEFECTIVE_TOKEN;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
negoex_restrict_auth_schemes(ctx, msg->schemes, msg->nschemes);
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static OM_uint32
|
|
Packit |
fd8b60 |
emit_acceptor_nego(OM_uint32 *minor, spnego_gss_ctx_id_t ctx)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
OM_uint32 major;
|
|
Packit |
fd8b60 |
uint8_t random[32];
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = negoex_random(minor, ctx, random, 32);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
return major;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
negoex_add_nego_message(ctx, ACCEPTOR_NEGO, random);
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static OM_uint32
|
|
Packit |
fd8b60 |
process_acceptor_nego(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
|
|
Packit |
fd8b60 |
struct negoex_message *messages, size_t nmessages)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct nego_message *msg;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
msg = negoex_locate_nego_message(messages, nmessages, ACCEPTOR_NEGO);
|
|
Packit |
fd8b60 |
if (msg == NULL) {
|
|
Packit |
fd8b60 |
*minor = ERR_NEGOEX_MISSING_NEGO_MESSAGE;
|
|
Packit |
fd8b60 |
return GSS_S_DEFECTIVE_TOKEN;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Reorder and prune our mech list to match the acceptor's list (or a
|
|
Packit |
fd8b60 |
* subset of it). */
|
|
Packit |
fd8b60 |
negoex_common_auth_schemes(ctx, msg->schemes, msg->nschemes);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static void
|
|
Packit |
fd8b60 |
query_meta_data(spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
|
|
Packit |
fd8b60 |
gss_name_t target, OM_uint32 req_flags)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
OM_uint32 major, minor;
|
|
Packit |
fd8b60 |
struct negoex_auth_mech *p, *next;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
K5_TAILQ_FOREACH_SAFE(p, &ctx->negoex_mechs, links, next) {
|
|
Packit |
fd8b60 |
major = gssspi_query_meta_data(&minor, p->oid, cred, &p->mech_context,
|
|
Packit |
fd8b60 |
target, req_flags, &p->metadata);
|
|
Packit |
fd8b60 |
/* GSS_Query_meta_data failure removes mechanism from list. */
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
negoex_delete_auth_mech(ctx, p);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static void
|
|
Packit |
fd8b60 |
exchange_meta_data(spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
|
|
Packit |
fd8b60 |
gss_name_t target, OM_uint32 req_flags,
|
|
Packit |
fd8b60 |
struct negoex_message *messages, size_t nmessages)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
OM_uint32 major, minor;
|
|
Packit |
fd8b60 |
struct negoex_auth_mech *mech;
|
|
Packit |
fd8b60 |
enum message_type type;
|
|
Packit |
fd8b60 |
struct exchange_message *msg;
|
|
Packit |
fd8b60 |
uint32_t i;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
type = ctx->initiate ? ACCEPTOR_META_DATA : INITIATOR_META_DATA;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
for (i = 0; i < nmessages; i++) {
|
|
Packit |
fd8b60 |
if (messages[i].type != type)
|
|
Packit |
fd8b60 |
continue;
|
|
Packit |
fd8b60 |
msg = &messages[i].u.e;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
mech = negoex_locate_auth_scheme(ctx, msg->scheme);
|
|
Packit |
fd8b60 |
if (mech == NULL)
|
|
Packit |
fd8b60 |
continue;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = gssspi_exchange_meta_data(&minor, mech->oid, cred,
|
|
Packit |
fd8b60 |
&mech->mech_context, target,
|
|
Packit |
fd8b60 |
req_flags, &msg->token);
|
|
Packit |
fd8b60 |
/* GSS_Exchange_meta_data failure removes mechanism from list. */
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
negoex_delete_auth_mech(ctx, mech);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*
|
|
Packit |
fd8b60 |
* In the initiator, if we are processing the acceptor's first reply, discard
|
|
Packit |
fd8b60 |
* the optimistic context if the acceptor ignored the optimistic token. If the
|
|
Packit |
fd8b60 |
* acceptor continued the optimistic mech, discard all other mechs.
|
|
Packit |
fd8b60 |
*/
|
|
Packit |
fd8b60 |
static void
|
|
Packit |
fd8b60 |
check_optimistic_result(spnego_gss_ctx_id_t ctx,
|
|
Packit |
fd8b60 |
struct negoex_message *messages, size_t nmessages)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct negoex_auth_mech *mech;
|
|
Packit |
fd8b60 |
OM_uint32 tmpmin;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
assert(ctx->initiate && ctx->negoex_step == 2);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Do nothing if we didn't make an optimistic context. */
|
|
Packit |
fd8b60 |
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
|
|
Packit |
fd8b60 |
if (mech == NULL || mech->mech_context == GSS_C_NO_CONTEXT)
|
|
Packit |
fd8b60 |
return;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* If the acceptor used the optimistic token, it will send an acceptor
|
|
Packit |
fd8b60 |
* token or a checksum (or both) in its first reply. */
|
|
Packit |
fd8b60 |
if (negoex_locate_exchange_message(messages, nmessages,
|
|
Packit |
fd8b60 |
CHALLENGE) != NULL ||
|
|
Packit |
fd8b60 |
negoex_locate_verify_message(messages, nmessages) != NULL) {
|
|
Packit |
fd8b60 |
/* The acceptor continued the optimistic mech, and metadata exchange
|
|
Packit |
fd8b60 |
* didn't remove it. Commit to this mechanism. */
|
|
Packit |
fd8b60 |
negoex_select_auth_mech(ctx, mech);
|
|
Packit |
fd8b60 |
} else {
|
|
Packit |
fd8b60 |
/* The acceptor ignored the optimistic token. Restart the mech. */
|
|
Packit |
fd8b60 |
(void)gss_delete_sec_context(&tmpmin, &mech->mech_context, NULL);
|
|
Packit |
fd8b60 |
krb5_free_keyblock_contents(NULL, &mech->key);
|
|
Packit |
fd8b60 |
krb5_free_keyblock_contents(NULL, &mech->verify_key);
|
|
Packit |
fd8b60 |
mech->complete = mech->sent_checksum = FALSE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Perform an initiator step of the underlying mechanism exchange. */
|
|
Packit |
fd8b60 |
static OM_uint32
|
|
Packit |
fd8b60 |
mech_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
|
|
Packit |
fd8b60 |
gss_name_t target, OM_uint32 req_flags, OM_uint32 time_req,
|
|
Packit |
fd8b60 |
struct negoex_message *messages, size_t nmessages,
|
|
Packit |
fd8b60 |
gss_buffer_t output_token, OM_uint32 *time_rec)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
OM_uint32 major, first_major = 0, first_minor = 0;
|
|
Packit |
fd8b60 |
struct negoex_auth_mech *mech = NULL;
|
|
Packit |
fd8b60 |
gss_buffer_t input_token = GSS_C_NO_BUFFER;
|
|
Packit |
fd8b60 |
struct exchange_message *msg;
|
|
Packit |
fd8b60 |
int first_mech;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
output_token->value = NULL;
|
|
Packit |
fd8b60 |
output_token->length = 0;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Allow disabling of optimistic token for testing. */
|
|
Packit |
fd8b60 |
if (ctx->negoex_step == 1 &&
|
|
Packit |
fd8b60 |
secure_getenv("NEGOEX_NO_OPTIMISTIC_TOKEN") != NULL)
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (K5_TAILQ_EMPTY(&ctx->negoex_mechs)) {
|
|
Packit |
fd8b60 |
*minor = ERR_NEGOEX_NO_AVAILABLE_MECHS;
|
|
Packit |
fd8b60 |
return GSS_S_FAILURE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*
|
|
Packit |
fd8b60 |
* Get the input token. The challenge could be for the optimistic mech,
|
|
Packit |
fd8b60 |
* which we might have discarded in metadata exchange, so ignore the
|
|
Packit |
fd8b60 |
* challenge if it doesn't match the first auth mech.
|
|
Packit |
fd8b60 |
*/
|
|
Packit |
fd8b60 |
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
|
|
Packit |
fd8b60 |
msg = negoex_locate_exchange_message(messages, nmessages, CHALLENGE);
|
|
Packit |
fd8b60 |
if (msg != NULL && GUID_EQ(msg->scheme, mech->scheme))
|
|
Packit |
fd8b60 |
input_token = &msg->token;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (mech->complete)
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
first_mech = TRUE;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
while (!K5_TAILQ_EMPTY(&ctx->negoex_mechs)) {
|
|
Packit |
fd8b60 |
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = gss_init_sec_context(minor, cred, &mech->mech_context, target,
|
|
Packit |
fd8b60 |
mech->oid, req_flags, time_req,
|
|
Packit |
fd8b60 |
GSS_C_NO_CHANNEL_BINDINGS, input_token,
|
|
Packit |
fd8b60 |
&ctx->actual_mech, output_token,
|
|
Packit |
fd8b60 |
&ctx->ctx_flags, time_rec);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (major == GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
mech->complete = 1;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (!GSS_ERROR(major))
|
|
Packit |
fd8b60 |
return get_session_keys(minor, mech);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Remember the error we got from the first mech. */
|
|
Packit |
fd8b60 |
if (first_mech) {
|
|
Packit |
fd8b60 |
first_major = major;
|
|
Packit |
fd8b60 |
first_minor = *minor;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* If we still have multiple mechs to try, move on to the next one. */
|
|
Packit |
fd8b60 |
negoex_delete_auth_mech(ctx, mech);
|
|
Packit |
fd8b60 |
first_mech = FALSE;
|
|
Packit |
fd8b60 |
input_token = GSS_C_NO_BUFFER;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (K5_TAILQ_EMPTY(&ctx->negoex_mechs)) {
|
|
Packit |
fd8b60 |
major = first_major;
|
|
Packit |
fd8b60 |
*minor = first_minor;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return major;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Perform an acceptor step of the underlying mechanism exchange. */
|
|
Packit |
fd8b60 |
static OM_uint32
|
|
Packit |
fd8b60 |
mech_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
|
|
Packit |
fd8b60 |
gss_cred_id_t cred, struct negoex_message *messages,
|
|
Packit |
fd8b60 |
size_t nmessages, gss_buffer_t output_token, OM_uint32 *time_rec)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
OM_uint32 major, tmpmin;
|
|
Packit |
fd8b60 |
struct negoex_auth_mech *mech;
|
|
Packit |
fd8b60 |
struct exchange_message *msg;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
assert(!ctx->initiate && !K5_TAILQ_EMPTY(&ctx->negoex_mechs));
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
msg = negoex_locate_exchange_message(messages, nmessages, AP_REQUEST);
|
|
Packit |
fd8b60 |
if (msg == NULL) {
|
|
Packit |
fd8b60 |
/* No input token is okay on the first request or if the mech is
|
|
Packit |
fd8b60 |
* complete. */
|
|
Packit |
fd8b60 |
if (ctx->negoex_step == 1 ||
|
|
Packit |
fd8b60 |
K5_TAILQ_FIRST(&ctx->negoex_mechs)->complete)
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
*minor = ERR_NEGOEX_MISSING_AP_REQUEST_MESSAGE;
|
|
Packit |
fd8b60 |
return GSS_S_DEFECTIVE_TOKEN;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (ctx->negoex_step == 1) {
|
|
Packit |
fd8b60 |
/* Ignore the optimistic token if it isn't for our most preferred
|
|
Packit |
fd8b60 |
* mech. */
|
|
Packit |
fd8b60 |
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
|
|
Packit |
fd8b60 |
if (!GUID_EQ(msg->scheme, mech->scheme))
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
} else {
|
|
Packit |
fd8b60 |
/* The initiator has selected a mech; discard other entries. */
|
|
Packit |
fd8b60 |
mech = negoex_locate_auth_scheme(ctx, msg->scheme);
|
|
Packit |
fd8b60 |
if (mech == NULL) {
|
|
Packit |
fd8b60 |
*minor = ERR_NEGOEX_NO_AVAILABLE_MECHS;
|
|
Packit |
fd8b60 |
return GSS_S_FAILURE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
negoex_select_auth_mech(ctx, mech);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (mech->complete)
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (ctx->internal_name != GSS_C_NO_NAME)
|
|
Packit |
fd8b60 |
gss_release_name(&tmpmin, &ctx->internal_name);
|
|
Packit |
fd8b60 |
if (ctx->deleg_cred != GSS_C_NO_CREDENTIAL)
|
|
Packit |
fd8b60 |
gss_release_cred(&tmpmin, &ctx->deleg_cred);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = gss_accept_sec_context(minor, &mech->mech_context, cred,
|
|
Packit |
fd8b60 |
&msg->token, GSS_C_NO_CHANNEL_BINDINGS,
|
|
Packit |
fd8b60 |
&ctx->internal_name, &ctx->actual_mech,
|
|
Packit |
fd8b60 |
output_token, &ctx->ctx_flags,
|
|
Packit |
fd8b60 |
time_rec, &ctx->deleg_cred);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (major == GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
mech->complete = 1;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (!GSS_ERROR(major)) {
|
|
Packit |
fd8b60 |
major = get_session_keys(minor, mech);
|
|
Packit |
fd8b60 |
} else if (ctx->negoex_step == 1) {
|
|
Packit |
fd8b60 |
/* This was an optimistic token; pretend this never happened. */
|
|
Packit |
fd8b60 |
major = GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
*minor = 0;
|
|
Packit |
fd8b60 |
gss_release_buffer(&tmpmin, output_token);
|
|
Packit |
fd8b60 |
gss_delete_sec_context(&tmpmin, &mech->mech_context, GSS_C_NO_BUFFER);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return major;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static krb5_keyusage
|
|
Packit |
fd8b60 |
verify_keyusage(spnego_gss_ctx_id_t ctx, int make_checksum)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
/* Of course, these are the wrong way around in the spec. */
|
|
Packit |
fd8b60 |
return (ctx->initiate ^ !make_checksum) ?
|
|
Packit |
fd8b60 |
NEGOEX_KEYUSAGE_ACCEPTOR_CHECKSUM : NEGOEX_KEYUSAGE_INITIATOR_CHECKSUM;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static OM_uint32
|
|
Packit |
fd8b60 |
verify_checksum(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
|
|
Packit |
fd8b60 |
struct negoex_message *messages, size_t nmessages,
|
|
Packit |
fd8b60 |
gss_buffer_t input_token, int *send_alert_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
krb5_error_code ret;
|
|
Packit |
fd8b60 |
struct negoex_auth_mech *mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
|
|
Packit |
fd8b60 |
struct verify_message *msg;
|
|
Packit |
fd8b60 |
krb5_crypto_iov iov[3];
|
|
Packit |
fd8b60 |
krb5_keyusage usage = verify_keyusage(ctx, FALSE);
|
|
Packit |
fd8b60 |
krb5_boolean valid;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*send_alert_out = FALSE;
|
|
Packit |
fd8b60 |
assert(mech != NULL);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* The other party may not be ready to send a verify token yet, or (in the
|
|
Packit |
fd8b60 |
* first initiator step) may send one for a mechanism we don't support. */
|
|
Packit |
fd8b60 |
msg = negoex_locate_verify_message(messages, nmessages);
|
|
Packit |
fd8b60 |
if (msg == NULL || !GUID_EQ(msg->scheme, mech->scheme))
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* A recoverable error may cause us to be unable to verify a token from the
|
|
Packit |
fd8b60 |
* other party. In this case we should send an alert. */
|
|
Packit |
fd8b60 |
if (mech->verify_key.enctype == ENCTYPE_NULL) {
|
|
Packit |
fd8b60 |
*send_alert_out = TRUE;
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Verify the checksum over the existing transcript and the portion of the
|
|
Packit |
fd8b60 |
* input token leading up to the verify message. */
|
|
Packit |
fd8b60 |
assert(input_token != NULL);
|
|
Packit |
fd8b60 |
iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
|
|
Packit |
fd8b60 |
iov[0].data = make_data(ctx->negoex_transcript.data,
|
|
Packit |
fd8b60 |
ctx->negoex_transcript.len);
|
|
Packit |
fd8b60 |
iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
|
|
Packit |
fd8b60 |
iov[1].data = make_data(input_token->value, msg->offset_in_token);
|
|
Packit |
fd8b60 |
iov[2].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
|
|
Packit |
fd8b60 |
iov[2].data = make_data((uint8_t *)msg->cksum, msg->cksum_len);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
ret = krb5_c_verify_checksum_iov(ctx->kctx, msg->cksum_type,
|
|
Packit |
fd8b60 |
&mech->verify_key, usage, iov, 3, &valid);
|
|
Packit |
fd8b60 |
if (ret) {
|
|
Packit |
fd8b60 |
*minor = ret;
|
|
Packit |
fd8b60 |
return GSS_S_FAILURE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
if (!valid || !krb5_c_is_keyed_cksum(msg->cksum_type)) {
|
|
Packit |
fd8b60 |
*minor = ERR_NEGOEX_INVALID_CHECKSUM;
|
|
Packit |
fd8b60 |
return GSS_S_BAD_SIG;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
mech->verified_checksum = TRUE;
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static OM_uint32
|
|
Packit |
fd8b60 |
make_checksum(OM_uint32 *minor, spnego_gss_ctx_id_t ctx)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
krb5_error_code ret;
|
|
Packit |
fd8b60 |
krb5_data d;
|
|
Packit |
fd8b60 |
krb5_keyusage usage = verify_keyusage(ctx, TRUE);
|
|
Packit |
fd8b60 |
krb5_checksum cksum;
|
|
Packit |
fd8b60 |
struct negoex_auth_mech *mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
assert(mech != NULL);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (mech->key.enctype == ENCTYPE_NULL) {
|
|
Packit |
fd8b60 |
if (mech->complete) {
|
|
Packit |
fd8b60 |
*minor = ERR_NEGOEX_NO_VERIFY_KEY;
|
|
Packit |
fd8b60 |
return GSS_S_UNAVAILABLE;
|
|
Packit |
fd8b60 |
} else {
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
d = make_data(ctx->negoex_transcript.data, ctx->negoex_transcript.len);
|
|
Packit |
fd8b60 |
ret = krb5_c_make_checksum(ctx->kctx, 0, &mech->key, usage, &d, &cksum);
|
|
Packit |
fd8b60 |
if (ret) {
|
|
Packit |
fd8b60 |
*minor = ret;
|
|
Packit |
fd8b60 |
return GSS_S_FAILURE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
negoex_add_verify_message(ctx, mech->scheme, cksum.checksum_type,
|
|
Packit |
fd8b60 |
cksum.contents, cksum.length);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
mech->sent_checksum = TRUE;
|
|
Packit |
fd8b60 |
krb5_free_checksum_contents(ctx->kctx, &cksum);
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* If the other side sent a VERIFY_NO_KEY pulse alert, clear the checksum state
|
|
Packit |
fd8b60 |
* on the mechanism so that we send another VERIFY message. */
|
|
Packit |
fd8b60 |
static void
|
|
Packit |
fd8b60 |
process_alerts(spnego_gss_ctx_id_t ctx,
|
|
Packit |
fd8b60 |
struct negoex_message *messages, uint32_t nmessages)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct alert_message *msg;
|
|
Packit |
fd8b60 |
struct negoex_auth_mech *mech;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
msg = negoex_locate_alert_message(messages, nmessages);
|
|
Packit |
fd8b60 |
if (msg != NULL && msg->verify_no_key) {
|
|
Packit |
fd8b60 |
mech = negoex_locate_auth_scheme(ctx, msg->scheme);
|
|
Packit |
fd8b60 |
if (mech != NULL) {
|
|
Packit |
fd8b60 |
mech->sent_checksum = FALSE;
|
|
Packit |
fd8b60 |
krb5_free_keyblock_contents(NULL, &mech->key);
|
|
Packit |
fd8b60 |
krb5_free_keyblock_contents(NULL, &mech->verify_key);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static OM_uint32
|
|
Packit |
fd8b60 |
make_output_token(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
|
|
Packit |
fd8b60 |
gss_buffer_t mech_output_token, int send_alert,
|
|
Packit |
fd8b60 |
gss_buffer_t output_token)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
OM_uint32 major;
|
|
Packit |
fd8b60 |
struct negoex_auth_mech *mech;
|
|
Packit |
fd8b60 |
enum message_type type;
|
|
Packit |
fd8b60 |
size_t old_transcript_len = ctx->negoex_transcript.len;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
output_token->length = 0;
|
|
Packit |
fd8b60 |
output_token->value = NULL;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* If the mech is complete and we previously sent a checksum, we just
|
|
Packit |
fd8b60 |
* processed the last leg and don't need to send another token. */
|
|
Packit |
fd8b60 |
if (mech_output_token->length == 0 &&
|
|
Packit |
fd8b60 |
K5_TAILQ_FIRST(&ctx->negoex_mechs)->sent_checksum)
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (ctx->negoex_step == 1) {
|
|
Packit |
fd8b60 |
if (ctx->initiate)
|
|
Packit |
fd8b60 |
major = emit_initiator_nego(minor, ctx);
|
|
Packit |
fd8b60 |
else
|
|
Packit |
fd8b60 |
major = emit_acceptor_nego(minor, ctx);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
return major;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
type = ctx->initiate ? INITIATOR_META_DATA : ACCEPTOR_META_DATA;
|
|
Packit |
fd8b60 |
K5_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links) {
|
|
Packit |
fd8b60 |
if (mech->metadata.length > 0) {
|
|
Packit |
fd8b60 |
negoex_add_exchange_message(ctx, type, mech->scheme,
|
|
Packit |
fd8b60 |
&mech->metadata);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (mech_output_token->length > 0) {
|
|
Packit |
fd8b60 |
type = ctx->initiate ? AP_REQUEST : CHALLENGE;
|
|
Packit |
fd8b60 |
negoex_add_exchange_message(ctx, type, mech->scheme,
|
|
Packit |
fd8b60 |
mech_output_token);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (send_alert)
|
|
Packit |
fd8b60 |
negoex_add_verify_no_key_alert(ctx, mech->scheme);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Try to add a VERIFY message if we haven't already done so. */
|
|
Packit |
fd8b60 |
if (!mech->sent_checksum) {
|
|
Packit |
fd8b60 |
major = make_checksum(minor, ctx);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
return major;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (ctx->negoex_transcript.data == NULL) {
|
|
Packit |
fd8b60 |
*minor = ENOMEM;
|
|
Packit |
fd8b60 |
return GSS_S_FAILURE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Copy what we added to the transcript into the output token. */
|
|
Packit |
fd8b60 |
output_token->length = ctx->negoex_transcript.len - old_transcript_len;
|
|
Packit |
fd8b60 |
output_token->value = gssalloc_malloc(output_token->length);
|
|
Packit |
fd8b60 |
if (output_token->value == NULL) {
|
|
Packit |
fd8b60 |
*minor = ENOMEM;
|
|
Packit |
fd8b60 |
return GSS_S_FAILURE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
memcpy(output_token->value,
|
|
Packit |
fd8b60 |
(uint8_t *)ctx->negoex_transcript.data + old_transcript_len,
|
|
Packit |
fd8b60 |
output_token->length);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return GSS_S_COMPLETE;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
OM_uint32
|
|
Packit |
fd8b60 |
negoex_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
|
|
Packit |
fd8b60 |
gss_name_t target_name, OM_uint32 req_flags, OM_uint32 time_req,
|
|
Packit |
fd8b60 |
gss_buffer_t input_token, gss_buffer_t output_token,
|
|
Packit |
fd8b60 |
OM_uint32 *time_rec)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
OM_uint32 major, tmpmin;
|
|
Packit |
fd8b60 |
gss_buffer_desc mech_output_token = GSS_C_EMPTY_BUFFER;
|
|
Packit |
fd8b60 |
struct negoex_message *messages = NULL;
|
|
Packit |
fd8b60 |
struct negoex_auth_mech *mech;
|
|
Packit |
fd8b60 |
size_t nmessages = 0;
|
|
Packit |
fd8b60 |
int send_alert = FALSE;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (ctx->negoex_step == 0 && input_token != GSS_C_NO_BUFFER &&
|
|
Packit |
fd8b60 |
input_token->length != 0)
|
|
Packit |
fd8b60 |
return GSS_S_DEFECTIVE_TOKEN;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = negoex_prep_context_for_negoex(minor, ctx);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
ctx->negoex_step++;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (input_token != GSS_C_NO_BUFFER && input_token->length > 0) {
|
|
Packit |
fd8b60 |
major = negoex_parse_token(minor, ctx, input_token, &messages,
|
|
Packit |
fd8b60 |
&nmessages);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
process_alerts(ctx, messages, nmessages);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (ctx->negoex_step == 1) {
|
|
Packit |
fd8b60 |
/* Choose a random conversation ID. */
|
|
Packit |
fd8b60 |
major = negoex_random(minor, ctx, ctx->negoex_conv_id, GUID_LENGTH);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Query each mech for its metadata (this may prune the mech list). */
|
|
Packit |
fd8b60 |
query_meta_data(ctx, cred, target_name, req_flags);
|
|
Packit |
fd8b60 |
} else if (ctx->negoex_step == 2) {
|
|
Packit |
fd8b60 |
/* See if the mech processed the optimistic token. */
|
|
Packit |
fd8b60 |
check_optimistic_result(ctx, messages, nmessages);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Pass the acceptor metadata to each mech to prune the list. */
|
|
Packit |
fd8b60 |
exchange_meta_data(ctx, cred, target_name, req_flags,
|
|
Packit |
fd8b60 |
messages, nmessages);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Process the ACCEPTOR_NEGO message. */
|
|
Packit |
fd8b60 |
major = process_acceptor_nego(minor, ctx, messages, nmessages);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Process the input token and/or produce an output token. This may prune
|
|
Packit |
fd8b60 |
* the mech list, but on success there will be at least one mech entry. */
|
|
Packit |
fd8b60 |
major = mech_init(minor, ctx, cred, target_name, req_flags, time_req,
|
|
Packit |
fd8b60 |
messages, nmessages, &mech_output_token, time_rec);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
assert(!K5_TAILQ_EMPTY(&ctx->negoex_mechs));
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* At this point in step 2 we have performed the metadata exchange and
|
|
Packit |
fd8b60 |
* chosen a mech we can use, so discard any fallback mech entries. */
|
|
Packit |
fd8b60 |
if (ctx->negoex_step == 2)
|
|
Packit |
fd8b60 |
negoex_select_auth_mech(ctx, K5_TAILQ_FIRST(&ctx->negoex_mechs));
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = verify_checksum(minor, ctx, messages, nmessages, input_token,
|
|
Packit |
fd8b60 |
&send_alert);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (input_token != GSS_C_NO_BUFFER) {
|
|
Packit |
fd8b60 |
k5_buf_add_len(&ctx->negoex_transcript, input_token->value,
|
|
Packit |
fd8b60 |
input_token->length);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = make_output_token(minor, ctx, &mech_output_token, send_alert,
|
|
Packit |
fd8b60 |
output_token);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
|
|
Packit |
fd8b60 |
major = (mech->complete && mech->verified_checksum) ? GSS_S_COMPLETE :
|
|
Packit |
fd8b60 |
GSS_S_CONTINUE_NEEDED;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
cleanup:
|
|
Packit |
fd8b60 |
free(messages);
|
|
Packit |
fd8b60 |
gss_release_buffer(&tmpmin, &mech_output_token);
|
|
Packit |
fd8b60 |
negoex_prep_context_for_spnego(ctx);
|
|
Packit |
fd8b60 |
return major;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
OM_uint32
|
|
Packit |
fd8b60 |
negoex_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
|
|
Packit |
fd8b60 |
gss_buffer_t input_token, gss_buffer_t output_token,
|
|
Packit |
fd8b60 |
OM_uint32 *time_rec)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
OM_uint32 major, tmpmin;
|
|
Packit |
fd8b60 |
gss_buffer_desc mech_output_token = GSS_C_EMPTY_BUFFER;
|
|
Packit |
fd8b60 |
struct negoex_message *messages = NULL;
|
|
Packit |
fd8b60 |
struct negoex_auth_mech *mech;
|
|
Packit |
fd8b60 |
size_t nmessages;
|
|
Packit |
fd8b60 |
int send_alert = FALSE;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
|
|
Packit |
fd8b60 |
major = GSS_S_DEFECTIVE_TOKEN;
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = negoex_prep_context_for_negoex(minor, ctx);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
ctx->negoex_step++;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = negoex_parse_token(minor, ctx, input_token, &messages, &nmessages);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
process_alerts(ctx, messages, nmessages);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (ctx->negoex_step == 1) {
|
|
Packit |
fd8b60 |
/* Read the INITIATOR_NEGO message to prune the candidate mech list. */
|
|
Packit |
fd8b60 |
major = process_initiator_nego(minor, ctx, messages, nmessages);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*
|
|
Packit |
fd8b60 |
* Pass the initiator metadata to each mech to prune the list, and
|
|
Packit |
fd8b60 |
* query each mech for its acceptor metadata (which may also prune the
|
|
Packit |
fd8b60 |
* list).
|
|
Packit |
fd8b60 |
*/
|
|
Packit |
fd8b60 |
exchange_meta_data(ctx, cred, GSS_C_NO_NAME, 0, messages, nmessages);
|
|
Packit |
fd8b60 |
query_meta_data(ctx, cred, GSS_C_NO_NAME, 0);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (K5_TAILQ_EMPTY(&ctx->negoex_mechs)) {
|
|
Packit |
fd8b60 |
*minor = ERR_NEGOEX_NO_AVAILABLE_MECHS;
|
|
Packit |
fd8b60 |
major = GSS_S_FAILURE;
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*
|
|
Packit |
fd8b60 |
* Process the input token and possibly produce an output token. This may
|
|
Packit |
fd8b60 |
* prune the list to a single mech. Continue on error if an output token
|
|
Packit |
fd8b60 |
* is generated, so that we send the token to the initiator.
|
|
Packit |
fd8b60 |
*/
|
|
Packit |
fd8b60 |
major = mech_accept(minor, ctx, cred, messages, nmessages,
|
|
Packit |
fd8b60 |
&mech_output_token, time_rec);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE && mech_output_token.length == 0)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (major == GSS_S_COMPLETE) {
|
|
Packit |
fd8b60 |
major = verify_checksum(minor, ctx, messages, nmessages, input_token,
|
|
Packit |
fd8b60 |
&send_alert);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
k5_buf_add_len(&ctx->negoex_transcript,
|
|
Packit |
fd8b60 |
input_token->value, input_token->length);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
major = make_output_token(minor, ctx, &mech_output_token, send_alert,
|
|
Packit |
fd8b60 |
output_token);
|
|
Packit |
fd8b60 |
if (major != GSS_S_COMPLETE)
|
|
Packit |
fd8b60 |
goto cleanup;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
|
|
Packit |
fd8b60 |
major = (mech->complete && mech->verified_checksum) ? GSS_S_COMPLETE :
|
|
Packit |
fd8b60 |
GSS_S_CONTINUE_NEEDED;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
cleanup:
|
|
Packit |
fd8b60 |
free(messages);
|
|
Packit |
fd8b60 |
gss_release_buffer(&tmpmin, &mech_output_token);
|
|
Packit |
fd8b60 |
negoex_prep_context_for_spnego(ctx);
|
|
Packit |
fd8b60 |
return major;
|
|
Packit |
fd8b60 |
}
|