From: Luke Howard Date: Tue, 15 Mar 2011 08:57:31 +0000 (+1100) Subject: Merge branch 'master' into tlv-mic X-Git-Url: http://www.project-moonshot.org/gitweb/?a=commitdiff_plain;h=a9dc729b5479c7c1f36dd0b106ef4720dca3f075;hp=eac53185dfa9b9d81095d8ae86bce84529de41f9;p=moonshot.git Merge branch 'master' into tlv-mic Conflicts: mech_eap/util_name.c --- diff --git a/mech_eap/accept_sec_context.c b/mech_eap/accept_sec_context.c index 5829ed9..d8463d0 100644 --- a/mech_eap/accept_sec_context.c +++ b/mech_eap/accept_sec_context.c @@ -82,6 +82,7 @@ acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred) major = gssEapImportName(minor, &nameBuf, (ctx->gssFlags & GSS_C_ANON_FLAG) ? GSS_C_NT_ANONYMOUS : GSS_C_NT_USER_NAME, + ctx->mechanismUsed, &ctx->initiatorName); if (GSS_ERROR(major)) return major; @@ -125,6 +126,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, @@ -225,7 +269,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; } @@ -272,7 +316,7 @@ importInitiatorIdentity(OM_uint32 *minor, gssEapReleaseName(&tmpMinor, &ctx->initiatorName); return gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME, - &ctx->initiatorName); + ctx->mechanismUsed, &ctx->initiatorName); } /* @@ -585,7 +629,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) @@ -620,6 +664,9 @@ eapGssSmAcceptGssChannelBindings(OM_uint32 *minor, OM_uint32 major, tmpMinor; gss_iov_buffer_desc iov[2]; + if (ctx->flags & CTX_FLAG_KRB_REAUTH) + return GSS_S_CONTINUE_NEEDED; + iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE; iov[0].buffer.length = 0; iov[0].buffer.value = NULL; @@ -666,29 +713,37 @@ 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; + + major = gssEapVerifyConversationMIC(minor, ctx, inputToken); + if (GSS_ERROR(major)) + return major; + GSSEAP_SM_TRANSITION_NEXT(ctx); *minor = 0; @@ -697,27 +752,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, @@ -725,22 +798,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 { @@ -761,15 +832,15 @@ static struct gss_eap_sm eapGssAcceptorSm[] = { 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 { @@ -782,10 +853,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 }, }; @@ -935,7 +1006,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) @@ -943,6 +1014,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 @@ -953,11 +1025,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->kerberosCtx, cred->krbCred, inputToken, - chanBindings, + &wireChanBindings, &krbInitiator, &mech, outputToken, @@ -968,19 +1054,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->kerberosCtx, 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 2ea2c5c..eb52b40 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) { @@ -136,10 +136,10 @@ gssEapExportSecContext(OM_uint32 *minor, * The partial context is only transmitted for unestablished acceptor * contexts. */ - if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) { - assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0); - - major = gssEapExportPartialContext(minor, ctx, &partialCtx); + if (!CTX_IS_INITIATOR(ctx) && + !CTX_IS_ESTABLISHED(ctx) && + ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0)) { + major = exportPartialRadiusContext(minor, ctx, &partialCtx); if (GSS_ERROR(major)) goto cleanup; } @@ -151,6 +151,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 */ @@ -186,6 +189,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 10cd176..a12adb4 100644 --- a/mech_eap/gssapiP_eap.h +++ b/mech_eap/gssapiP_eap.h @@ -105,6 +105,7 @@ struct gss_name_struct { GSSEAP_MUTEX mutex; /* mutex protects attrCtx */ OM_uint32 flags; + gss_OID mechanismUsed; /* this is immutable */ krb5_principal krbPrincipal; /* this is immutable */ struct gss_eap_attr_ctx *attrCtx; }; @@ -138,6 +139,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) @@ -202,6 +204,7 @@ struct gss_ctx_id_struct #define kerberosCtx ctxU.kerberos #endif } ctxU; + gss_buffer_desc conversation; }; #define TOK_FLAG_SENDER_IS_ACCEPTOR 0x01 diff --git a/mech_eap/import_name.c b/mech_eap/import_name.c index d20d75c..87f0830 100644 --- a/mech_eap/import_name.c +++ b/mech_eap/import_name.c @@ -43,5 +43,5 @@ gss_import_name(OM_uint32 *minor, gss_name_t *output_name) { return gssEapImportName(minor, import_name_buffer, - input_name_type, output_name); + input_name_type, GSS_C_NO_OID, output_name); } diff --git a/mech_eap/import_sec_context.c b/mech_eap/import_sec_context.c index 08c5005..1821431 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) @@ -228,6 +228,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; +} + +static OM_uint32 gssEapImportContext(OM_uint32 *minor, gss_buffer_t token, gss_ctx_id_t ctx) @@ -304,11 +345,15 @@ gssEapImportContext(OM_uint32 *minor, * acceptor contexts. */ if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) { - assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0); - - major = gssEapImportPartialContext(minor, &p, &remain, 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 1f445bc..de0b605 100644 --- a/mech_eap/init_sec_context.c +++ b/mech_eap/init_sec_context.c @@ -429,21 +429,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) { @@ -463,7 +473,7 @@ eapGssSmInitGssReauth(OM_uint32 *minor, (gss_OID)gss_mech_krb5, reqFlags | GSS_C_MUTUAL_FLAG, timeReq, - chanBindings, + &wireChanBindings, inputToken, &actualMech, outputToken, @@ -480,13 +490,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; } @@ -543,7 +558,9 @@ eapGssSmInitAcceptorName(OM_uint32 *minor, ctx->acceptorName == GSS_C_NO_NAME) { /* Accept target name hint from acceptor */ major = gssEapImportName(minor, inputToken, - GSS_C_NT_USER_NAME, &ctx->acceptorName); + GSS_C_NT_USER_NAME, + ctx->mechanismUsed, + &ctx->acceptorName); if (GSS_ERROR(major)) return major; } @@ -561,6 +578,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, @@ -585,7 +646,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); @@ -689,7 +750,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); @@ -714,6 +775,9 @@ eapGssSmInitGssChannelBindings(OM_uint32 *minor, OM_uint32 major; gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER; + if (ctx->flags & CTX_FLAG_KRB_REAUTH) + return GSS_S_CONTINUE_NEEDED; + if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS) buffer = chanBindings->application_data; @@ -758,39 +822,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; @@ -798,6 +874,9 @@ eapGssSmInitCompleteAcceptorExts(OM_uint32 *minor, return GSS_S_COMPLETE; } +/* + * Initiator state machine. + */ static struct gss_eap_sm eapGssInitiatorSm[] = { { ITOK_TYPE_CONTEXT_ERR, @@ -813,6 +892,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, @@ -852,15 +938,15 @@ static struct gss_eap_sm eapGssInitiatorSm[] = { 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 { @@ -873,11 +959,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/util.h b/mech_eap/util.h index ddf95a9..1927f17 100644 --- a/mech_eap/util.h +++ b/mech_eap/util.h @@ -164,52 +164,60 @@ 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 /* 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_INITIATOR_MIC 0x0000000A /* required */ +#define ITOK_TYPE_ACCEPTOR_MIC 0x0000000B /* required */ +#define ITOK_TYPE_SUPPORTED_ACCEPTOR_EXTS 0x0000000C /* optional */ +#define ITOK_TYPE_SUPPORTED_INITIATOR_EXTS 0x0000000D /* optional */ +#define ITOK_TYPE_VERSION_INFO 0x0000000E /* optional */ +#define ITOK_TYPE_VENDOR_INFO 0x0000000F /* 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 */ 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 -gssEapDisplayName(OM_uint32 *minor, - gss_name_t name, - gss_buffer_t output_name_buffer, - gss_OID *output_name_type); +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); @@ -467,6 +475,7 @@ OM_uint32 gssEapExportNameInternal(OM_uint32 *minor, OM_uint32 gssEapImportName(OM_uint32 *minor, const gss_buffer_t input_name_buffer, gss_OID input_name_type, + gss_OID input_mech_type, gss_name_t *output_name); OM_uint32 gssEapImportNameInternal(OM_uint32 *minor, const gss_buffer_t input_name_buffer, @@ -477,6 +486,12 @@ gssEapDuplicateName(OM_uint32 *minor, const gss_name_t input_name, gss_name_t *dest_name); +OM_uint32 +gssEapDisplayName(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t output_name_buffer, + gss_OID *output_name_type); + /* util_oid.c */ OM_uint32 composeOid(OM_uint32 *minor_status, @@ -586,7 +601,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 */ @@ -613,24 +628,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_context.c b/mech_eap/util_context.c index 03bad1c..ed6edac 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,95 +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) { - if (!gssEapIsConcreteMechanismOid(oid)) { - *minor = GSSEAP_WRONG_MECH; - return GSS_S_BAD_MECH; - } + *convMIC = iov[1].buffer; - if (!gssEapInternalizeOid(oid, &ctx->mechanismUsed)) { - major = duplicateOid(minor, oid, &ctx->mechanismUsed); - if (GSS_ERROR(major)) - return major; - } + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapVerifyConversationMIC(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t convMIC) +{ + OM_uint32 major; + gss_iov_buffer_desc iov[3]; + int confState; + size_t tokenHeaderLength; + + if (convMIC == GSS_C_NO_BUFFER || convMIC->length < 16) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_BAD_SIG; } - innerInputToken->length = bodySize; - innerInputToken->value = p; + iov[0].type = GSS_IOV_BUFFER_TYPE_DATA; + iov[0].buffer = ctx->conversation; - *minor = 0; - return GSS_S_COMPLETE; + /* + * 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 -gssEapContextTime(OM_uint32 *minor, - gss_ctx_id_t context_handle, - OM_uint32 *time_rec) +gssEapMakeTokenChannelBindings(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_channel_bindings_t userChanBindings, + gss_buffer_t inputToken, + gss_channel_bindings_t wireChanBindings) { - *minor = 0; + gss_buffer_t wireData = &wireChanBindings->application_data; + unsigned char *p; + size_t tokenHeaderLength = 0; - if (context_handle->expiryTime == 0) { - *time_rec = GSS_C_INDEFINITE; - } else { - time_t now, lifetime; + memset(wireChanBindings, 0, sizeof(*wireChanBindings)); - time(&now); - lifetime = context_handle->expiryTime - now; - if (lifetime <= 0) { - *time_rec = 0; - return GSS_S_CONTEXT_EXPIRED; - } - *time_rec = lifetime; + 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_cred.c b/mech_eap/util_cred.c index 6330118..a2a01ba 100644 --- a/mech_eap/util_cred.c +++ b/mech_eap/util_cred.c @@ -120,6 +120,9 @@ gssEapAcquireCred(OM_uint32 *minor, { OM_uint32 major, tmpMinor; gss_cred_id_t cred; +#ifdef GSSEAP_DEBUG + gss_buffer_desc envPassword; +#endif /* XXX TODO validate with changed set_cred_option API */ *pCred = GSS_C_NO_CREDENTIAL; @@ -181,7 +184,13 @@ gssEapAcquireCred(OM_uint32 *minor, } if (nameBuf.length != 0) { - major = gssEapImportName(minor, &nameBuf, nameType, &cred->name); + gss_OID mech = GSS_C_NO_OID; + + if (cred->mechanisms != GSS_C_NO_OID_SET && + cred->mechanisms->count == 1) + mech = &cred->mechanisms->elements[0]; + + major = gssEapImportName(minor, &nameBuf, nameType, mech, &cred->name); if (GSS_ERROR(major)) goto cleanup; } @@ -189,6 +198,15 @@ gssEapAcquireCred(OM_uint32 *minor, cred->flags |= CRED_FLAG_DEFAULT_IDENTITY; } +#ifdef GSSEAP_DEBUG + if (password == GSS_C_NO_BUFFER && + (envPassword.value = getenv("GSSEAP_CREDS")) != NULL) { + envPassword.length = strlen((char *)envPassword.value); + major = duplicateBuffer(minor, &envPassword, &cred->password); + if (GSS_ERROR(major)) + goto cleanup; + } else +#endif /* GSSEAP_DEBUG */ if (password != GSS_C_NO_BUFFER) { major = duplicateBuffer(minor, password, &cred->password); if (GSS_ERROR(major)) diff --git a/mech_eap/util_name.c b/mech_eap/util_name.c index 70d23e2..dad8fb9 100644 --- a/mech_eap/util_name.c +++ b/mech_eap/util_name.c @@ -111,6 +111,7 @@ gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName) GSSEAP_KRB_INIT(&krbContext); krb5_free_principal(krbContext, name->krbPrincipal); + gssEapReleaseOid(&tmpMinor, &name->mechanismUsed); gssEapReleaseAttrContext(&tmpMinor, name); @@ -282,7 +283,10 @@ gssEapImportNameInternal(OM_uint32 *minor, remain = nameBuffer->length; if (flags & EXPORT_NAME_FLAG_OID) { - if (remain < 6 + GSS_EAP_MECHANISM->length) + gss_OID_desc mech; + + /* TOK_ID || MECH_OID_LEN || MECH_OID */ + if (remain < 6) return GSS_S_BAD_NAME; if (flags & EXPORT_NAME_FLAG_COMPOSITE) @@ -297,18 +301,34 @@ gssEapImportNameInternal(OM_uint32 *minor, /* MECH_OID_LEN */ len = load_uint16_be(p); - if (len != 2 + GSS_EAP_MECHANISM->length) + if (len < 2) return GSS_S_BAD_NAME; UPDATE_REMAIN(2); /* MECH_OID */ if (p[0] != 0x06) return GSS_S_BAD_NAME; - if (p[1] != GSS_EAP_MECHANISM->length) - return GSS_S_BAD_MECH; - if (memcmp(&p[2], GSS_EAP_MECHANISM->elements, GSS_EAP_MECHANISM->length)) - return GSS_S_BAD_MECH; - UPDATE_REMAIN(2 + GSS_EAP_MECHANISM->length); + + mech.length = p[1]; + mech.elements = &p[2]; + + CHECK_REMAIN(mech.length); + + if (!gssEapIsMechanismOid(&mech)) { + major = GSS_S_BAD_NAME; + *minor = GSSEAP_WRONG_MECH; + goto cleanup; + } + + if (oidEqual(&mech, GSS_EAP_MECHANISM)) { + name->mechanismUsed = GSS_C_NO_OID; + } else if (!gssEapInternalizeOid(&mech, &name->mechanismUsed)) { + major = duplicateOid(minor, &mech, &name->mechanismUsed); + if (GSS_ERROR(major)) + goto cleanup; + } + + UPDATE_REMAIN(2 + mech.length); } /* NAME_LEN */ @@ -379,7 +399,8 @@ OM_uint32 gssEapImportName(OM_uint32 *minor, const gss_buffer_t nameBuffer, gss_OID nameType, - gss_name_t *name) + gss_OID mechType, + gss_name_t *pName) { struct gss_eap_name_import_provider nameTypes[] = { { GSS_C_NT_USER_NAME, importUserName }, @@ -393,18 +414,36 @@ gssEapImportName(OM_uint32 *minor, #endif }; size_t i; - - *name = GSS_C_NO_NAME; + OM_uint32 major = GSS_S_BAD_NAMETYPE; + OM_uint32 tmpMinor; + gss_name_t name = GSS_C_NO_NAME; if (nameType == GSS_C_NO_OID) nameType = nameTypes[0].oid; for (i = 0; i < sizeof(nameTypes) / sizeof(nameTypes[0]); i++) { - if (oidEqual(nameTypes[i].oid, nameType)) - return nameTypes[i].import(minor, nameBuffer, name); + if (oidEqual(nameTypes[i].oid, nameType)) { + major = nameTypes[i].import(minor, nameBuffer, &name); + break; + } } - return GSS_S_BAD_NAMETYPE; + if (major == GSS_S_COMPLETE && + mechType != GSS_C_NO_OID) { + assert(gssEapIsConcreteMechanismOid(mechType)); + assert(name->mechanismUsed == GSS_C_NO_OID); + + if (!gssEapInternalizeOid(mechType, &name->mechanismUsed)) { + major = duplicateOid(minor, mechType, &name->mechanismUsed); + } + } + + if (GSS_ERROR(major)) + gssEapReleaseName(&tmpMinor, &name); + else + *pName = name; + + return major; } OM_uint32 @@ -428,10 +467,16 @@ gssEapExportNameInternal(OM_uint32 *minor, size_t krbNameLen, exportedNameLen; unsigned char *p; gss_buffer_desc attrs = GSS_C_EMPTY_BUFFER; + gss_OID mech; exportedName->length = 0; exportedName->value = NULL; + if (name->mechanismUsed != GSS_C_NO_OID) + mech = name->mechanismUsed; + else + mech = GSS_EAP_MECHANISM; + GSSEAP_KRB_INIT(&krbContext); *minor = krb5_unparse_name(krbContext, name->krbPrincipal, &krbName); @@ -443,7 +488,7 @@ gssEapExportNameInternal(OM_uint32 *minor, exportedNameLen = 0; if (flags & EXPORT_NAME_FLAG_OID) { - exportedNameLen += 6 + GSS_EAP_MECHANISM->length; + exportedNameLen += 6 + mech->length; } exportedNameLen += 4 + krbNameLen; if (flags & EXPORT_NAME_FLAG_COMPOSITE) { @@ -470,14 +515,14 @@ gssEapExportNameInternal(OM_uint32 *minor, : TOK_TYPE_EXPORT_NAME, p); p += 2; - store_uint16_be(GSS_EAP_MECHANISM->length + 2, p); + store_uint16_be(mech->length + 2, p); p += 2; /* MECH_OID */ *p++ = 0x06; - *p++ = GSS_EAP_MECHANISM->length & 0xff; - memcpy(p, GSS_EAP_MECHANISM->elements, GSS_EAP_MECHANISM->length); - p += GSS_EAP_MECHANISM->length; + *p++ = mech->length & 0xff; + memcpy(p, mech->elements, mech->length); + p += mech->length; } /* NAME_LEN */ @@ -528,6 +573,22 @@ gssEapDuplicateName(OM_uint32 *minor, return major; } + if (input_name->mechanismUsed == GSS_C_NO_OID) { + name->mechanismUsed = GSS_C_NO_OID; + } else if (gssEapIsConcreteMechanismOid(input_name->mechanismUsed)) { + if (!gssEapInternalizeOid(input_name->mechanismUsed, + &name->mechanismUsed)) { + major = duplicateOid(minor, input_name->mechanismUsed, + &name->mechanismUsed); + if (GSS_ERROR(major)) + goto cleanup; + } + } else { + major = GSS_S_BAD_MECH; + *minor = GSSEAP_WRONG_MECH; + goto cleanup; + } + name->flags = input_name->flags; *minor = krb5_copy_principal(krbContext, input_name->krbPrincipal, diff --git a/mech_eap/util_reauth.c b/mech_eap/util_reauth.c index f1112ca..22f089f 100644 --- a/mech_eap/util_reauth.c +++ b/mech_eap/util_reauth.c @@ -735,7 +735,7 @@ gssEapGlueToMechName(OM_uint32 *minor, goto cleanup; major = gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME, - pMechName); + ctx->mechanismUsed, pMechName); if (GSS_ERROR(major)) goto cleanup; 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..f1ba1f2 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,222 @@ 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) { + if (!gssEapIsConcreteMechanismOid(oid)) { + *minor = GSSEAP_WRONG_MECH; + return GSS_S_BAD_MECH; + } + + if (!gssEapInternalizeOid(oid, &ctx->mechanismUsed)) { + major = duplicateOid(minor, oid, &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 +455,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 +465,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); } /*