From: Luke Howard Date: Tue, 26 Apr 2011 15:58:13 +0000 (+0200) Subject: Merge branch 'master' into tlv-mic X-Git-Url: http://www.project-moonshot.org/gitweb/?p=moonshot.git;a=commitdiff_plain;h=9677dd5838f7e834a9d0997e198bd9823d633c00;hp=cd2644392fbee95873de8ddf53965f990b19d059 Merge branch 'master' into tlv-mic --- diff --git a/mech_eap/Makefile.am b/mech_eap/Makefile.am index 3bf752d..58d7b2c 100644 --- a/mech_eap/Makefile.am +++ b/mech_eap/Makefile.am @@ -24,6 +24,7 @@ mech_eap_la_SOURCES = \ acquire_cred_with_password.c \ add_cred.c \ add_cred_with_password.c \ + authorize_localname.c \ canonicalize_name.c \ compare_name.c \ context_time.c \ @@ -68,7 +69,6 @@ mech_eap_la_SOURCES = \ store_cred.c \ unwrap.c \ unwrap_iov.c \ - userok.c \ util_attr.cpp \ util_base64.c \ util_buffer.c \ diff --git a/mech_eap/accept_sec_context.c b/mech_eap/accept_sec_context.c index cc8702d..17d1020 100644 --- a/mech_eap/accept_sec_context.c +++ b/mech_eap/accept_sec_context.c @@ -131,6 +131,49 @@ acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred) } static OM_uint32 +gssEapSupportedInitiatorExts[] = { +}; + +static struct gss_eap_itok_map +gssEapAcceptorExtsFlagMap[] = { + { ITOK_TYPE_REAUTH_CREDS, CTX_FLAG_KRB_REAUTH_SUPPORTED }, +}; + +static OM_uint32 +eapGssSmAcceptExts(OM_uint32 *minor, + gss_cred_id_t cred GSSEAP_UNUSED, + gss_ctx_id_t ctx GSSEAP_UNUSED, + gss_name_t target GSSEAP_UNUSED, + gss_OID mech GSSEAP_UNUSED, + OM_uint32 reqFlags GSSEAP_UNUSED, + OM_uint32 timeReq GSSEAP_UNUSED, + gss_channel_bindings_t chanBindings GSSEAP_UNUSED, + gss_buffer_t inputToken, + gss_buffer_t outputToken, + OM_uint32 *smFlags GSSEAP_UNUSED) +{ + OM_uint32 major; + + major = gssEapProcessSupportedExts(minor, inputToken, + gssEapAcceptorExtsFlagMap, + sizeof(gssEapAcceptorExtsFlagMap) / + sizeof(gssEapAcceptorExtsFlagMap[0]), + &ctx->flags); + if (GSS_ERROR(major)) + return major; + + major = gssEapEncodeSupportedExts(minor, + gssEapSupportedInitiatorExts, + sizeof(gssEapSupportedInitiatorExts) / + sizeof(gssEapSupportedInitiatorExts[0]), + outputToken); + if (GSS_ERROR(major)) + return major; + + return GSS_S_CONTINUE_NEEDED; +} + +static OM_uint32 eapGssSmAcceptAcceptorName(OM_uint32 *minor, gss_cred_id_t cred GSSEAP_UNUSED, gss_ctx_id_t ctx, @@ -231,7 +274,7 @@ eapGssSmAcceptIdentity(OM_uint32 *minor, GSSEAP_SM_TRANSITION_NEXT(ctx); *minor = 0; - *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL; + *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL; return GSS_S_CONTINUE_NEEDED; } @@ -596,7 +639,7 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor, major = GSS_S_CONTINUE_NEEDED; *minor = 0; - *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL; + *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL; cleanup: if (request != NULL) @@ -616,6 +659,40 @@ cleanup: } static OM_uint32 +eapGssSmAcceptGssFlags(OM_uint32 *minor, + gss_cred_id_t cred GSSEAP_UNUSED, + gss_ctx_id_t ctx, + gss_name_t target GSSEAP_UNUSED, + gss_OID mech GSSEAP_UNUSED, + OM_uint32 reqFlags GSSEAP_UNUSED, + OM_uint32 timeReq GSSEAP_UNUSED, + gss_channel_bindings_t chanBindings GSSEAP_UNUSED, + gss_buffer_t inputToken, + gss_buffer_t outputToken GSSEAP_UNUSED, + OM_uint32 *smFlags GSSEAP_UNUSED) +{ + unsigned char *p; + OM_uint32 initiatorGssFlags; + + assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0); + + if (inputToken->length < 4) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* allow flags to grow for future expansion */ + p = (unsigned char *)inputToken->value + inputToken->length - 4; + + initiatorGssFlags = load_uint32_be(p); + initiatorGssFlags &= GSSEAP_WIRE_FLAGS_MASK; + + ctx->gssFlags |= initiatorGssFlags; + + return GSS_S_CONTINUE_NEEDED; +} + +static OM_uint32 eapGssSmAcceptGssChannelBindings(OM_uint32 *minor, gss_cred_id_t cred GSSEAP_UNUSED, gss_ctx_id_t ctx, @@ -631,6 +708,8 @@ eapGssSmAcceptGssChannelBindings(OM_uint32 *minor, OM_uint32 major, tmpMinor; gss_iov_buffer_desc iov[2]; + assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0); + iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE; iov[0].buffer.length = 0; iov[0].buffer.value = NULL; @@ -677,29 +756,39 @@ eapGssSmAcceptReauthCreds(OM_uint32 *minor, * If we're built with fast reauthentication enabled, then * fabricate a ticket from the initiator to ourselves. */ - major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken); + if (ctx->flags & CTX_FLAG_KRB_REAUTH_SUPPORTED) + major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken); + else + major = GSS_S_UNAVAILABLE; + if (major == GSS_S_UNAVAILABLE) major = GSS_S_COMPLETE; - if (major == GSS_S_COMPLETE) - major = GSS_S_CONTINUE_NEEDED; - return major; + return GSS_ERROR(major) ? major : GSS_S_CONTINUE_NEEDED; } #endif static OM_uint32 -eapGssSmAcceptCompleteInitiatorExts(OM_uint32 *minor, - gss_cred_id_t cred GSSEAP_UNUSED, - gss_ctx_id_t ctx, - gss_name_t target GSSEAP_UNUSED, - gss_OID mech GSSEAP_UNUSED, - OM_uint32 reqFlags GSSEAP_UNUSED, - OM_uint32 timeReq GSSEAP_UNUSED, - gss_channel_bindings_t chanBindings GSSEAP_UNUSED, - gss_buffer_t inputToken GSSEAP_UNUSED, - gss_buffer_t outputToken GSSEAP_UNUSED, - OM_uint32 *smFlags GSSEAP_UNUSED) +eapGssSmAcceptInitiatorMIC(OM_uint32 *minor, + gss_cred_id_t cred GSSEAP_UNUSED, + gss_ctx_id_t ctx, + gss_name_t target GSSEAP_UNUSED, + gss_OID mech GSSEAP_UNUSED, + OM_uint32 reqFlags GSSEAP_UNUSED, + OM_uint32 timeReq GSSEAP_UNUSED, + gss_channel_bindings_t chanBindings GSSEAP_UNUSED, + gss_buffer_t inputToken, + gss_buffer_t outputToken GSSEAP_UNUSED, + OM_uint32 *smFlags GSSEAP_UNUSED) { + OM_uint32 major; + + assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0); + + major = gssEapVerifyConversationMIC(minor, ctx, inputToken); + if (GSS_ERROR(major)) + return major; + GSSEAP_SM_TRANSITION_NEXT(ctx); *minor = 0; @@ -708,27 +797,45 @@ eapGssSmAcceptCompleteInitiatorExts(OM_uint32 *minor, } static OM_uint32 -eapGssSmAcceptCompleteAcceptorExts(OM_uint32 *minor, - gss_cred_id_t cred GSSEAP_UNUSED, - gss_ctx_id_t ctx, - gss_name_t target GSSEAP_UNUSED, - gss_OID mech GSSEAP_UNUSED, - OM_uint32 reqFlags GSSEAP_UNUSED, - OM_uint32 timeReq GSSEAP_UNUSED, - gss_channel_bindings_t chanBindings GSSEAP_UNUSED, - gss_buffer_t inputToken GSSEAP_UNUSED, - gss_buffer_t outputToken GSSEAP_UNUSED, - OM_uint32 *smFlags) +eapGssSmAcceptAcceptorMIC(OM_uint32 *minor, + gss_cred_id_t cred GSSEAP_UNUSED, + gss_ctx_id_t ctx, + gss_name_t target GSSEAP_UNUSED, + gss_OID mech GSSEAP_UNUSED, + OM_uint32 reqFlags GSSEAP_UNUSED, + OM_uint32 timeReq GSSEAP_UNUSED, + gss_channel_bindings_t chanBindings GSSEAP_UNUSED, + gss_buffer_t inputToken GSSEAP_UNUSED, + gss_buffer_t outputToken, + OM_uint32 *smFlags) { + OM_uint32 major; + + major = gssEapGetConversationMIC(minor, ctx, outputToken); + if (GSS_ERROR(major)) + return major; + GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED); *minor = 0; - *smFlags |= SM_FLAG_FORCE_SEND_TOKEN; + *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL; return GSS_S_COMPLETE; } +/* + * Acceptor state machine. + */ static struct gss_eap_sm eapGssAcceptorSm[] = { +#ifdef GSSEAP_ENABLE_REAUTH + { + ITOK_TYPE_REAUTH_REQ, + ITOK_TYPE_REAUTH_RESP, + GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE, + 0, + eapGssSmAcceptGssReauth, + }, +#endif { ITOK_TYPE_ACCEPTOR_NAME_REQ, ITOK_TYPE_ACCEPTOR_NAME_RESP, @@ -736,22 +843,20 @@ static struct gss_eap_sm eapGssAcceptorSm[] = { 0, eapGssSmAcceptAcceptorName }, -#ifdef GSSEAP_DEBUG { - ITOK_TYPE_VENDOR_INFO, - ITOK_TYPE_NONE, + ITOK_TYPE_SUPPORTED_ACCEPTOR_EXTS, + ITOK_TYPE_SUPPORTED_INITIATOR_EXTS, GSSEAP_STATE_INITIAL, 0, - eapGssSmAcceptVendorInfo, + eapGssSmAcceptExts, }, -#endif -#ifdef GSSEAP_ENABLE_REAUTH +#ifdef GSSEAP_DEBUG { - ITOK_TYPE_REAUTH_REQ, - ITOK_TYPE_REAUTH_RESP, + ITOK_TYPE_VENDOR_INFO, + ITOK_TYPE_NONE, GSSEAP_STATE_INITIAL, 0, - eapGssSmAcceptGssReauth, + eapGssSmAcceptVendorInfo, }, #endif { @@ -769,18 +874,25 @@ static struct gss_eap_sm eapGssAcceptorSm[] = { eapGssSmAcceptAuthenticate }, { + ITOK_TYPE_GSS_FLAGS, + ITOK_TYPE_NONE, + GSSEAP_STATE_INITIATOR_EXTS, + 0, + eapGssSmAcceptGssFlags + }, + { ITOK_TYPE_GSS_CHANNEL_BINDINGS, ITOK_TYPE_NONE, GSSEAP_STATE_INITIATOR_EXTS, - SM_ITOK_FLAG_REQUIRED, + 0, eapGssSmAcceptGssChannelBindings, }, { - ITOK_TYPE_NONE, + ITOK_TYPE_INITIATOR_MIC, ITOK_TYPE_NONE, GSSEAP_STATE_INITIATOR_EXTS, - 0, - eapGssSmAcceptCompleteInitiatorExts, + SM_ITOK_FLAG_REQUIRED, + eapGssSmAcceptInitiatorMIC, }, #ifdef GSSEAP_ENABLE_REAUTH { @@ -793,10 +905,10 @@ static struct gss_eap_sm eapGssAcceptorSm[] = { #endif { ITOK_TYPE_NONE, - ITOK_TYPE_NONE, + ITOK_TYPE_ACCEPTOR_MIC, GSSEAP_STATE_ACCEPTOR_EXTS, 0, - eapGssSmAcceptCompleteAcceptorExts + eapGssSmAcceptAcceptorMIC }, }; @@ -952,7 +1064,7 @@ eapGssSmAcceptGssReauth(OM_uint32 *minor, gss_OID mech, OM_uint32 reqFlags GSSEAP_UNUSED, OM_uint32 timeReq GSSEAP_UNUSED, - gss_channel_bindings_t chanBindings, + gss_channel_bindings_t userChanBindings, gss_buffer_t inputToken, gss_buffer_t outputToken, OM_uint32 *smFlags) @@ -960,6 +1072,7 @@ eapGssSmAcceptGssReauth(OM_uint32 *minor, OM_uint32 major, tmpMinor; gss_name_t krbInitiator = GSS_C_NO_NAME; OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE; + struct gss_channel_bindings_struct wireChanBindings = { 0 }; /* * If we're built with fast reauthentication support, it's valid @@ -970,11 +1083,25 @@ eapGssSmAcceptGssReauth(OM_uint32 *minor, ctx->flags |= CTX_FLAG_KRB_REAUTH; + /* + * To avoid an additional round trip, we use GSS channel bindings + * to integrity protect the rest of the initiator exchange. This + * does have the disadvantage of making it impossible for the + * acceptor to ignore application channel bindings, behaviour + * which differs from normal Kerberos and GSS-EAP itself. + */ + major = gssEapMakeTokenChannelBindings(minor, ctx, + userChanBindings, + inputToken, + &wireChanBindings); + if (GSS_ERROR(major)) + return major; + major = gssAcceptSecContext(minor, &ctx->reauthCtx, cred->reauthCred, inputToken, - chanBindings, + &wireChanBindings, &krbInitiator, &mech, outputToken, @@ -985,19 +1112,24 @@ eapGssSmAcceptGssReauth(OM_uint32 *minor, major = acceptReadyKrb(minor, ctx, cred, krbInitiator, mech, timeRec); if (major == GSS_S_COMPLETE) { - GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED); + /* Generate acceptor MIC */ + GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ACCEPTOR_EXTS); } ctx->gssFlags = gssFlags; } else if (GSS_ERROR(major) && (*smFlags & SM_FLAG_INPUT_TOKEN_CRITICAL) == 0) { - /* pretend reauthentication attempt never happened */ + /* Fall back to EAP */ gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER); ctx->flags &= ~(CTX_FLAG_KRB_REAUTH); GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL); - major = GSS_S_CONTINUE_NEEDED; + } else { + GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE); } + major = GSS_S_CONTINUE_NEEDED; + gssReleaseName(&tmpMinor, &krbInitiator); + gss_release_buffer(&tmpMinor, &wireChanBindings.application_data); return major; } diff --git a/mech_eap/export_sec_context.c b/mech_eap/export_sec_context.c index 43f3f28..1426d62 100644 --- a/mech_eap/export_sec_context.c +++ b/mech_eap/export_sec_context.c @@ -38,7 +38,7 @@ #include "gssapiP_eap.h" static OM_uint32 -gssEapExportPartialContext(OM_uint32 *minor, +exportPartialRadiusContext(OM_uint32 *minor, gss_ctx_id_t ctx, gss_buffer_t token) { @@ -143,7 +143,7 @@ gssEapExportSecContext(OM_uint32 *minor, */ if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx) && (ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) { - major = gssEapExportPartialContext(minor, ctx, &partialCtx); + major = exportPartialRadiusContext(minor, ctx, &partialCtx); if (GSS_ERROR(major)) goto cleanup; } @@ -155,6 +155,9 @@ gssEapExportSecContext(OM_uint32 *minor, length += 4 + acceptorName.length; /* acceptorName.value */ length += 24 + sequenceSize(ctx->seqState); /* seqState */ + if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) + length += 4 + ctx->conversation.length; + if (partialCtx.value != NULL) length += 4 + partialCtx.length; /* partialCtx.value */ @@ -190,6 +193,9 @@ gssEapExportSecContext(OM_uint32 *minor, if (GSS_ERROR(major)) goto cleanup; + if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) + p = store_buffer(&ctx->conversation, p, FALSE); + if (partialCtx.value != NULL) p = store_buffer(&partialCtx, p, FALSE); diff --git a/mech_eap/gssapiP_eap.h b/mech_eap/gssapiP_eap.h index d1d6bce..19e7fb2 100644 --- a/mech_eap/gssapiP_eap.h +++ b/mech_eap/gssapiP_eap.h @@ -141,6 +141,7 @@ struct gss_cred_id_struct #define CTX_FLAG_INITIATOR 0x00000001 #define CTX_FLAG_KRB_REAUTH 0x00000002 +#define CTX_FLAG_KRB_REAUTH_SUPPORTED 0x00000004 #define CTX_IS_INITIATOR(ctx) (((ctx)->flags & CTX_FLAG_INITIATOR) != 0) @@ -203,6 +204,7 @@ struct gss_ctx_id_struct #define reauthCtx ctxU.reauth #endif } ctxU; + gss_buffer_desc conversation; }; #define TOK_FLAG_SENDER_IS_ACCEPTOR 0x01 diff --git a/mech_eap/import_sec_context.c b/mech_eap/import_sec_context.c index d571bca..978970a 100644 --- a/mech_eap/import_sec_context.c +++ b/mech_eap/import_sec_context.c @@ -49,7 +49,7 @@ } while (0) static OM_uint32 -gssEapImportPartialContext(OM_uint32 *minor, +importPartialRadiusContext(OM_uint32 *minor, unsigned char **pBuf, size_t *pRemain, gss_ctx_id_t ctx) @@ -233,6 +233,47 @@ importName(OM_uint32 *minor, } static OM_uint32 +importConversation(OM_uint32 *minor, + unsigned char **pBuf, + size_t *pRemain, + gss_ctx_id_t ctx) +{ + OM_uint32 major; + unsigned char *p = *pBuf; + size_t remain = *pRemain; + gss_buffer_desc tmp; + + if (remain < 4) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + tmp.length = load_uint32_be(p); + if (tmp.length == 0 || + remain - 4 < tmp.length) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (p[4] != 0x06) { + *minor = GSSEAP_BAD_TOK_HEADER; + return GSS_S_DEFECTIVE_TOKEN; + } + + tmp.value = p + 4; + + major = duplicateBuffer(minor, &tmp, &ctx->conversation); + if (GSS_ERROR(major)) + return major; + + *pBuf += 4 + tmp.length; + *pRemain -= 4 + tmp.length; + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 gssEapImportContext(OM_uint32 *minor, gss_buffer_t token, gss_ctx_id_t ctx) @@ -308,11 +349,16 @@ gssEapImportContext(OM_uint32 *minor, * The partial context should only be expected for unestablished * acceptor contexts. */ - if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx) && - (ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) { - major = gssEapImportPartialContext(minor, &p, &remain, ctx); + if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) { + major = importConversation(minor, &p, &remain, ctx); if (GSS_ERROR(major)) return major; + + if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) { + major = importPartialRadiusContext(minor, &p, &remain, ctx); + if (GSS_ERROR(major)) + return major; + } } #ifdef GSSEAP_DEBUG diff --git a/mech_eap/init_sec_context.c b/mech_eap/init_sec_context.c index 930eb32..becb767 100644 --- a/mech_eap/init_sec_context.c +++ b/mech_eap/init_sec_context.c @@ -223,7 +223,11 @@ peerConfigInit(OM_uint32 *minor, assert(cred->name != GSS_C_NO_NAME); - if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) { + /* + * draft-ietf-abfab-gss-eap-01: the host portion is empty + * for initiators. + */ + if ((cred->name->flags & NAME_FLAG_NAI) == 0) { *minor = GSSEAP_BAD_INITIATOR_NAME; return GSS_S_BAD_NAME; } @@ -443,21 +447,31 @@ eapGssSmInitGssReauth(OM_uint32 *minor, gss_OID mech GSSEAP_UNUSED, OM_uint32 reqFlags, OM_uint32 timeReq, - gss_channel_bindings_t chanBindings, + gss_channel_bindings_t userChanBindings, gss_buffer_t inputToken, gss_buffer_t outputToken, - OM_uint32 *smFlags GSSEAP_UNUSED) + OM_uint32 *smFlags) { OM_uint32 major, tmpMinor; gss_name_t mechTarget = GSS_C_NO_NAME; gss_OID actualMech = GSS_C_NO_OID; OM_uint32 gssFlags, timeRec; + struct gss_channel_bindings_struct wireChanBindings = { 0 }; assert(cred != GSS_C_NO_CREDENTIAL); if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) { - if (!gssEapCanReauthP(cred, target, timeReq)) - return GSS_S_CONTINUE_NEEDED; + if (!gssEapCanReauthP(cred, target, timeReq)) { + major = GSS_S_CONTINUE_NEEDED; + goto cleanup; + } + + major = gssEapMakeTokenChannelBindings(minor, ctx, + userChanBindings, + GSS_C_NO_BUFFER, + &wireChanBindings); + if (GSS_ERROR(major)) + goto cleanup; ctx->flags |= CTX_FLAG_KRB_REAUTH; } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) { @@ -477,7 +491,7 @@ eapGssSmInitGssReauth(OM_uint32 *minor, (gss_OID)gss_mech_krb5, reqFlags | GSS_C_MUTUAL_FLAG, timeReq, - chanBindings, + &wireChanBindings, inputToken, &actualMech, outputToken, @@ -494,13 +508,18 @@ eapGssSmInitGssReauth(OM_uint32 *minor, major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec); if (GSS_ERROR(major)) goto cleanup; - GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED); + + GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ACCEPTOR_EXTS); } else { GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE); + *smFlags |= SM_FLAG_SEND_TOKEN; } + major = GSS_S_CONTINUE_NEEDED; + cleanup: gssReleaseName(&tmpMinor, &mechTarget); + gss_release_buffer(&tmpMinor, &wireChanBindings.application_data); return major; } @@ -577,6 +596,50 @@ eapGssSmInitAcceptorName(OM_uint32 *minor, } static OM_uint32 +gssEapSupportedAcceptorExts[] = { + ITOK_TYPE_REAUTH_CREDS, +}; + +static struct gss_eap_itok_map +gssEapInitiatorExtsFlagMap[] = { +}; + +static OM_uint32 +eapGssSmInitExts(OM_uint32 *minor, + gss_cred_id_t cred GSSEAP_UNUSED, + gss_ctx_id_t ctx GSSEAP_UNUSED, + gss_name_t target GSSEAP_UNUSED, + gss_OID mech GSSEAP_UNUSED, + OM_uint32 reqFlags GSSEAP_UNUSED, + OM_uint32 timeReq GSSEAP_UNUSED, + gss_channel_bindings_t chanBindings GSSEAP_UNUSED, + gss_buffer_t inputToken, + gss_buffer_t outputToken, + OM_uint32 *smFlags GSSEAP_UNUSED) +{ + OM_uint32 major = GSS_S_COMPLETE; + + if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) { + major = gssEapEncodeSupportedExts(minor, + gssEapSupportedAcceptorExts, + sizeof(gssEapSupportedAcceptorExts) / + sizeof(gssEapSupportedAcceptorExts[0]), + outputToken); + } else if (inputToken != GSS_C_NO_BUFFER) { + major = gssEapProcessSupportedExts(minor, inputToken, + gssEapInitiatorExtsFlagMap, + sizeof(gssEapInitiatorExtsFlagMap) / + sizeof(gssEapInitiatorExtsFlagMap[0]), + &ctx->flags); + } + + if (GSS_ERROR(major)) + return major; + + return GSS_S_CONTINUE_NEEDED; +} + +static OM_uint32 eapGssSmInitIdentity(OM_uint32 *minor, gss_cred_id_t cred GSSEAP_UNUSED, gss_ctx_id_t ctx, @@ -601,7 +664,7 @@ eapGssSmInitIdentity(OM_uint32 *minor, GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL); } else #endif - *smFlags |= SM_FLAG_FORCE_SEND_TOKEN; + *smFlags |= SM_FLAG_SEND_TOKEN; assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0); assert(inputToken == GSS_C_NO_BUFFER); @@ -705,7 +768,7 @@ cleanup: *minor = tmpMinor; } - *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL; + *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL; } wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0); @@ -715,6 +778,30 @@ cleanup: } static OM_uint32 +eapGssSmInitGssFlags(OM_uint32 *minor, + gss_cred_id_t cred GSSEAP_UNUSED, + gss_ctx_id_t ctx, + gss_name_t target GSSEAP_UNUSED, + gss_OID mech GSSEAP_UNUSED, + OM_uint32 reqFlags GSSEAP_UNUSED, + OM_uint32 timeReq GSSEAP_UNUSED, + gss_channel_bindings_t chanBindings GSSEAP_UNUSED, + gss_buffer_t inputToken GSSEAP_UNUSED, + gss_buffer_t outputToken, + OM_uint32 *smFlags GSSEAP_UNUSED) +{ + unsigned char wireFlags[4]; + gss_buffer_desc flagsBuf; + + store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags); + + flagsBuf.length = sizeof(wireFlags); + flagsBuf.value = wireFlags; + + return duplicateBuffer(minor, &flagsBuf, outputToken); +} + +static OM_uint32 eapGssSmInitGssChannelBindings(OM_uint32 *minor, gss_cred_id_t cred GSSEAP_UNUSED, gss_ctx_id_t ctx, @@ -725,24 +812,19 @@ eapGssSmInitGssChannelBindings(OM_uint32 *minor, gss_channel_bindings_t chanBindings, gss_buffer_t inputToken GSSEAP_UNUSED, gss_buffer_t outputToken, - OM_uint32 *smFlags) + OM_uint32 *smFlags GSSEAP_UNUSED) { OM_uint32 major; - gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER; - if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS) - buffer = chanBindings->application_data; - - major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT, - &buffer, NULL, outputToken); - if (GSS_ERROR(major)) - return major; - - assert(outputToken->value != NULL); + if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0 && + chanBindings != GSS_C_NO_CHANNEL_BINDINGS) { + major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT, + &chanBindings->application_data, NULL, outputToken); + if (GSS_ERROR(major)) + return major; + } *minor = 0; - *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL; - return GSS_S_CONTINUE_NEEDED; } @@ -774,39 +856,51 @@ eapGssSmInitReauthCreds(OM_uint32 *minor, #endif /* GSSEAP_ENABLE_REAUTH */ static OM_uint32 -eapGssSmInitCompleteInitiatorExts(OM_uint32 *minor, - gss_cred_id_t cred GSSEAP_UNUSED, - gss_ctx_id_t ctx, - gss_name_t target GSSEAP_UNUSED, - gss_OID mech GSSEAP_UNUSED, - OM_uint32 reqFlags GSSEAP_UNUSED, - OM_uint32 timeReq GSSEAP_UNUSED, - gss_channel_bindings_t chanBindings GSSEAP_UNUSED, - gss_buffer_t inputToken GSSEAP_UNUSED, - gss_buffer_t outputToken GSSEAP_UNUSED, - OM_uint32 *smFlags) +eapGssSmInitInitiatorMIC(OM_uint32 *minor, + gss_cred_id_t cred GSSEAP_UNUSED, + gss_ctx_id_t ctx, + gss_name_t target GSSEAP_UNUSED, + gss_OID mech GSSEAP_UNUSED, + OM_uint32 reqFlags GSSEAP_UNUSED, + OM_uint32 timeReq GSSEAP_UNUSED, + gss_channel_bindings_t chanBindings GSSEAP_UNUSED, + gss_buffer_t inputToken GSSEAP_UNUSED, + gss_buffer_t outputToken, + OM_uint32 *smFlags) { + OM_uint32 major; + + major = gssEapGetConversationMIC(minor, ctx, outputToken); + if (GSS_ERROR(major)) + return major; + GSSEAP_SM_TRANSITION_NEXT(ctx); *minor = 0; - *smFlags |= SM_FLAG_FORCE_SEND_TOKEN; + *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL; return GSS_S_CONTINUE_NEEDED; } static OM_uint32 -eapGssSmInitCompleteAcceptorExts(OM_uint32 *minor, - gss_cred_id_t cred GSSEAP_UNUSED, - gss_ctx_id_t ctx, - gss_name_t target GSSEAP_UNUSED, - gss_OID mech GSSEAP_UNUSED, - OM_uint32 reqFlags GSSEAP_UNUSED, - OM_uint32 timeReq GSSEAP_UNUSED, - gss_channel_bindings_t chanBindings GSSEAP_UNUSED, - gss_buffer_t inputToken GSSEAP_UNUSED, - gss_buffer_t outputToken GSSEAP_UNUSED, - OM_uint32 *smFlags GSSEAP_UNUSED) +eapGssSmInitAcceptorMIC(OM_uint32 *minor, + gss_cred_id_t cred GSSEAP_UNUSED, + gss_ctx_id_t ctx, + gss_name_t target GSSEAP_UNUSED, + gss_OID mech GSSEAP_UNUSED, + OM_uint32 reqFlags GSSEAP_UNUSED, + OM_uint32 timeReq GSSEAP_UNUSED, + gss_channel_bindings_t chanBindings GSSEAP_UNUSED, + gss_buffer_t inputToken, + gss_buffer_t outputToken GSSEAP_UNUSED, + OM_uint32 *smFlags GSSEAP_UNUSED) { + OM_uint32 major; + + major = gssEapVerifyConversationMIC(minor, ctx, inputToken); + if (GSS_ERROR(major)) + return major; + GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED); *minor = 0; @@ -814,6 +908,9 @@ eapGssSmInitCompleteAcceptorExts(OM_uint32 *minor, return GSS_S_COMPLETE; } +/* + * Initiator state machine. + */ static struct gss_eap_sm eapGssInitiatorSm[] = { { ITOK_TYPE_CONTEXT_ERR, @@ -829,6 +926,13 @@ static struct gss_eap_sm eapGssInitiatorSm[] = { 0, eapGssSmInitAcceptorName }, + { + ITOK_TYPE_SUPPORTED_INITIATOR_EXTS, + ITOK_TYPE_SUPPORTED_ACCEPTOR_EXTS, + GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE, + 0, + eapGssSmInitExts + }, #ifdef GSSEAP_DEBUG { ITOK_TYPE_NONE, @@ -866,17 +970,24 @@ static struct gss_eap_sm eapGssInitiatorSm[] = { }, { ITOK_TYPE_NONE, + ITOK_TYPE_GSS_FLAGS, + GSSEAP_STATE_INITIATOR_EXTS, + 0, + eapGssSmInitGssFlags + }, + { + ITOK_TYPE_NONE, ITOK_TYPE_GSS_CHANNEL_BINDINGS, GSSEAP_STATE_INITIATOR_EXTS, - SM_ITOK_FLAG_REQUIRED, + 0, eapGssSmInitGssChannelBindings }, { ITOK_TYPE_NONE, - ITOK_TYPE_NONE, + ITOK_TYPE_INITIATOR_MIC, GSSEAP_STATE_INITIATOR_EXTS, 0, - eapGssSmInitCompleteInitiatorExts + eapGssSmInitInitiatorMIC }, #ifdef GSSEAP_ENABLE_REAUTH { @@ -889,11 +1000,11 @@ static struct gss_eap_sm eapGssInitiatorSm[] = { #endif /* other extensions go here */ { - ITOK_TYPE_NONE, + ITOK_TYPE_ACCEPTOR_MIC, ITOK_TYPE_NONE, GSSEAP_STATE_ACCEPTOR_EXTS, - 0, - eapGssSmInitCompleteAcceptorExts + SM_ITOK_FLAG_REQUIRED, + eapGssSmInitAcceptorMIC } }; diff --git a/mech_eap/inquire_name.c b/mech_eap/inquire_name.c index 2a4e0e3..a9bbd5c 100644 --- a/mech_eap/inquire_name.c +++ b/mech_eap/inquire_name.c @@ -58,9 +58,6 @@ OM_uint32 gss_inquire_name(OM_uint32 *minor, return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; } - if (attrs == NULL) - return GSS_S_COMPLETE; - GSSEAP_MUTEX_LOCK(&name->mutex); major = gssEapInquireName(minor, name, name_is_MN, MN_mech, attrs); diff --git a/mech_eap/mech_eap.exports b/mech_eap/mech_eap.exports index 449f013..3ba5892 100644 --- a/mech_eap/mech_eap.exports +++ b/mech_eap/mech_eap.exports @@ -2,6 +2,7 @@ gss_accept_sec_context gss_acquire_cred gss_add_cred gss_add_cred_with_password +gss_authorize_localname gss_canonicalize_name gss_compare_name gss_context_time @@ -41,7 +42,6 @@ gss_set_sec_context_option gss_store_cred gss_unwrap gss_unwrap_iov -gss_userok gss_verify_mic gss_wrap gss_wrap_iov diff --git a/mech_eap/userok.c b/mech_eap/userok.c deleted file mode 100644 index 9853992..0000000 --- a/mech_eap/userok.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2011, 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. - */ - -/* - * Local authorization services. - */ - -#include "gssapiP_eap.h" - -OM_uint32 KRB5_CALLCONV -gss_userok(OM_uint32 *minor, - const gss_name_t name GSSEAP_UNUSED, - const char *user GSSEAP_UNUSED, - int *user_ok) -{ - /* - * The MIT mechglue will fallback to comparing names in the absence - * of a mechanism implementation of gss_userok. To avoid this and - * force the mechglue to use attribute-based authorization, always - * return access denied here. - */ - - *minor = 0; - *user_ok = 0; - return GSS_S_COMPLETE; -} diff --git a/mech_eap/util.h b/mech_eap/util.h index b3399be..e5376a6 100644 --- a/mech_eap/util.h +++ b/mech_eap/util.h @@ -164,46 +164,65 @@ enum gss_eap_token_type { TOK_TYPE_ACCEPTOR_CONTEXT = 0x0602, /* acceptor-sent context token */ }; +struct gss_eap_itok_map { + OM_uint32 type; /* inner token type */ + OM_uint32 flag; /* context flag */ +}; + /* inner token types and flags */ -#define ITOK_TYPE_NONE 0x00000000 -#define ITOK_TYPE_CONTEXT_ERR 0x00000001 /* critical */ -#define ITOK_TYPE_ACCEPTOR_NAME_REQ 0x00000002 /* TBD */ -#define ITOK_TYPE_ACCEPTOR_NAME_RESP 0x00000003 /* TBD */ -#define ITOK_TYPE_EAP_RESP 0x00000004 /* critical, required, if not reauth */ -#define ITOK_TYPE_EAP_REQ 0x00000005 /* critical, required, if not reauth */ -#define ITOK_TYPE_GSS_CHANNEL_BINDINGS 0x00000006 /* critical, required, if not reauth */ -#define ITOK_TYPE_REAUTH_CREDS 0x00000007 /* optional */ -#define ITOK_TYPE_REAUTH_REQ 0x00000008 /* optional */ -#define ITOK_TYPE_REAUTH_RESP 0x00000009 /* optional */ -#define ITOK_TYPE_VERSION_INFO 0x0000000A /* optional */ -#define ITOK_TYPE_VENDOR_INFO 0x0000000B /* optional */ - -#define ITOK_FLAG_CRITICAL 0x80000000 /* critical, wire flag */ -#define ITOK_FLAG_VERIFIED 0x40000000 /* verified, API flag */ - -#define ITOK_TYPE_MASK (~(ITOK_FLAG_CRITICAL | ITOK_FLAG_VERIFIED)) +#define ITOK_TYPE_NONE 0x00000000 +#define ITOK_TYPE_CONTEXT_ERR 0x00000001 /* critical */ +#define ITOK_TYPE_ACCEPTOR_NAME_REQ 0x00000002 /* TBD */ +#define ITOK_TYPE_ACCEPTOR_NAME_RESP 0x00000003 /* TBD */ +#define ITOK_TYPE_EAP_RESP 0x00000004 /* critical, required, if not reauth */ +#define ITOK_TYPE_EAP_REQ 0x00000005 /* critical, required, if not reauth */ +#define ITOK_TYPE_GSS_CHANNEL_BINDINGS 0x00000006 /* optional */ +#define ITOK_TYPE_REAUTH_CREDS 0x00000007 /* optional */ +#define ITOK_TYPE_REAUTH_REQ 0x00000008 /* optional */ +#define ITOK_TYPE_REAUTH_RESP 0x00000009 /* optional */ +#define ITOK_TYPE_GSS_FLAGS 0x0000000A /* optional */ +#define ITOK_TYPE_INITIATOR_MIC 0x0000000B /* required */ +#define ITOK_TYPE_ACCEPTOR_MIC 0x0000000C /* required */ +#define ITOK_TYPE_SUPPORTED_ACCEPTOR_EXTS 0x0000000D /* optional */ +#define ITOK_TYPE_SUPPORTED_INITIATOR_EXTS 0x0000000E /* optional */ + +/* experimental */ +#define ITOK_TYPE_VERSION_INFO 0x00000080 /* optional */ +#define ITOK_TYPE_VENDOR_INFO 0x00000081 /* optional */ + +#define ITOK_FLAG_CRITICAL 0x80000000 /* critical, wire flag */ +#define ITOK_FLAG_VERIFIED 0x40000000 /* verified, API flag */ + +#define ITOK_TYPE_MASK (~(ITOK_FLAG_CRITICAL | ITOK_FLAG_VERIFIED)) + +#define ITOK_HEADER_LENGTH 8 /* type || length */ + +#define GSSEAP_WIRE_FLAGS_MASK ( GSS_C_MUTUAL_FLAG ) OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx); OM_uint32 gssEapReleaseContext(OM_uint32 *minor, gss_ctx_id_t *pCtx); OM_uint32 -gssEapMakeToken(OM_uint32 *minor, - gss_ctx_id_t ctx, - const gss_buffer_t innerToken, - enum gss_eap_token_type tokenType, - gss_buffer_t outputToken); +gssEapContextTime(OM_uint32 *minor, + gss_ctx_id_t context_handle, + OM_uint32 *time_rec); OM_uint32 -gssEapVerifyToken(OM_uint32 *minor, - gss_ctx_id_t ctx, - const gss_buffer_t inputToken, - enum gss_eap_token_type *tokenType, - gss_buffer_t innerInputToken); +gssEapGetConversationMIC(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_buffer_t convMIC); OM_uint32 -gssEapContextTime(OM_uint32 *minor, - gss_ctx_id_t context_handle, - OM_uint32 *time_rec); +gssEapVerifyConversationMIC(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t convMIC); + +OM_uint32 +gssEapMakeTokenChannelBindings(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_channel_bindings_t userBindings, + gss_buffer_t inputToken, + gss_channel_bindings_t wireBindings); /* util_cred.c */ OM_uint32 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred); @@ -604,7 +623,7 @@ struct gss_eap_sm { }; /* state machine flags, set by handler */ -#define SM_FLAG_FORCE_SEND_TOKEN 0x00000001 /* send token even if no inner tokens */ +#define SM_FLAG_SEND_TOKEN 0x00000001 /* exit state machine, send token */ #define SM_FLAG_OUTPUT_TOKEN_CRITICAL 0x00000002 /* output token is critical */ /* state machine flags, set by state machine */ @@ -631,24 +650,48 @@ gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state); /* util_token.c */ OM_uint32 -gssEapEncodeInnerTokens(OM_uint32 *minor, - gss_buffer_set_t extensions, - OM_uint32 *types, - gss_buffer_t buffer); -OM_uint32 gssEapDecodeInnerTokens(OM_uint32 *minor, const gss_buffer_t buffer, gss_buffer_set_t *pExtensions, OM_uint32 **pTypes); +OM_uint32 +gssEapRecordContextTokenHeader(OM_uint32 *minor, + gss_ctx_id_t ctx, + enum gss_eap_token_type tokType); + +OM_uint32 +gssEapRecordInnerContextToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_buffer_t innerToken, + OM_uint32 type); + +OM_uint32 +gssEapVerifyContextToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t inputToken, + enum gss_eap_token_type tokenType, + gss_buffer_t innerInputToken); + +OM_uint32 +gssEapEncodeSupportedExts(OM_uint32 *minor, + OM_uint32 *types, + size_t typesCount, + gss_buffer_t outputToken); + +OM_uint32 +gssEapProcessSupportedExts(OM_uint32 *minor, + gss_buffer_t inputToken, + struct gss_eap_itok_map *map, + size_t mapCount, + OM_uint32 *flags); + size_t -tokenSize(const gss_OID_desc *mech, size_t body_size); +tokenSize(size_t bodySize); void -makeTokenHeader(const gss_OID_desc *mech, - size_t body_size, - unsigned char **buf, - enum gss_eap_token_type tok_type); +makeTokenHeader(size_t body_size, + unsigned char **buf); OM_uint32 verifyTokenHeader(OM_uint32 *minor, diff --git a/mech_eap/util_attr.cpp b/mech_eap/util_attr.cpp index 01c3135..4ce40fa 100644 --- a/mech_eap/util_attr.cpp +++ b/mech_eap/util_attr.cpp @@ -854,13 +854,15 @@ gssEapInquireName(OM_uint32 *minor, return GSS_S_UNAVAILABLE; } - try { - if (!name->attrCtx->getAttributeTypes(attrs)) { - *minor = GSSEAP_NO_ATTR_CONTEXT; - return GSS_S_UNAVAILABLE; + if (attrs != NULL) { + try { + if (!name->attrCtx->getAttributeTypes(attrs)) { + *minor = GSSEAP_NO_ATTR_CONTEXT; + return GSS_S_UNAVAILABLE; + } + } catch (std::exception &e) { + return name->attrCtx->mapException(minor, e); } - } catch (std::exception &e) { - return name->attrCtx->mapException(minor, e); } return GSS_S_COMPLETE; diff --git a/mech_eap/util_context.c b/mech_eap/util_context.c index e9a9308..148afed 100644 --- a/mech_eap/util_context.c +++ b/mech_eap/util_context.c @@ -130,6 +130,7 @@ gssEapReleaseContext(OM_uint32 *minor, gssEapReleaseOid(&tmpMinor, &ctx->mechanismUsed); sequenceFree(&tmpMinor, &ctx->seqState); gssEapReleaseCred(&tmpMinor, &ctx->defaultCred); + gss_release_buffer(&tmpMinor, &ctx->conversation); GSSEAP_MUTEX_DESTROY(&ctx->mutex); @@ -142,88 +143,140 @@ gssEapReleaseContext(OM_uint32 *minor, } OM_uint32 -gssEapMakeToken(OM_uint32 *minor, - gss_ctx_id_t ctx, - const gss_buffer_t innerToken, - enum gss_eap_token_type tokenType, - gss_buffer_t outputToken) +gssEapContextTime(OM_uint32 *minor, + gss_ctx_id_t context_handle, + OM_uint32 *time_rec) { - unsigned char *p; + *minor = 0; - outputToken->length = tokenSize(ctx->mechanismUsed, innerToken->length); - outputToken->value = GSSEAP_MALLOC(outputToken->length); - if (outputToken->value == NULL) { - *minor = ENOMEM; - return GSS_S_FAILURE; - } + if (context_handle->expiryTime == 0) { + *time_rec = GSS_C_INDEFINITE; + } else { + time_t now, lifetime; - p = (unsigned char *)outputToken->value; - makeTokenHeader(ctx->mechanismUsed, innerToken->length, &p, tokenType); - memcpy(p, innerToken->value, innerToken->length); + time(&now); + lifetime = context_handle->expiryTime - now; + if (lifetime <= 0) { + *time_rec = 0; + return GSS_S_CONTEXT_EXPIRED; + } + *time_rec = lifetime; + } - *minor = 0; return GSS_S_COMPLETE; } OM_uint32 -gssEapVerifyToken(OM_uint32 *minor, - gss_ctx_id_t ctx, - const gss_buffer_t inputToken, - enum gss_eap_token_type *actualToken, - gss_buffer_t innerInputToken) +gssEapGetConversationMIC(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_buffer_t convMIC) { OM_uint32 major; - size_t bodySize; - unsigned char *p = (unsigned char *)inputToken->value; - gss_OID_desc oidBuf; - gss_OID oid; + gss_iov_buffer_desc iov[2]; - if (ctx->mechanismUsed != GSS_C_NO_OID) { - oid = ctx->mechanismUsed; - } else { - oidBuf.elements = NULL; - oidBuf.length = 0; - oid = &oidBuf; - } + iov[0].type = GSS_IOV_BUFFER_TYPE_DATA; + iov[0].buffer = ctx->conversation; - major = verifyTokenHeader(minor, oid, &bodySize, &p, - inputToken->length, actualToken); + iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE; + iov[1].buffer.length = 0; + iov[1].buffer.value = NULL; + + major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL, iov, 2, TOK_TYPE_MIC); if (GSS_ERROR(major)) return major; - if (ctx->mechanismUsed == GSS_C_NO_OID) { - major = gssEapCanonicalizeOid(minor, oid, 0, &ctx->mechanismUsed); - if (GSS_ERROR(major)) - return major; - } - - innerInputToken->length = bodySize; - innerInputToken->value = p; + *convMIC = iov[1].buffer; *minor = 0; return GSS_S_COMPLETE; } OM_uint32 -gssEapContextTime(OM_uint32 *minor, - gss_ctx_id_t context_handle, - OM_uint32 *time_rec) +gssEapVerifyConversationMIC(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t convMIC) { - *minor = 0; + OM_uint32 major; + gss_iov_buffer_desc iov[3]; + int confState; + size_t tokenHeaderLength; - if (context_handle->expiryTime == 0) { - *time_rec = GSS_C_INDEFINITE; - } else { - time_t now, lifetime; + if (convMIC == GSS_C_NO_BUFFER || convMIC->length < 16) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_BAD_SIG; + } - time(&now); - lifetime = context_handle->expiryTime - now; - if (lifetime <= 0) { - *time_rec = 0; - return GSS_S_CONTEXT_EXPIRED; - } - *time_rec = lifetime; + iov[0].type = GSS_IOV_BUFFER_TYPE_DATA; + iov[0].buffer = ctx->conversation; + + /* + * The conversation state already includes the MIC and its + * TLV header, as well as a header for emiting a subsequent + * token. These should not be included as input to verifyMIC. + */ + tokenHeaderLength = ITOK_HEADER_LENGTH + convMIC->length + + 2 + ctx->mechanismUsed->length + 2; + assert(ctx->conversation.length >= tokenHeaderLength); + iov[0].buffer.length -= tokenHeaderLength; + + iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER; + iov[1].buffer.length = 16; + iov[1].buffer.value = convMIC->value; + + iov[2].type = GSS_IOV_BUFFER_TYPE_TRAILER; + iov[2].buffer.length = convMIC->length - 16; + iov[2].buffer.value = (unsigned char *)convMIC->value + 16; + + major = gssEapUnwrapOrVerifyMIC(minor, ctx, &confState, NULL, + iov, 3, TOK_TYPE_MIC); + + + return major; +} + +OM_uint32 +gssEapMakeTokenChannelBindings(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_channel_bindings_t userChanBindings, + gss_buffer_t inputToken, + gss_channel_bindings_t wireChanBindings) +{ + gss_buffer_t wireData = &wireChanBindings->application_data; + unsigned char *p; + size_t tokenHeaderLength = 0; + + memset(wireChanBindings, 0, sizeof(*wireChanBindings)); + + if (!CTX_IS_INITIATOR(ctx)) { + assert(inputToken != GSS_C_NO_BUFFER); + + tokenHeaderLength = ITOK_HEADER_LENGTH + inputToken->length + + 2 + ctx->mechanismUsed->length + 2; + assert(ctx->conversation.length >= tokenHeaderLength); } + wireData->length = ctx->conversation.length - tokenHeaderLength; + + if (userChanBindings != GSS_C_NO_CHANNEL_BINDINGS) + wireData->length += userChanBindings->application_data.length; + + wireData->value = GSSEAP_MALLOC(wireData->length); + if (wireData->value == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + p = (unsigned char *)wireData->value; + + memcpy(p, ctx->conversation.value, ctx->conversation.length - tokenHeaderLength); + p += ctx->conversation.length - tokenHeaderLength; + + if (userChanBindings != GSS_C_NO_CHANNEL_BINDINGS) { + memcpy(p, userChanBindings->application_data.value, + userChanBindings->application_data.length); + p += userChanBindings->application_data.length; + } + + *minor = 0; return GSS_S_COMPLETE; } diff --git a/mech_eap/util_sm.c b/mech_eap/util_sm.c index ca69923..3bb28b8 100644 --- a/mech_eap/util_sm.c +++ b/mech_eap/util_sm.c @@ -95,21 +95,16 @@ gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state) #endif /* GSSEAP_DEBUG */ static OM_uint32 -makeErrorToken(OM_uint32 *minor, - OM_uint32 majorStatus, - OM_uint32 minorStatus, - gss_buffer_set_t *outputToken) +recordErrorToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + OM_uint32 majorStatus, + OM_uint32 minorStatus) { - OM_uint32 major; unsigned char errorData[8]; gss_buffer_desc errorBuffer; assert(GSS_ERROR(majorStatus)); - major = gss_create_empty_buffer_set(minor, outputToken); - if (GSS_ERROR(major)) - return major; - /* * Only return error codes that the initiator could have caused, * to avoid information leakage. @@ -130,61 +125,39 @@ makeErrorToken(OM_uint32 *minor, errorBuffer.length = sizeof(errorData); errorBuffer.value = errorData; - major = gss_add_buffer_set_member(minor, &errorBuffer, outputToken); - if (GSS_ERROR(major)) - return major; - - return GSS_S_COMPLETE; + return gssEapRecordInnerContextToken(minor, ctx, &errorBuffer, + ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL); } static OM_uint32 -allocInnerTokens(OM_uint32 *minor, - size_t count, - gss_buffer_set_t *pTokens, - OM_uint32 **pTokenTypes) +makeContextToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + size_t headerOffset, + gss_buffer_t outputToken) { - OM_uint32 major, tmpMinor; - gss_buffer_set_t tokens = GSS_C_NO_BUFFER_SET; - OM_uint32 *tokenTypes = NULL; + size_t tokSize, bodySize; + unsigned char *p; - major = gss_create_empty_buffer_set(minor, &tokens); - if (GSS_ERROR(major)) - goto cleanup; + assert(ctx->conversation.length > headerOffset); - assert(tokens->count == 0); - assert(tokens->elements == NULL); + bodySize = ctx->conversation.length - headerOffset; + tokSize = tokenSize(bodySize); - tokens->elements = (gss_buffer_desc *)GSSEAP_CALLOC(count, sizeof(gss_buffer_desc)); - if (tokens->elements == NULL) { - major = GSS_S_FAILURE; + outputToken->value = GSSEAP_MALLOC(tokSize); + if (outputToken->value == NULL) { *minor = ENOMEM; - goto cleanup; + return GSS_S_FAILURE; } - tokenTypes = (OM_uint32 *)GSSEAP_CALLOC(count, sizeof(OM_uint32)); - if (tokenTypes == NULL) { - major = GSS_S_FAILURE; - *minor = ENOMEM; - goto cleanup; - } + outputToken->length = tokSize; - major = GSS_S_COMPLETE; - *minor = 0; + p = (unsigned char *)outputToken->value; -cleanup: - if (GSS_ERROR(major)) { - gss_release_buffer_set(&tmpMinor, &tokens); - tokens = GSS_C_NO_BUFFER_SET; - if (tokenTypes != NULL) { - GSSEAP_FREE(tokenTypes); - tokenTypes = NULL; - } - } - - *pTokens = tokens; - *pTokenTypes = tokenTypes; + makeTokenHeader(bodySize, &p); + memcpy(p, (unsigned char *)ctx->conversation.value + headerOffset, bodySize); - return major; + *minor = 0; + return GSS_S_COMPLETE; } OM_uint32 @@ -205,12 +178,13 @@ gssEapSmStep(OM_uint32 *minor, gss_buffer_desc unwrappedInputToken = GSS_C_EMPTY_BUFFER; gss_buffer_desc unwrappedOutputToken = GSS_C_EMPTY_BUFFER; gss_buffer_set_t innerInputTokens = GSS_C_NO_BUFFER_SET; - gss_buffer_set_t innerOutputTokens = GSS_C_NO_BUFFER_SET; OM_uint32 *inputTokenTypes = NULL, *outputTokenTypes = NULL; unsigned int smFlags = 0; size_t i, j; int initialContextToken = 0; enum gss_eap_token_type tokType; + size_t headerOffset, firstTokenOffset; + size_t innerOutputTokenCount = 0; assert(smCount > 0); @@ -220,17 +194,13 @@ gssEapSmStep(OM_uint32 *minor, outputToken->value = NULL; if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) { - major = gssEapVerifyToken(minor, ctx, inputToken, &tokType, - &unwrappedInputToken); - if (GSS_ERROR(major)) - goto cleanup; + tokType = CTX_IS_INITIATOR(ctx) ? + TOK_TYPE_ACCEPTOR_CONTEXT : TOK_TYPE_INITIATOR_CONTEXT; - if (tokType != (CTX_IS_INITIATOR(ctx) - ? TOK_TYPE_ACCEPTOR_CONTEXT : TOK_TYPE_INITIATOR_CONTEXT)) { - major = GSS_S_DEFECTIVE_TOKEN; - *minor = GSSEAP_WRONG_TOK_ID; + major = gssEapVerifyContextToken(minor, ctx, inputToken, tokType, + &unwrappedInputToken); + if (GSS_ERROR(major)) goto cleanup; - } } else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) { major = GSS_S_DEFECTIVE_TOKEN; *minor = GSSEAP_WRONG_SIZE; @@ -252,12 +222,20 @@ gssEapSmStep(OM_uint32 *minor, if (GSS_ERROR(major)) goto cleanup; + headerOffset = ctx->conversation.length; + assert(innerInputTokens != GSS_C_NO_BUFFER_SET); - major = allocInnerTokens(minor, smCount, &innerOutputTokens, &outputTokenTypes); + /* Get ready to emit an output token */ + tokType = CTX_IS_INITIATOR(ctx) ? + TOK_TYPE_INITIATOR_CONTEXT : TOK_TYPE_ACCEPTOR_CONTEXT; + + major = gssEapRecordContextTokenHeader(minor, ctx, tokType); if (GSS_ERROR(major)) goto cleanup; + firstTokenOffset = ctx->conversation.length; + /* Process all the tokens that are valid for the current state. */ for (i = 0; i < smCount; i++) { struct gss_eap_sm *smp = &sm[i]; @@ -321,32 +299,45 @@ gssEapSmStep(OM_uint32 *minor, smFlags |= SM_FLAG_TRANSITED; if (innerOutputToken.value != NULL) { - innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken; - assert(smp->outputTokenType != ITOK_TYPE_NONE); - outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType; + OM_uint32 outputTokenType = smp->outputTokenType; + if (smFlags & SM_FLAG_OUTPUT_TOKEN_CRITICAL) - outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL; - innerOutputTokens->count++; + outputTokenType |= ITOK_FLAG_CRITICAL; + + assert(smp->outputTokenType != ITOK_TYPE_NONE); + + tmpMajor = gssEapRecordInnerContextToken(&tmpMinor, ctx, + &innerOutputToken, + outputTokenType); + if (GSS_ERROR(tmpMajor)) { + major = tmpMajor; + *minor = tmpMinor; + break; + } + + innerOutputTokenCount++; } + /* * Break out if we made a state transition and have some tokens to send. */ - if ((smFlags & SM_FLAG_TRANSITED) && - ((smFlags & SM_FLAG_FORCE_SEND_TOKEN) || innerOutputTokens->count != 0)) { + if (smFlags & SM_FLAG_SEND_TOKEN) { SM_ASSERT_VALID(ctx, major); break; } } else if ((smp->itokFlags & SM_ITOK_FLAG_REQUIRED) && smp->inputTokenType != ITOK_TYPE_NONE) { /* Check for required inner tokens */ +#ifdef GSSEAP_DEBUG + fprintf(stderr, "GSS-EAP: missing required token %08X\n", + smp->inputTokenType); +#endif major = GSS_S_DEFECTIVE_TOKEN; *minor = GSSEAP_MISSING_REQUIRED_ITOK; break; } } - assert(innerOutputTokens->count <= smCount); - /* Check we understood all critical tokens sent by peer */ if (!GSS_ERROR(major)) { for (j = 0; j < innerInputTokens->count; j++) { @@ -365,38 +356,27 @@ gssEapSmStep(OM_uint32 *minor, goto cleanup; /* return error directly to caller */ /* replace any emitted tokens with error token */ - gss_release_buffer_set(&tmpMinor, &innerOutputTokens); + ctx->conversation.length = firstTokenOffset; - tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputTokens); + tmpMajor = recordErrorToken(&tmpMinor, ctx, major, *minor); if (GSS_ERROR(tmpMajor)) { major = tmpMajor; *minor = tmpMinor; goto cleanup; } - if (innerOutputTokens->count != 0) - outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL; + innerOutputTokenCount = 1; } /* Format output token from inner tokens */ - if (innerOutputTokens->count != 0 || /* inner tokens to send */ + if (innerOutputTokenCount != 0 || /* inner tokens to send */ !CTX_IS_INITIATOR(ctx) || /* any leg acceptor */ !CTX_IS_ESTABLISHED(ctx)) { /* non-last leg initiator */ - tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens, - outputTokenTypes, &unwrappedOutputToken); - if (tmpMajor == GSS_S_COMPLETE) { - if (CTX_IS_INITIATOR(ctx)) - tokType = TOK_TYPE_INITIATOR_CONTEXT; - else - tokType = TOK_TYPE_ACCEPTOR_CONTEXT; - - tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken, - tokType, outputToken); - if (GSS_ERROR(tmpMajor)) { - major = tmpMajor; - *minor = tmpMinor; - goto cleanup; - } + tmpMajor = makeContextToken(&tmpMinor, ctx, headerOffset, outputToken); + if (GSS_ERROR(tmpMajor)) { + major = tmpMajor; + *minor = tmpMinor; + goto cleanup; } } @@ -407,7 +387,6 @@ gssEapSmStep(OM_uint32 *minor, cleanup: gss_release_buffer_set(&tmpMinor, &innerInputTokens); - gss_release_buffer_set(&tmpMinor, &innerOutputTokens); if (inputTokenTypes != NULL) GSSEAP_FREE(inputTokenTypes); if (outputTokenTypes != NULL) diff --git a/mech_eap/util_token.c b/mech_eap/util_token.c index a929198..5e69b26 100644 --- a/mech_eap/util_token.c +++ b/mech_eap/util_token.c @@ -58,72 +58,6 @@ #include "gssapiP_eap.h" OM_uint32 -gssEapEncodeInnerTokens(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) { - major = GSS_S_FAILURE; - *minor = ENOMEM; - 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] & ITOK_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); - - major = GSS_S_COMPLETE; - *minor = 0; - -cleanup: - if (GSS_ERROR(major)) { - gss_release_buffer(&tmpMinor, buffer); - } - - return major; -} - -OM_uint32 gssEapDecodeInnerTokens(OM_uint32 *minor, const gss_buffer_t buffer, gss_buffer_set_t *pExtensions, @@ -172,7 +106,7 @@ gssEapDecodeInnerTokens(OM_uint32 *minor, types[extensions->count] = load_uint32_be(&p[0]); extension.length = load_uint32_be(&p[4]); - if (remain < 8 + extension.length) { + if (remain < ITOK_HEADER_LENGTH + extension.length) { major = GSS_S_DEFECTIVE_TOKEN; *minor = GSSEAP_TOK_TRUNC; goto cleanup; @@ -183,8 +117,8 @@ gssEapDecodeInnerTokens(OM_uint32 *minor, if (GSS_ERROR(major)) goto cleanup; - p += 8 + extension.length; - remain -= 8 + extension.length; + p += ITOK_HEADER_LENGTH + extension.length; + remain -= ITOK_HEADER_LENGTH + extension.length; } while (remain != 0); cleanup: @@ -201,6 +135,215 @@ cleanup: } /* + * Add some data to the initiator/acceptor conversation. + */ +static OM_uint32 +recordTokens(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_buffer_t tokens, + size_t tokensCount) +{ + unsigned char *buf; + size_t i, size, offset; + + size = ctx->conversation.length; + + for (i = 0; i < tokensCount; i++) + size += tokens[i].length; + + buf = GSSEAP_REALLOC(ctx->conversation.value, size); + if (buf == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + offset = ctx->conversation.length; + + ctx->conversation.length = size; + ctx->conversation.value = buf; + + for (i = 0; i < tokensCount; i++) { + memcpy(buf + offset, tokens[i].value, tokens[i].length); + offset += tokens[i].length; + } + + *minor = 0; + return GSS_S_COMPLETE; +} + +/* + * Record the context token header. + */ +OM_uint32 +gssEapRecordContextTokenHeader(OM_uint32 *minor, + gss_ctx_id_t ctx, + enum gss_eap_token_type tokType) +{ + unsigned char wireOidHeader[2], wireTokType[2]; + gss_buffer_desc buffers[3]; + + assert(ctx->mechanismUsed != GSS_C_NO_OID); + + wireOidHeader[0] = 0x06; + wireOidHeader[1] = ctx->mechanismUsed->length; + buffers[0].length = sizeof(wireOidHeader); + buffers[0].value = wireOidHeader; + + buffers[1].length = ctx->mechanismUsed->length; + buffers[1].value = ctx->mechanismUsed->elements; + + store_uint16_be(tokType, wireTokType); + buffers[2].length = sizeof(wireTokType); + buffers[2].value = wireTokType; + + return recordTokens(minor, ctx, buffers, sizeof(buffers)/sizeof(buffers[0])); +} + +/* + * Record an inner context token. + */ +OM_uint32 +gssEapRecordInnerContextToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_buffer_t innerToken, + OM_uint32 itokType) +{ + gss_buffer_desc buffers[2]; + unsigned char itokHeader[ITOK_HEADER_LENGTH]; + + assert(innerToken != GSS_C_NO_BUFFER); + + store_uint32_be(itokType, &itokHeader[0]); + store_uint32_be(innerToken->length, &itokHeader[4]); + buffers[0].length = sizeof(itokHeader); + buffers[0].value = itokHeader; + + buffers[1] = *innerToken; + + return recordTokens(minor, ctx, buffers, sizeof(buffers)/sizeof(buffers[0])); +} + +OM_uint32 +gssEapVerifyContextToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t inputToken, + enum gss_eap_token_type tokType, + gss_buffer_t innerInputToken) +{ + OM_uint32 major; + size_t bodySize; + unsigned char *p = (unsigned char *)inputToken->value; + gss_OID_desc oidBuf; + gss_OID oid; + enum gss_eap_token_type actualTokType; + gss_buffer_desc tokenBuf; + + if (ctx->mechanismUsed != GSS_C_NO_OID) { + oid = ctx->mechanismUsed; + } else { + oidBuf.elements = NULL; + oidBuf.length = 0; + oid = &oidBuf; + } + + major = verifyTokenHeader(minor, oid, &bodySize, &p, + inputToken->length, &actualTokType); + if (GSS_ERROR(major)) + return major; + + if (actualTokType != tokType) { + *minor = GSSEAP_WRONG_TOK_ID; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (ctx->mechanismUsed == GSS_C_NO_OID) { + major = gssEapCanonicalizeOid(minor, oid, 0, &ctx->mechanismUsed); + if (GSS_ERROR(major)) + return major; + } + + innerInputToken->length = bodySize; + innerInputToken->value = p; + + /* + * Add OID, tokenType, body to conversation; variable length + * header omitted. A better API to verifyTokenHeader would + * avoid this ugly pointer arithmetic. XXX FIXME + */ + tokenBuf.value = p - (2 + oid->length + 2); + tokenBuf.length = 2 + oid->length + 2 + bodySize; + + major = recordTokens(minor, ctx, &tokenBuf, 1); + if (GSS_ERROR(major)) + return major; + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapEncodeSupportedExts(OM_uint32 *minor, + OM_uint32 *types, + size_t typesCount, + gss_buffer_t outputToken) +{ + size_t i; + unsigned char *p; + + outputToken->value = GSSEAP_MALLOC(4 * typesCount); + if (outputToken->value == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + p = (unsigned char *)outputToken->value; + + outputToken->length = 4 * typesCount; + + for (i = 0; i < typesCount; i++) { + store_uint32_be(types[i], p); + p += 4; + } + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapProcessSupportedExts(OM_uint32 *minor, + gss_buffer_t inputToken, + struct gss_eap_itok_map *map, + size_t mapCount, + OM_uint32 *flags) +{ + size_t i; + unsigned char *p; + + if ((inputToken->length % 4) != 0) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + p = (unsigned char *)inputToken->value; + + for (i = 0; i < inputToken->length / 4; i++) { + OM_uint32 type = load_uint32_be(p); + size_t j; + + for (j = 0; j < mapCount; j++) { + if (map->type == type) { + *flags |= map->flag; + break; + } + } + + p += 4; + } + + *minor = 0; + return GSS_S_COMPLETE; +} + +/* * $Id: util_token.c 23457 2009-12-08 00:04:48Z tlyu $ */ @@ -305,12 +448,8 @@ der_read_length(unsigned char **buf, ssize_t *bufsize) /* returns the length of a token, given the mech oid and the body size */ size_t -tokenSize(const gss_OID_desc *mech, size_t body_size) +tokenSize(size_t body_size) { - assert(mech != GSS_C_NO_OID); - - /* set body_size to sequence contents size */ - body_size += 4 + (size_t) mech->length; /* NEED overflow check */ return 1 + der_length_size(body_size) + body_size; } @@ -319,20 +458,11 @@ tokenSize(const gss_OID_desc *mech, size_t body_size) void makeTokenHeader( - const gss_OID_desc *mech, size_t body_size, - unsigned char **buf, - enum gss_eap_token_type tok_type) + unsigned char **buf) { *(*buf)++ = 0x60; - der_write_length(buf, (tok_type == -1) ?2:4 + mech->length + body_size); - *(*buf)++ = 0x06; - *(*buf)++ = (unsigned char)mech->length; - memcpy(*buf, mech->elements, mech->length); - *buf += mech->length; - assert(tok_type != TOK_TYPE_NONE); - *(*buf)++ = (unsigned char)((tok_type>>8) & 0xff); - *(*buf)++ = (unsigned char)(tok_type & 0xff); + der_write_length(buf, body_size); } /*