Blame src/lib/gssapi/spnego/negoex_ctx.c

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,
rpm-build ec7300
          gss_channel_bindings_t bindings, gss_buffer_t output_token,
rpm-build ec7300
          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,
rpm-build ec7300
                                     mech->oid, req_flags, time_req, bindings,
rpm-build ec7300
                                     input_token, &ctx->actual_mech,
rpm-build ec7300
                                     output_token, &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,
rpm-build ec7300
            size_t nmessages, gss_channel_bindings_t bindings,
rpm-build ec7300
            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,
rpm-build ec7300
                                   &msg->token, bindings, &ctx->internal_name,
rpm-build ec7300
                                   &ctx->actual_mech, output_token,
rpm-build ec7300
                                   &ctx->ctx_flags, time_rec,
rpm-build ec7300
                                   &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,
rpm-build ec7300
            gss_buffer_t input_token, gss_channel_bindings_t bindings,
rpm-build ec7300
            gss_buffer_t output_token, 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,
rpm-build ec7300
                      messages, nmessages, bindings, &mech_output_token,
rpm-build ec7300
                      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,
rpm-build ec7300
              gss_buffer_t input_token, gss_channel_bindings_t bindings,
rpm-build ec7300
              gss_buffer_t output_token, 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
     */
rpm-build ec7300
    major = mech_accept(minor, ctx, cred, messages, nmessages, bindings,
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
}