From 470ef4b75879ab258bc434f0c3f41ab6fe828ff2 Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Sun, 10 Oct 2010 19:16:05 +0200 Subject: [PATCH] make extension tokens extensible --- accept_sec_context.c | 76 +++++++------ gssapiP_eap.h | 1 - init_sec_context.c | 55 ++++++--- util.h | 36 ++++++ util_exts.c | 307 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 425 insertions(+), 50 deletions(-) create mode 100644 util_exts.c diff --git a/accept_sec_context.c b/accept_sec_context.c index b1119f2..debe76a 100644 --- a/accept_sec_context.c +++ b/accept_sec_context.c @@ -405,11 +405,11 @@ cleanup: } static OM_uint32 -acceptGssChannelBindings(OM_uint32 *minor, - gss_ctx_id_t ctx, +verifyGssChannelBindings(OM_uint32 *minor, gss_cred_id_t cred, - gss_buffer_t inputToken, - gss_channel_bindings_t chanBindings) + gss_ctx_id_t ctx, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken) { OM_uint32 major, tmpMinor; gss_iov_buffer_desc iov[2]; @@ -430,7 +430,7 @@ acceptGssChannelBindings(OM_uint32 *minor, !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) { major = GSS_S_BAD_BINDINGS; } else { - major = GSS_S_CONTINUE_NEEDED; + major = GSS_S_COMPLETE; } gss_release_buffer(&tmpMinor, &iov[0].buffer); @@ -438,6 +438,11 @@ acceptGssChannelBindings(OM_uint32 *minor, return major; } +static struct gss_eap_extension_provider +eapGssVerifyInitExtensions[] = { + { EXT_TYPE_GSS_CHANNEL_BINDINGS, 1, verifyGssChannelBindings }, +}; + static OM_uint32 eapGssSmAcceptExtensionsReq(OM_uint32 *minor, gss_ctx_id_t ctx, @@ -448,11 +453,10 @@ eapGssSmAcceptExtensionsReq(OM_uint32 *minor, { OM_uint32 major; - outputToken->length = 0; - outputToken->value = NULL; - - major = acceptGssChannelBindings(minor, ctx, cred, inputToken, - chanBindings); + major = gssEapVerifyExtensions(minor, cred, ctx, eapGssVerifyInitExtensions, + sizeof(eapGssVerifyInitExtensions) / + sizeof(eapGssVerifyInitExtensions[0]), + chanBindings, inputToken); if (GSS_ERROR(major)) return major; @@ -462,6 +466,31 @@ eapGssSmAcceptExtensionsReq(OM_uint32 *minor, } static OM_uint32 +makeReauthCreds(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_channel_bindings_t chanBindings, + gss_buffer_t outputToken) +{ + OM_uint32 major = GSS_S_UNAVAILABLE; + +#ifdef GSSEAP_ENABLE_REAUTH + /* + * If we're built with fast reauthentication enabled, then + * fabricate a ticket from the initiator to ourselves. + */ + major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken); +#endif + + return major; +} + +static struct gss_eap_extension_provider +eapGssMakeAcceptExtensions[] = { + { EXT_TYPE_REAUTH_CREDS, 0, makeReauthCreds }, +}; + +static OM_uint32 eapGssSmAcceptExtensionsResp(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred, @@ -469,31 +498,14 @@ eapGssSmAcceptExtensionsResp(OM_uint32 *minor, gss_channel_bindings_t chanBindings, gss_buffer_t outputToken) { - OM_uint32 major, tmpMinor; - gss_buffer_desc credsToken = GSS_C_EMPTY_BUFFER; + OM_uint32 major; -#ifdef GSSEAP_ENABLE_REAUTH - /* - * If we're built with fast reauthentication enabled, then - * fabricate a ticket from the initiator to ourselves. - * Otherwise return an empty token. - */ - major = gssEapMakeReauthCreds(minor, ctx, cred, &credsToken); + major = gssEapMakeExtensions(minor, cred, ctx, eapGssMakeAcceptExtensions, + sizeof(eapGssMakeAcceptExtensions) / + sizeof(eapGssMakeAcceptExtensions[0]), + chanBindings, outputToken); if (GSS_ERROR(major)) return major; -#else - credsToken.value = ""; -#endif /* GSSEAP_ENABLE_REAUTH */ - - major = duplicateBuffer(minor, &credsToken, outputToken); - if (GSS_ERROR(major)) { - gss_release_buffer(&tmpMinor, &credsToken); - return major; - } - -#ifdef GSSEAP_ENABLE_REAUTH - gss_release_buffer(&tmpMinor, &credsToken); -#endif ctx->state = EAP_STATE_ESTABLISHED; diff --git a/gssapiP_eap.h b/gssapiP_eap.h index 5ff9bd2..473aa84 100644 --- a/gssapiP_eap.h +++ b/gssapiP_eap.h @@ -199,7 +199,6 @@ struct gss_ctx_id_struct { #define KEY_USAGE_ACCEPTOR_SIGN 23 #define KEY_USAGE_INITIATOR_SEAL 24 #define KEY_USAGE_INITIATOR_SIGN 25 -#define KEY_USAGE_CHANNEL_BINDINGS 64 /* wrap_iov.c */ OM_uint32 diff --git a/init_sec_context.c b/init_sec_context.c index 68c55eb..80abb05 100644 --- a/init_sec_context.c +++ b/init_sec_context.c @@ -487,7 +487,8 @@ cleanup: } static OM_uint32 -initGssChannelBindings(OM_uint32 *minor, +makeGssChannelBindings(OM_uint32 *minor, + gss_cred_id_t cred, gss_ctx_id_t ctx, gss_channel_bindings_t chanBindings, gss_buffer_t outputToken) @@ -495,7 +496,6 @@ initGssChannelBindings(OM_uint32 *minor, OM_uint32 major; gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER; - if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS) buffer = chanBindings->application_data; @@ -504,9 +504,14 @@ initGssChannelBindings(OM_uint32 *minor, if (GSS_ERROR(major)) return major; - return GSS_S_CONTINUE_NEEDED; + return GSS_S_COMPLETE; } +static struct gss_eap_extension_provider +eapGssMakeInitExtensions[] = { + { EXT_TYPE_GSS_CHANNEL_BINDINGS, 1, makeGssChannelBindings }, +}; + static OM_uint32 eapGssSmInitExtensionsReq(OM_uint32 *minor, gss_cred_id_t cred, @@ -519,27 +524,42 @@ eapGssSmInitExtensionsReq(OM_uint32 *minor, gss_buffer_t inputToken, gss_buffer_t outputToken) { - OM_uint32 major, tmpMinor; - gss_buffer_desc cbToken = GSS_C_EMPTY_BUFFER; + OM_uint32 major; - major = initGssChannelBindings(minor, ctx, chanBindings, &cbToken); + major = gssEapMakeExtensions(minor, cred, ctx, eapGssMakeInitExtensions, + sizeof(eapGssMakeInitExtensions) / + sizeof(eapGssMakeInitExtensions[0]), + chanBindings, outputToken); if (GSS_ERROR(major)) return major; - ctx->state = EAP_STATE_EXTENSIONS_RESP; - - major = duplicateBuffer(minor, &cbToken, outputToken); - if (GSS_ERROR(major)) { - gss_release_buffer(&tmpMinor, &cbToken); - return major; - } + assert(outputToken->value != NULL); - gss_release_buffer(&tmpMinor, &cbToken); + ctx->state = EAP_STATE_EXTENSIONS_RESP; return GSS_S_CONTINUE_NEEDED; } static OM_uint32 +verifyReauthCreds(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken) +{ +#ifdef GSSEAP_ENABLE_REAUTH + return gssEapStoreReauthCreds(minor, ctx, cred, inputToken); +#else + return GSS_S_UNAVAILABLE; +#endif +} + +static struct gss_eap_extension_provider +eapGssVerifyAcceptExtensions[] = { + { EXT_TYPE_REAUTH_CREDS, 0, verifyReauthCreds }, +}; + +static OM_uint32 eapGssSmInitExtensionsResp(OM_uint32 *minor, gss_cred_id_t cred, gss_ctx_id_t ctx, @@ -551,13 +571,14 @@ eapGssSmInitExtensionsResp(OM_uint32 *minor, gss_buffer_t inputToken, gss_buffer_t outputToken) { -#ifdef GSSEAP_ENABLE_REAUTH OM_uint32 major; - major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken); + major = gssEapVerifyExtensions(minor, cred, ctx, eapGssVerifyAcceptExtensions, + sizeof(eapGssVerifyAcceptExtensions) / + sizeof(eapGssVerifyAcceptExtensions[0]), + chanBindings, inputToken); if (GSS_ERROR(major)) return major; -#endif ctx->state = EAP_STATE_ESTABLISHED; diff --git a/util.h b/util.h index 58a4941..9184ebf 100644 --- a/util.h +++ b/util.h @@ -243,6 +243,42 @@ gssEapDeriveRfc3961Key(OM_uint32 *minor, krb5_enctype enctype, krb5_keyblock *pKey); +/* util_exts.c */ +#define EXT_FLAG_CRITICAL 0x80000000 +#define EXT_FLAG_VERIFIED 0x40000000 + +#define EXT_TYPE_GSS_CHANNEL_BINDINGS 0x00000000 +#define EXT_TYPE_REAUTH_CREDS 0x00000001 +#define EXT_TYPE_MASK (~(EXT_FLAG_CRITICAL | EXT_FLAG_VERIFIED)) + +struct gss_eap_extension_provider { + OM_uint32 type; + int critical; + OM_uint32 (*callback)(OM_uint32 *, + gss_cred_id_t, + gss_ctx_id_t, + gss_channel_bindings_t, + gss_buffer_t); +}; + +OM_uint32 +gssEapMakeExtensions(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + const struct gss_eap_extension_provider *exts, + size_t count, + gss_channel_bindings_t chanBindings, + gss_buffer_t buffer); + +OM_uint32 +gssEapVerifyExtensions(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + const struct gss_eap_extension_provider *exts, + size_t count, + gss_channel_bindings_t chanBindings, + const gss_buffer_t buffer); + /* util_krb.c */ OM_uint32 gssEapKerberosInit(OM_uint32 *minor, krb5_context *context); diff --git a/util_exts.c b/util_exts.c new file mode 100644 index 0000000..906430c --- /dev/null +++ b/util_exts.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 "gssapiP_eap.h" + +static OM_uint32 +encodeExtensions(OM_uint32 *minor, + gss_buffer_set_t extensions, + OM_uint32 *types, + gss_buffer_t buffer) +{ + OM_uint32 major, tmpMinor; + size_t required = 0, i; + unsigned char *p; + + buffer->value = NULL; + buffer->length = 0; + + if (extensions != GSS_C_NO_BUFFER_SET) { + for (i = 0; i < extensions->count; i++) { + required += 8 + extensions->elements[i].length; + } + } + + /* + * We must always return a non-NULL token otherwise the calling state + * machine assumes we are finished. Hence care in case malloc(0) does + * return NULL. + */ + buffer->value = GSSEAP_MALLOC(required ? required : 1); + if (buffer->value == NULL) { + *minor = ENOMEM; + major = GSS_S_FAILURE; + goto cleanup; + } + + buffer->length = required; + p = (unsigned char *)buffer->value; + + if (extensions != GSS_C_NO_BUFFER_SET) { + for (i = 0; i < extensions->count; i++) { + gss_buffer_t extension = &extensions->elements[i]; + + assert((types[i] & EXT_FLAG_VERIFIED) == 0); /* private flag */ + + /* + * Extensions are encoded as type-length-value, where the upper + * bit of the type indicates criticality. + */ + store_uint32_be(types[i], &p[0]); + store_uint32_be(extension->length, &p[4]); + memcpy(&p[8], extension->value, extension->length); + + p += 8 + extension->length; + } + } + + assert(p == (unsigned char *)buffer->value + required); + assert(buffer->value != NULL); + +cleanup: + if (GSS_ERROR(major)) { + gss_release_buffer(&tmpMinor, buffer); + } + + return major; +} + +static OM_uint32 +decodeExtensions(OM_uint32 *minor, + const gss_buffer_t buffer, + gss_buffer_set_t *pExtensions, + OM_uint32 **pTypes) +{ + OM_uint32 major, tmpMinor; + gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET; + OM_uint32 *types = NULL; + unsigned char *p; + size_t remain; + + *pExtensions = GSS_C_NO_BUFFER_SET; + *pTypes = NULL; + + major = gss_create_empty_buffer_set(minor, &extensions); + if (GSS_ERROR(major)) + goto cleanup; + + if (buffer->length == 0) { + major = GSS_S_COMPLETE; + goto cleanup; + } + + p = (unsigned char *)buffer->value; + remain = buffer->length; + + do { + OM_uint32 *ntypes; + gss_buffer_desc extension; + + if (remain < 8) { + major = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + + ntypes = GSSEAP_REALLOC(types, + (extensions->count + 1) * sizeof(OM_uint32)); + if (ntypes == NULL) { + *minor = ENOMEM; + major = GSS_S_FAILURE; + goto cleanup; + } + types = ntypes; + + types[extensions->count] = load_uint32_be(&p[0]); + extension.length = load_uint32_be(&p[4]); + + if (remain < 8 + extension.length) { + major = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + extension.value = &p[8]; + + major = gss_add_buffer_set_member(minor, &extension, &extensions); + if (GSS_ERROR(major)) + goto cleanup; + + p += 8 + extension.length; + remain -= 8 + extension.length; + } while (remain != 0); + +cleanup: + if (GSS_ERROR(major)) { + gss_release_buffer_set(&tmpMinor, &extensions); + if (types != NULL) + GSSEAP_FREE(types); + } else { + *pExtensions = extensions; + *pTypes = types; + } + + return major; +} + +OM_uint32 +gssEapMakeExtensions(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + const struct gss_eap_extension_provider *exts, + size_t count, + gss_channel_bindings_t chanBindings, + gss_buffer_t buffer) +{ + OM_uint32 major, tmpMinor; + size_t i, j; + gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET; + OM_uint32 *types; + + assert(buffer != GSS_C_NO_BUFFER); + + buffer->length = 0; + buffer->value = NULL; + + types = GSSEAP_CALLOC(count, sizeof(OM_uint32)); + if (types == NULL) { + *minor = ENOMEM; + major = GSS_S_FAILURE; + goto cleanup; + } + + for (i = 0, j = 0; i < count; i++) { + const struct gss_eap_extension_provider *ext = &exts[i]; + gss_buffer_desc extension = GSS_C_EMPTY_BUFFER; + + types[j] = ext->type; + if (ext->critical) + types[j] |= EXT_FLAG_CRITICAL; + + major = ext->callback(minor, cred, ctx, chanBindings, &extension); + if (GSS_ERROR(major)) { + if (ext->critical) + continue; + else + goto cleanup; + } + + major = gss_add_buffer_set_member(minor, &extension, &extensions); + if (GSS_ERROR(major)) + goto cleanup; + + j++; + } + + assert(j == (extensions == GSS_C_NO_BUFFER_SET ? 0 : extensions->count)); + + major = encodeExtensions(minor, extensions, types, buffer); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + gss_release_buffer_set(&tmpMinor, &extensions); + if (types != NULL) + GSSEAP_FREE(types); + + return major; +} + +OM_uint32 +gssEapVerifyExtensions(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + const struct gss_eap_extension_provider *exts, + size_t count, + gss_channel_bindings_t chanBindings, + const gss_buffer_t buffer) +{ + OM_uint32 major, tmpMinor; + gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET; + OM_uint32 *types = NULL; + size_t i; + + major = decodeExtensions(minor, buffer, &extensions, &types); + if (GSS_ERROR(major)) + goto cleanup; + + for (i = 0; i < count; i++) { + const struct gss_eap_extension_provider *ext = &exts[i]; + gss_buffer_t extension = GSS_C_NO_BUFFER; + size_t j; + + for (j = 0; j < extensions->count; j++) { + if ((types[j] & EXT_TYPE_MASK) == ext->type) { + extension = &extensions->elements[j]; + break; + } + } + + if (extension != GSS_C_NO_BUFFER) { + /* Process extension and mark as verified */ + major = ext->callback(minor, cred, ctx, chanBindings, + &extensions->elements[j]); + if (GSS_ERROR(major)) + goto cleanup; + + types[j] |= EXT_FLAG_VERIFIED; + } else if (ext->critical) { + /* Critical extension missing */ + *minor = ENOENT; + major = GSS_S_UNAVAILABLE; + gssEapSaveStatusInfo(*minor, + "Missing critical GSS EAP extension %08x", + ext->type); + goto cleanup; + } + } + + /* Check we processed all critical extensions */ + for (i = 0; i < extensions->count; i++) { + if ((types[i] & EXT_FLAG_CRITICAL) && + (types[i] & EXT_FLAG_VERIFIED) == 0) { + *minor = ENOSYS; + major = GSS_S_UNAVAILABLE; + gssEapSaveStatusInfo(*minor, + "Received unknown critical GSS EAP extension %08x", + (types[i] & EXT_TYPE_MASK)); + goto cleanup; + } + } + + *minor = 0; + major = GSS_S_COMPLETE; + +cleanup: + gss_release_buffer_set(&tmpMinor, &extensions); + if (types != NULL) + GSSEAP_FREE(types); + + return major; +} -- 2.1.4