Blame src/plugins/gssapi/negoextest/main.c

Packit fd8b60
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
Packit fd8b60
/* plugins/gssapi/negoextest/main.c - GSS test module for NegoEx */
Packit fd8b60
/*
Packit fd8b60
 * Copyright (C) 2019 by the Massachusetts Institute of Technology.
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-int.h"
Packit fd8b60
#include <gssapi/gssapi.h>
Packit fd8b60
#include <gssapi/gssapi_ext.h>
Packit fd8b60
#include <gssapi/gssapi_alloc.h>
Packit fd8b60
Packit fd8b60
struct test_context {
Packit fd8b60
    int initiator;
Packit fd8b60
    uint8_t hops;               /* hops remaining; 0 means established */
Packit fd8b60
};
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gss_init_sec_context(OM_uint32 *minor_status,
Packit fd8b60
                     gss_cred_id_t claimant_cred_handle,
Packit fd8b60
                     gss_ctx_id_t *context_handle, gss_name_t target_name,
Packit fd8b60
                     gss_OID mech_type, OM_uint32 req_flags,
Packit fd8b60
                     OM_uint32 time_req,
Packit fd8b60
                     gss_channel_bindings_t input_chan_bindings,
Packit fd8b60
                     gss_buffer_t input_token, gss_OID *actual_mech,
Packit fd8b60
                     gss_buffer_t output_token, OM_uint32 *ret_flags,
Packit fd8b60
                     OM_uint32 *time_rec)
Packit fd8b60
{
Packit fd8b60
    struct test_context *ctx = (struct test_context *)*context_handle;
Packit fd8b60
    OM_uint32 major;
Packit fd8b60
    gss_buffer_desc tok;
Packit fd8b60
    const char *envstr;
Packit fd8b60
    uint8_t hops, mech_last_octet;
Packit fd8b60
rpm-build c2b31c
    envstr = getenv("GSS_INIT_BINDING");
rpm-build c2b31c
    if (envstr != NULL) {
rpm-build c2b31c
        assert(strlen(envstr) > 0);
rpm-build c2b31c
        assert(input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS);
rpm-build c2b31c
        assert(strlen(envstr) == input_chan_bindings->application_data.length);
rpm-build c2b31c
        assert(strcmp((char *)input_chan_bindings->application_data.value,
rpm-build c2b31c
                      envstr) == 0);
rpm-build c2b31c
    }
rpm-build c2b31c
Packit fd8b60
    if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
Packit fd8b60
        envstr = getenv("HOPS");
Packit fd8b60
        hops = (envstr != NULL) ? atoi(envstr) : 1;
Packit fd8b60
        assert(hops > 0);
Packit fd8b60
    } else if (input_token->length == 4 &&
Packit fd8b60
               memcmp(input_token->value, "fail", 4) == 0) {
Packit fd8b60
        *minor_status = 12345;
Packit fd8b60
        return GSS_S_FAILURE;
Packit fd8b60
    } else {
Packit fd8b60
        hops = ((uint8_t *)input_token->value)[0];
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    mech_last_octet = ((uint8_t *)mech_type->elements)[mech_type->length - 1];
Packit fd8b60
    envstr = getenv("INIT_FAIL");
Packit fd8b60
    if (envstr != NULL && atoi(envstr) == mech_last_octet)
Packit fd8b60
        return GSS_S_FAILURE;
Packit fd8b60
Packit fd8b60
    if (ctx == NULL) {
Packit fd8b60
        ctx = malloc(sizeof(*ctx));
Packit fd8b60
        assert(ctx != NULL);
Packit fd8b60
        ctx->initiator = 1;
Packit fd8b60
        ctx->hops = hops;
Packit fd8b60
        *context_handle = (gss_ctx_id_t)ctx;
Packit fd8b60
    } else if (ctx != NULL) {
Packit fd8b60
        assert(ctx->initiator);
Packit fd8b60
        ctx->hops--;
Packit fd8b60
        assert(ctx->hops == hops);
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    if (ctx->hops > 0) {
Packit fd8b60
        /* Generate a token containing the remaining hop count. */
Packit fd8b60
        ctx->hops--;
Packit fd8b60
        tok.value = &ctx->hops;
Packit fd8b60
        tok.length = 1;
Packit fd8b60
        major = gss_encapsulate_token(&tok, mech_type, output_token);
Packit fd8b60
        assert(major == GSS_S_COMPLETE);
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    return (ctx->hops > 0) ? GSS_S_CONTINUE_NEEDED : GSS_S_COMPLETE;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gss_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
Packit fd8b60
                       gss_cred_id_t verifier_cred_handle,
Packit fd8b60
                       gss_buffer_t input_token,
Packit fd8b60
                       gss_channel_bindings_t input_chan_bindings,
Packit fd8b60
                       gss_name_t *src_name, gss_OID *mech_type,
Packit fd8b60
                       gss_buffer_t output_token, OM_uint32 *ret_flags,
Packit fd8b60
                       OM_uint32 *time_rec,
Packit fd8b60
                       gss_cred_id_t *delegated_cred_handle)
Packit fd8b60
{
Packit fd8b60
    struct test_context *ctx = (struct test_context *)*context_handle;
Packit fd8b60
    uint8_t hops, mech_last_octet;
Packit fd8b60
    const char *envstr;
Packit fd8b60
rpm-build c2b31c
    envstr = getenv("GSS_ACCEPT_BINDING");
rpm-build c2b31c
    if (envstr != NULL) {
rpm-build c2b31c
        assert(strlen(envstr) > 0);
rpm-build c2b31c
        assert(input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS);
rpm-build c2b31c
        assert(strlen(envstr) == input_chan_bindings->application_data.length);
rpm-build c2b31c
        assert(strcmp((char *)input_chan_bindings->application_data.value,
rpm-build c2b31c
                      envstr) == 0);
rpm-build c2b31c
    }
rpm-build c2b31c
Packit fd8b60
    /*
Packit fd8b60
     * The unwrapped token sits at the end and is just one byte giving the
Packit fd8b60
     * remaining number of hops.  The final octet of the mech encoding should
Packit fd8b60
     * be just prior to it.
Packit fd8b60
     */
Packit fd8b60
    assert(input_token->length >= 2);
Packit fd8b60
    hops = ((uint8_t *)input_token->value)[input_token->length - 1];
Packit fd8b60
    mech_last_octet = ((uint8_t *)input_token->value)[input_token->length - 2];
Packit fd8b60
Packit fd8b60
    envstr = getenv("ACCEPT_FAIL");
Packit fd8b60
    if (envstr != NULL && atoi(envstr) == mech_last_octet) {
Packit fd8b60
        output_token->value = gssalloc_strdup("fail");
Packit fd8b60
        assert(output_token->value != NULL);
Packit fd8b60
        output_token->length = 4;
Packit fd8b60
        return GSS_S_FAILURE;
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    if (*context_handle == GSS_C_NO_CONTEXT) {
Packit fd8b60
        ctx = malloc(sizeof(*ctx));
Packit fd8b60
        assert(ctx != NULL);
Packit fd8b60
        ctx->initiator = 0;
Packit fd8b60
        ctx->hops = hops;
Packit fd8b60
        *context_handle = (gss_ctx_id_t)ctx;
Packit fd8b60
    } else {
Packit fd8b60
        assert(!ctx->initiator);
Packit fd8b60
        ctx->hops--;
Packit fd8b60
        assert(ctx->hops == hops);
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    if (ctx->hops > 0) {
Packit fd8b60
        /* Generate a token containing the remaining hop count. */
Packit fd8b60
        ctx->hops--;
Packit fd8b60
        output_token->value = gssalloc_malloc(1);
Packit fd8b60
        assert(output_token->value != NULL);
Packit fd8b60
        memcpy(output_token->value, &ctx->hops, 1);
Packit fd8b60
        output_token->length = 1;
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    return (ctx->hops > 0) ? GSS_S_CONTINUE_NEEDED : GSS_S_COMPLETE;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gss_delete_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
Packit fd8b60
                       gss_buffer_t output_token)
Packit fd8b60
{
Packit fd8b60
    free(*context_handle);
Packit fd8b60
    *context_handle = GSS_C_NO_CONTEXT;
Packit fd8b60
    return GSS_S_COMPLETE;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
Packit fd8b60
                 OM_uint32 time_req, gss_OID_set desired_mechs,
Packit fd8b60
                 gss_cred_usage_t cred_usage,
Packit fd8b60
                 gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs,
Packit fd8b60
                 OM_uint32 *time_rec)
Packit fd8b60
{
Packit fd8b60
    return GSS_S_COMPLETE;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gss_acquire_cred_with_password(OM_uint32 *minor_status,
Packit fd8b60
                               const gss_name_t desired_name,
Packit fd8b60
                               const gss_buffer_t password, OM_uint32 time_req,
Packit fd8b60
                               const gss_OID_set desired_mechs,
Packit fd8b60
                               gss_cred_usage_t cred_usage,
Packit fd8b60
                               gss_cred_id_t *output_cred_handle,
Packit fd8b60
                               gss_OID_set *actual_mechs, OM_uint32 *time_rec)
Packit fd8b60
{
Packit fd8b60
    return GSS_S_COMPLETE;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gss_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle)
Packit fd8b60
{
Packit fd8b60
    return GSS_S_COMPLETE;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gss_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer,
Packit fd8b60
                gss_OID input_name_type, gss_name_t *output_name)
Packit fd8b60
{
Packit fd8b60
    static int dummy;
Packit fd8b60
Packit fd8b60
    /*
Packit fd8b60
     * We don't need to remember anything about names, but we do need to
Packit fd8b60
     * distinguish them from GSS_C_NO_NAME (to determine the direction of
Packit fd8b60
     * gss_query_meta_data() and gss_exchange_meta_data()), so assign an
Packit fd8b60
     * arbitrary data pointer.
Packit fd8b60
     */
Packit fd8b60
    *output_name = (gss_name_t)&dummy;
Packit fd8b60
    return GSS_S_COMPLETE;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gss_release_name(OM_uint32 *minor_status, gss_name_t *input_name)
Packit fd8b60
{
Packit fd8b60
    return GSS_S_COMPLETE;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gss_display_status(OM_uint32 *minor_status, OM_uint32 status_value,
Packit fd8b60
                   int status_type, gss_OID mech_type,
Packit fd8b60
                   OM_uint32 *message_context, gss_buffer_t status_string)
Packit fd8b60
{
Packit fd8b60
    if (status_type == GSS_C_MECH_CODE && status_value == 12345) {
Packit fd8b60
        status_string->value = gssalloc_strdup("failure from acceptor");
Packit fd8b60
        assert(status_string->value != NULL);
Packit fd8b60
        status_string->length = strlen(status_string->value);
Packit fd8b60
        return GSS_S_COMPLETE;
Packit fd8b60
    }
Packit fd8b60
    return GSS_S_BAD_STATUS;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gssspi_query_meta_data(OM_uint32 *minor_status, gss_const_OID mech_oid,
Packit fd8b60
                       gss_cred_id_t cred_handle, gss_ctx_id_t *context_handle,
Packit fd8b60
                       const gss_name_t targ_name, OM_uint32 req_flags,
Packit fd8b60
                       gss_buffer_t meta_data)
Packit fd8b60
{
Packit fd8b60
    const char *envstr;
Packit fd8b60
    uint8_t mech_last_octet;
Packit fd8b60
    int initiator = (targ_name != GSS_C_NO_NAME);
Packit fd8b60
Packit fd8b60
    mech_last_octet = ((uint8_t *)mech_oid->elements)[mech_oid->length - 1];
Packit fd8b60
    envstr = getenv(initiator ? "INIT_QUERY_FAIL" : "ACCEPT_QUERY_FAIL");
Packit fd8b60
    if (envstr != NULL && atoi(envstr) == mech_last_octet)
Packit fd8b60
        return GSS_S_FAILURE;
Packit fd8b60
    envstr = getenv(initiator ? "INIT_QUERY_NONE" : "ACCEPT_QUERY_NONE");
Packit fd8b60
    if (envstr != NULL && atoi(envstr) == mech_last_octet)
Packit fd8b60
        return GSS_S_COMPLETE;
Packit fd8b60
Packit fd8b60
    meta_data->value = gssalloc_strdup("X");
Packit fd8b60
    meta_data->length = 1;
Packit fd8b60
    return GSS_S_COMPLETE;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gssspi_exchange_meta_data(OM_uint32 *minor_status, gss_const_OID mech_oid,
Packit fd8b60
                          gss_cred_id_t cred_handle,
Packit fd8b60
                          gss_ctx_id_t *context_handle,
Packit fd8b60
                          const gss_name_t targ_name, OM_uint32 req_flags,
Packit fd8b60
                          gss_const_buffer_t meta_data)
Packit fd8b60
{
Packit fd8b60
    const char *envstr;
Packit fd8b60
    uint8_t mech_last_octet;
Packit fd8b60
    int initiator = (targ_name != GSS_C_NO_NAME);
Packit fd8b60
Packit fd8b60
    mech_last_octet = ((uint8_t *)mech_oid->elements)[mech_oid->length - 1];
Packit fd8b60
    envstr = getenv(initiator ? "INIT_EXCHANGE_FAIL" : "ACCEPT_EXCHANGE_FAIL");
Packit fd8b60
    if (envstr != NULL && atoi(envstr) == mech_last_octet)
Packit fd8b60
        return GSS_S_FAILURE;
Packit fd8b60
Packit fd8b60
    assert(meta_data->length == 1 && memcmp(meta_data->value, "X", 1) == 0);
Packit fd8b60
    return GSS_S_COMPLETE;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gssspi_query_mechanism_info(OM_uint32 *minor_status, gss_const_OID mech_oid,
Packit fd8b60
                            unsigned char auth_scheme[16])
Packit fd8b60
{
Packit fd8b60
    /* Copy the mech OID encoding and right-pad it with zeros. */
Packit fd8b60
    memset(auth_scheme, 0, 16);
Packit fd8b60
    assert(mech_oid->length <= 16);
Packit fd8b60
    memcpy(auth_scheme, mech_oid->elements, mech_oid->length);
Packit fd8b60
    return GSS_S_COMPLETE;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
OM_uint32 KRB5_CALLCONV
Packit fd8b60
gss_inquire_sec_context_by_oid(OM_uint32 *minor_status,
Packit fd8b60
                               const gss_ctx_id_t context_handle,
Packit fd8b60
                               const gss_OID desired_object,
Packit fd8b60
                               gss_buffer_set_t *data_set)
Packit fd8b60
{
Packit fd8b60
    struct test_context *ctx = (struct test_context *)context_handle;
Packit fd8b60
    OM_uint32 major;
Packit fd8b60
    uint8_t keybytes[32] = { 0 };
Packit fd8b60
    uint8_t typebytes[4];
Packit fd8b60
    gss_buffer_desc key, type;
Packit fd8b60
    const char *envstr;
Packit fd8b60
    int ask_verify;
Packit fd8b60
Packit fd8b60
    if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_KEY))
Packit fd8b60
        ask_verify = 0;
Packit fd8b60
    else if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_VERIFY_KEY))
Packit fd8b60
        ask_verify = 1;
Packit fd8b60
    else
Packit fd8b60
        return GSS_S_UNAVAILABLE;
Packit fd8b60
Packit fd8b60
    /*
Packit fd8b60
     * By default, make a key available only if the context is established.
Packit fd8b60
     * This can be overridden to "always", "init-always", "accept-always",
Packit fd8b60
     * or "never".
Packit fd8b60
     */
Packit fd8b60
    envstr = getenv("KEY");
Packit fd8b60
    if (envstr != NULL && strcmp(envstr, "never") == 0) {
Packit fd8b60
        return GSS_S_UNAVAILABLE;
Packit fd8b60
    } else if (ctx->hops > 0) {
Packit fd8b60
        if (envstr == NULL)
Packit fd8b60
            return GSS_S_UNAVAILABLE;
Packit fd8b60
        else if (strcmp(envstr, "init-always") == 0 && !ctx->initiator)
Packit fd8b60
            return GSS_S_UNAVAILABLE;
Packit fd8b60
        else if (strcmp(envstr, "accept-always") == 0 && ctx->initiator)
Packit fd8b60
            return GSS_S_UNAVAILABLE;
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    /* Perturb the key so that each side's verifier key is equal to the other's
Packit fd8b60
     * checksum key. */
Packit fd8b60
    keybytes[0] = ask_verify ^ ctx->initiator;
Packit fd8b60
Packit fd8b60
    /* Supply an all-zeros aes256-sha1 negoex key. */
Packit fd8b60
    if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_KEY) ||
Packit fd8b60
        gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_VERIFY_KEY)) {
Packit fd8b60
        store_32_le(ENCTYPE_AES256_CTS_HMAC_SHA1_96, typebytes);
Packit fd8b60
        key.value = keybytes;
Packit fd8b60
        key.length = sizeof(keybytes);
Packit fd8b60
        type.value = typebytes;
Packit fd8b60
        type.length = sizeof(typebytes);
Packit fd8b60
        major = gss_add_buffer_set_member(minor_status, &key, data_set);
Packit fd8b60
        if (major != GSS_S_COMPLETE)
Packit fd8b60
            return major;
Packit fd8b60
        return gss_add_buffer_set_member(minor_status, &type, data_set);
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    return GSS_S_UNAVAILABLE;
Packit fd8b60
}