Files
heimdal/lib/gssapi/spnego/context_storage.c
Luke Howard 1175fa0435 gss: don't include mech_locl.h in spnego
including mech_locl.h in spnego/context_storage.c is unnecessary and
breaks the build on Windows
2021-08-11 19:12:11 +10:00

493 lines
15 KiB
C

/*
* Copyright (C) 2021, PADL Software Pty Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "spnego_locl.h"
#define SC_MECH_TYPES 0x0001
#define SC_PREFERRED_MECH_TYPE 0x0002
#define SC_SELECTED_MECH_TYPE 0x0004
#define SC_NEGOTIATED_MECH_TYPE 0x0008
#define SC_NEGOTIATED_CTX_ID 0x0010
#define SC_MECH_FLAGS 0x0020
#define SC_MECH_TIME_REC 0x0040
#define SC_MECH_SRC_NAME 0x0080
#define SC_TARGET_NAME 0x0100
#define SC_NEGOEX 0x0200
#define SNC_OID 0x01
#define SNC_MECH_CONTEXT 0x02
#define SNC_METADATA 0x04
static krb5_error_code
ret_spnego_context(krb5_storage *sp, gssspnego_ctx *ctxp);
static krb5_error_code
store_spnego_context(krb5_storage *sp, gssspnego_ctx ctx);
static krb5_error_code
ret_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech **mechp);
static krb5_error_code
store_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech *mech);
static uint16_t
spnego_flags_to_int(struct spnego_flags flags);
static struct spnego_flags
int_to_spnego_flags(uint16_t f);
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_import_sec_context_internal(OM_uint32 *minor,
gss_const_buffer_t buffer,
gssspnego_ctx *ctxp)
{
krb5_error_code ret;
krb5_storage *sp;
sp = krb5_storage_from_readonly_mem(buffer->value, buffer->length);
if (sp == NULL) {
*minor = ENOMEM;
return GSS_S_FAILURE;
}
krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
ret = ret_spnego_context(sp, ctxp);
krb5_storage_free(sp);
*minor = ret;
return ret ? GSS_S_FAILURE : GSS_S_COMPLETE;
}
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_export_sec_context_internal(OM_uint32 *minor,
gssspnego_ctx ctx,
gss_buffer_t buffer)
{
krb5_error_code ret;
krb5_storage *sp;
krb5_data data;
sp = krb5_storage_emem();
if (sp == NULL) {
*minor = ENOMEM;
return GSS_S_FAILURE;
}
krb5_data_zero(&data);
krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
ret = store_spnego_context(sp, ctx);
if (ret == 0)
ret = krb5_storage_to_data(sp, &data);
if (ret == 0) {
buffer->length = data.length;
buffer->value = data.data;
}
krb5_storage_free(sp);
*minor = ret;
return ret ? GSS_S_FAILURE : GSS_S_COMPLETE;
}
static krb5_error_code
ret_spnego_context(krb5_storage *sp, gssspnego_ctx *ctxp)
{
OM_uint32 major = GSS_S_COMPLETE, minor;
gssspnego_ctx ctx = NULL;
krb5_error_code ret = 0;
krb5_data data;
gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
uint16_t sc_flags, spnego_flags;
*ctxp = NULL;
krb5_data_zero(&data);
CHECK(major, _gss_spnego_alloc_sec_context(&minor, (gss_ctx_id_t *)&ctx));
CHECK(ret, krb5_ret_uint16(sp, &sc_flags));
CHECK(ret, krb5_ret_uint16(sp, &spnego_flags));
ctx->flags = int_to_spnego_flags(spnego_flags);
if (sc_flags & SC_MECH_TYPES)
CHECK(major, _gss_mg_ret_buffer(&minor, sp, &ctx->NegTokenInit_mech_types));
if (sc_flags & SC_PREFERRED_MECH_TYPE)
CHECK(major, _gss_mg_ret_oid(&minor, sp, &ctx->preferred_mech_type));
if (sc_flags & SC_SELECTED_MECH_TYPE)
CHECK(major, _gss_mg_ret_oid(&minor, sp, &ctx->selected_mech_type));
if (sc_flags & SC_NEGOTIATED_MECH_TYPE)
CHECK(major, _gss_mg_ret_oid(&minor, sp, &ctx->negotiated_mech_type));
if (sc_flags & SC_NEGOTIATED_CTX_ID) {
CHECK(major, _gss_mg_ret_buffer(&minor, sp, &buf));
CHECK(major, gss_import_sec_context(&minor, &buf,
&ctx->negotiated_ctx_id));
gss_release_buffer(&minor, &buf);
}
if (sc_flags & SC_MECH_FLAGS)
CHECK(ret, krb5_ret_uint32(sp, &ctx->mech_flags));
if (sc_flags & SC_MECH_TIME_REC)
CHECK(ret, krb5_ret_uint32(sp, &ctx->mech_time_rec));
else
ctx->mech_time_rec = GSS_C_INDEFINITE;
if (sc_flags & SC_MECH_SRC_NAME) {
CHECK(major, _gss_mg_ret_buffer(&minor, sp, &buf));
CHECK(major, gss_import_name(&minor, &buf, GSS_C_NT_EXPORT_NAME,
&ctx->mech_src_name));
gss_release_buffer(&minor, &buf);
}
if (sc_flags & SC_TARGET_NAME) {
CHECK(major, _gss_mg_ret_buffer(&minor, sp, &buf));
CHECK(major, gss_import_name(&minor, &buf, GSS_C_NT_EXPORT_NAME,
&ctx->target_name));
gss_release_buffer(&minor, &buf);
}
if (sc_flags & SC_NEGOEX) {
uint8_t i, nschemes;
CHECK(ret, krb5_ret_uint8(sp, &ctx->negoex_step));
CHECK(ret, krb5_ret_data(sp, &data));
ctx->negoex_transcript = krb5_storage_emem();
if (ctx->negoex_transcript == NULL) {
ret = ENOMEM;
goto fail;
}
krb5_storage_set_byteorder(ctx->negoex_transcript,
KRB5_STORAGE_BYTEORDER_LE);
if (krb5_storage_write(ctx->negoex_transcript,
data.data, data.length) != data.length) {
ret = ENOMEM;
goto fail;
}
krb5_data_free(&data);
CHECK(ret, krb5_ret_uint32(sp, &ctx->negoex_seqnum));
if (krb5_storage_read(sp, ctx->negoex_conv_id,
GUID_LENGTH) != GUID_LENGTH) {
ret = KRB5_BAD_MSIZE;
goto fail;
}
CHECK(ret, krb5_ret_uint8(sp, &nschemes));
for (i = 0; i < nschemes; i++) {
struct negoex_auth_mech *mech;
CHECK(ret, ret_negoex_auth_mech(sp, &mech));
HEIM_TAILQ_INSERT_TAIL(&ctx->negoex_mechs, mech, links);
}
}
*ctxp = ctx;
fail:
if (ret == 0 && GSS_ERROR(major))
ret = minor ? minor : KRB5_BAD_MSIZE;
if (ret)
_gss_spnego_delete_sec_context(&minor, (gss_ctx_id_t *)&ctx,
GSS_C_NO_BUFFER);
krb5_data_free(&data);
gss_release_buffer(&minor, &buf);
return ret;
}
static krb5_error_code
store_spnego_context(krb5_storage *sp, gssspnego_ctx ctx)
{
OM_uint32 major = GSS_S_COMPLETE, minor;
krb5_error_code ret = 0;
krb5_data data;
gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
uint16_t sc_flags = 0, spnego_flags;
krb5_data_zero(&data);
if (ctx->NegTokenInit_mech_types.length)
sc_flags |= SC_MECH_TYPES;
if (ctx->preferred_mech_type)
sc_flags |= SC_PREFERRED_MECH_TYPE;
if (ctx->selected_mech_type)
sc_flags |= SC_SELECTED_MECH_TYPE;
if (ctx->negotiated_mech_type)
sc_flags |= SC_NEGOTIATED_MECH_TYPE;
if (ctx->negotiated_ctx_id)
sc_flags |= SC_NEGOTIATED_CTX_ID;
if (ctx->mech_flags)
sc_flags |= SC_MECH_FLAGS;
if (ctx->mech_time_rec != GSS_C_INDEFINITE)
sc_flags |= SC_MECH_TIME_REC;
if (ctx->mech_src_name)
sc_flags |= SC_MECH_SRC_NAME;
if (ctx->target_name)
sc_flags |= SC_TARGET_NAME;
if (ctx->negoex_step)
sc_flags |= SC_NEGOEX;
CHECK(ret, krb5_store_uint16(sp, sc_flags));
spnego_flags = spnego_flags_to_int(ctx->flags);
CHECK(ret, krb5_store_uint16(sp, spnego_flags));
if (sc_flags & SC_MECH_TYPES)
CHECK(major, _gss_mg_store_buffer(&minor, sp, &ctx->NegTokenInit_mech_types));
if (sc_flags & SC_PREFERRED_MECH_TYPE)
CHECK(major, _gss_mg_store_oid(&minor, sp, ctx->preferred_mech_type));
if (sc_flags & SC_SELECTED_MECH_TYPE)
CHECK(major, _gss_mg_store_oid(&minor, sp, ctx->selected_mech_type));
if (sc_flags & SC_NEGOTIATED_MECH_TYPE)
CHECK(major, _gss_mg_store_oid(&minor, sp, ctx->negotiated_mech_type));
if (sc_flags & SC_NEGOTIATED_CTX_ID) {
CHECK(major, gss_export_sec_context(&minor, &ctx->negotiated_ctx_id,
&buf));
CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
gss_release_buffer(&minor, &buf);
}
if (sc_flags & SC_MECH_FLAGS)
CHECK(ret, krb5_store_uint32(sp, ctx->mech_flags));
if (sc_flags & SC_MECH_TIME_REC)
CHECK(ret, krb5_store_uint32(sp, ctx->mech_time_rec));
if (sc_flags & SC_MECH_SRC_NAME) {
CHECK(major, gss_export_name(&minor, ctx->mech_src_name, &buf));
CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
gss_release_buffer(&minor, &buf);
}
if (sc_flags & SC_TARGET_NAME) {
CHECK(major, gss_export_name(&minor, ctx->target_name, &buf));
CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
gss_release_buffer(&minor, &buf);
}
if (sc_flags & SC_NEGOEX) {
uint32_t nschemes;
struct negoex_auth_mech *mech;
CHECK(ret, krb5_store_uint8(sp, ctx->negoex_step));
if (ctx->negoex_transcript) {
CHECK(ret, krb5_storage_to_data(ctx->negoex_transcript, &data));
}
CHECK(ret, krb5_store_data(sp, data));
krb5_data_free(&data);
CHECK(ret, krb5_store_uint32(sp, ctx->negoex_seqnum));
CHECK(ret, krb5_store_bytes(sp, ctx->negoex_conv_id, GUID_LENGTH));
nschemes = 0;
HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links)
nschemes++;
if (nschemes > 0xff) {
ret = ERANGE;
goto fail;
}
CHECK(ret, krb5_store_uint8(sp, nschemes));
HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links)
CHECK(ret, store_negoex_auth_mech(sp, mech));
}
fail:
if (ret == 0 && GSS_ERROR(major))
ret = minor ? minor : KRB5_BAD_MSIZE;
krb5_data_free(&data);
gss_release_buffer(&minor, &buf);
return ret;
}
static krb5_error_code
ret_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech **mechp)
{
krb5_error_code ret;
OM_uint32 major = GSS_S_COMPLETE, minor;
gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
struct negoex_auth_mech *mech;
krb5_context context = _gss_mg_krb5_context();
uint8_t snc_flags, negoex_flags;
*mechp = NULL;
mech = calloc(1, sizeof(*mech));
if (mech == NULL) {
ret = ENOMEM;
goto fail;
}
CHECK(ret, krb5_ret_uint8(sp, &snc_flags));
CHECK(ret, krb5_ret_uint8(sp, &negoex_flags));
if (negoex_flags & (1 << 0))
mech->complete = 1;
if (negoex_flags & (1 << 1))
mech->sent_checksum = 1;
if (negoex_flags & (1 << 2))
mech->verified_checksum = 1;
if (snc_flags & SNC_OID)
CHECK(major, _gss_mg_ret_oid(&minor, sp, &mech->oid));
if (krb5_storage_read(sp, mech->scheme, GUID_LENGTH) != GUID_LENGTH) {
ret = KRB5_BAD_MSIZE;
goto fail;
}
if (snc_flags & SNC_MECH_CONTEXT) {
CHECK(major, _gss_mg_ret_buffer(&minor, sp, &buf));
CHECK(major, gss_import_sec_context(&minor, &buf,
&mech->mech_context));
gss_release_buffer(&minor, &buf);
}
if (snc_flags & SNC_METADATA)
CHECK(major, _gss_mg_ret_buffer(&minor, sp, &mech->metadata));
*mechp = mech;
fail:
if (ret == 0 && GSS_ERROR(major))
ret = minor ? minor : KRB5_BAD_MSIZE;
if (ret)
_gss_negoex_release_auth_mech(context, mech);
gss_release_buffer(&minor, &buf);
return ret;
}
static krb5_error_code
store_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech *mech)
{
krb5_error_code ret;
OM_uint32 major = GSS_S_COMPLETE, minor;
gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
uint8_t negoex_flags = 0, snc_flags = 0;
negoex_flags = 0;
if (mech->complete)
negoex_flags |= (1 << 0);
if (mech->sent_checksum)
negoex_flags |= (1 << 1);
if (mech->verified_checksum)
negoex_flags |= (1 << 2);
if (mech->oid)
snc_flags |= SNC_OID;
if (mech->mech_context)
snc_flags |= SNC_MECH_CONTEXT;
if (mech->metadata.length)
snc_flags |= SNC_METADATA;
CHECK(ret, krb5_store_uint8(sp, snc_flags));
CHECK(ret, krb5_store_uint8(sp, negoex_flags));
if (snc_flags & SNC_OID)
CHECK(major, _gss_mg_store_oid(&minor, sp, mech->oid));
CHECK(ret, krb5_store_bytes(sp, mech->scheme, GUID_LENGTH));
if (snc_flags & SNC_MECH_CONTEXT) {
CHECK(major, gss_export_sec_context(&minor, &mech->mech_context,
&buf));
CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
gss_release_buffer(&minor, &buf);
}
if (snc_flags & SNC_METADATA)
CHECK(major, _gss_mg_store_buffer(&minor, sp, &mech->metadata));
fail:
if (ret == 0 && GSS_ERROR(major))
ret = minor ? minor : KRB5_BAD_MSIZE;
gss_release_buffer(&minor, &buf);
return ret;
}
static uint16_t
spnego_flags_to_int(struct spnego_flags flags)
{
uint16_t f = 0;
if (flags.open)
f |= (1 << 0);
if (flags.local)
f |= (1 << 1);
if (flags.require_mic)
f |= (1 << 2);
if (flags.peer_require_mic)
f |= (1 << 3);
if (flags.sent_mic)
f |= (1 << 4);
if (flags.verified_mic)
f |= (1 << 5);
if (flags.safe_omit)
f |= (1 << 6);
if (flags.maybe_open)
f |= (1 << 7);
if (flags.seen_supported_mech)
f |= (1 << 8);
return f;
}
static struct spnego_flags
int_to_spnego_flags(uint16_t f)
{
struct spnego_flags flags;
memset(&flags, 0, sizeof(flags));
if (f & (1 << 0))
flags.open = 1;
if (f & (1 << 1))
flags.local = 1;
if (f & (1 << 2))
flags.require_mic = 1;
if (f & (1 << 3))
flags.peer_require_mic = 1;
if (f & (1 << 4))
flags.sent_mic = 1;
if (f & (1 << 5))
flags.verified_mic = 1;
if (f & (1 << 6))
flags.safe_omit = 1;
if (f & (1 << 7))
flags.maybe_open = 1;
if (f & (1 << 8))
flags.seen_supported_mech = 1;
return flags;
}