From 938e32459851eae0b4580b067348c39ed5921fd8 Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Tue, 8 Mar 2011 13:32:56 +1100 Subject: [PATCH] initial TLV refactor --- Makefile.am | 4 +- accept_sec_context.c | 339 ++++++++++++++++++------------------ gssapiP_eap.h | 35 +++- gsseap_err.et | 6 +- import_sec_context.c | 2 +- init_sec_context.c | 478 +++++++++++++++++++++++++------------------------- util.h | 90 +++++----- util_context.c | 2 +- util_exts.c | 484 --------------------------------------------------- util_sm.c | 312 +++++++++++++++++++++++++++++++++ util_token.c | 145 ++++++++++++++- 11 files changed, 936 insertions(+), 961 deletions(-) delete mode 100644 util_exts.c create mode 100644 util_sm.c diff --git a/Makefile.am b/Makefile.am index 17757f4..f9be930 100644 --- a/Makefile.am +++ b/Makefile.am @@ -74,7 +74,6 @@ mech_eap_la_SOURCES = \ util_cksum.c \ util_cred.c \ util_crypt.c \ - util_exts.c \ util_krb.c \ util_lucid.c \ util_mech.c \ @@ -84,6 +83,7 @@ mech_eap_la_SOURCES = \ util_radius.cpp \ util_saml.cpp \ util_shib.cpp \ + util_sm.c \ util_token.c \ verify_mic.c \ wrap.c \ @@ -114,8 +114,6 @@ endif gsseap_err.h gsseap_err.c: gsseap_err.et $(COMPILE_ET) $< - - radsec_err.h radsec_err.c: radsec_err.et $(COMPILE_ET) $< diff --git a/accept_sec_context.c b/accept_sec_context.c index d192d71..6d65647 100644 --- a/accept_sec_context.c +++ b/accept_sec_context.c @@ -40,10 +40,14 @@ #ifdef GSSEAP_ENABLE_REAUTH static OM_uint32 eapGssSmAcceptGssReauth(OM_uint32 *minor, - gss_ctx_id_t ctx, gss_cred_id_t cred, - gss_buffer_t inputToken, + gss_ctx_id_t ctx, + gss_name_t target __attribute__((__unused__)), + gss_OID mech __attribute__((__unused__)), + OM_uint32 reqFlags __attribute__((__unused__)), + OM_uint32 timeReq __attribute__((__unused__)), gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, gss_buffer_t outputToken); #endif @@ -125,16 +129,25 @@ acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred) */ static OM_uint32 eapGssSmAcceptIdentity(OM_uint32 *minor, - gss_ctx_id_t ctx, gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target __attribute__((__unused__)), + gss_OID mech __attribute__((__unused__)), + OM_uint32 reqFlags __attribute__((__unused__)), + OM_uint32 timeReq __attribute__((__unused__)), + gss_channel_bindings_t chanBindings __attribute__((__unused__)), gss_buffer_t inputToken, - gss_channel_bindings_t chanBindings, gss_buffer_t outputToken) { OM_uint32 major; struct wpabuf *reqData; gss_buffer_desc pktBuffer; + if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) { + *minor = GSSEAP_CRED_MECH_MISMATCH; + return GSS_S_BAD_MECH; + } + if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) { *minor = GSSEAP_WRONG_SIZE; return GSS_S_DEFECTIVE_TOKEN; @@ -162,12 +175,10 @@ eapGssSmAcceptIdentity(OM_uint32 *minor, if (GSS_ERROR(major)) return major; - ctx->state = GSSEAP_STATE_AUTHENTICATE; - wpabuf_free(reqData); *minor = 0; - return GSS_S_CONTINUE_NEEDED; + return GSS_S_COMPLETE; /* advance state */ } /* @@ -406,10 +417,14 @@ fail: */ static OM_uint32 eapGssSmAcceptAuthenticate(OM_uint32 *minor, - gss_ctx_id_t ctx, gss_cred_id_t cred, - gss_buffer_t inputToken, + gss_ctx_id_t ctx, + gss_name_t target __attribute__((__unused__)), + gss_OID mech __attribute__((__unused__)), + OM_uint32 reqFlags __attribute__((__unused__)), + OM_uint32 timeReq __attribute__((__unused__)), gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, gss_buffer_t outputToken) { OM_uint32 major, tmpMinor; @@ -480,7 +495,6 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor, switch (frresp->code) { case PW_AUTHENTICATION_ACK: case PW_ACCESS_CHALLENGE: - major = GSS_S_CONTINUE_NEEDED; break; case PW_AUTHENTICATION_REJECT: *minor = GSSEAP_RADIUS_AUTH_FAILURE; @@ -508,6 +522,8 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor, &ctx->acceptorCtx.state, TRUE); if (GSS_ERROR(major) && *minor != GSSEAP_NO_SUCH_ATTR) goto cleanup; + + major = GSS_S_CONTINUE_NEEDED; } else { ctx->acceptorCtx.vps = frresp->vps; frresp->vps = NULL; @@ -519,12 +535,9 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor, if (GSS_ERROR(major)) goto cleanup; - ctx->state = GSSEAP_STATE_EXTENSIONS_REQ; + major = GSS_S_COMPLETE; /* advance state */ } - *minor = 0; - major = GSS_S_CONTINUE_NEEDED; - cleanup: if (request != NULL) rs_request_destroy(request); @@ -535,114 +548,141 @@ cleanup: } static OM_uint32 -eapGssSmAcceptExtensionsReq(OM_uint32 *minor, - gss_ctx_id_t ctx, - gss_cred_id_t cred, - gss_buffer_t inputToken, - gss_channel_bindings_t chanBindings, - gss_buffer_t outputToken) +eapGssSmAcceptGssChannelBindings(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target __attribute__((__unused__)), + gss_OID mech __attribute__((__unused__)), + OM_uint32 reqFlags __attribute__((__unused__)), + OM_uint32 timeReq __attribute__((__unused__)), + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) { - OM_uint32 major; + OM_uint32 major, tmpMinor; + gss_iov_buffer_desc iov[2]; + + iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE; + iov[0].buffer.length = 0; + iov[0].buffer.value = NULL; - major = gssEapVerifyExtensions(minor, cred, ctx, chanBindings, inputToken); + iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM; + iov[1].buffer = *inputToken; + + major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL, + iov, 2, TOK_TYPE_WRAP); if (GSS_ERROR(major)) - return major; + return GSS_S_BAD_BINDINGS; - outputToken->length = 0; - outputToken->value = NULL; + if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS && + !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) { + major = GSS_S_BAD_BINDINGS; + *minor = GSSEAP_BINDINGS_MISMATCH; + } else { + major = GSS_S_CONTINUE_NEEDED; /* process additional extensions */ + *minor = 0; + } - ctx->state = GSSEAP_STATE_EXTENSIONS_RESP; + gss_release_buffer(&tmpMinor, &iov[0].buffer); - *minor = 0; - return GSS_S_CONTINUE_NEEDED; + return major; } +#ifdef GSSEAP_ENABLE_REAUTH static OM_uint32 -eapGssSmAcceptExtensionsResp(OM_uint32 *minor, - gss_ctx_id_t ctx, - gss_cred_id_t cred, - gss_buffer_t inputToken, - gss_channel_bindings_t chanBindings, - gss_buffer_t outputToken) +eapGssSmAcceptReauthCreds(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target __attribute__((__unused__)), + gss_OID mech __attribute__((__unused__)), + OM_uint32 reqFlags __attribute__((__unused__)), + OM_uint32 timeReq __attribute__((__unused__)), + gss_channel_bindings_t chanBindings __attribute__((__unused__)), + gss_buffer_t inputToken, + gss_buffer_t outputToken) { OM_uint32 major; - major = gssEapMakeExtensions(minor, cred, ctx, chanBindings, outputToken); + /* + * If we're built with fast reauthentication enabled, then + * fabricate a ticket from the initiator to ourselves. + */ + major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken); if (GSS_ERROR(major)) return major; - ctx->state = GSSEAP_STATE_ESTABLISHED; - *minor = 0; - return GSS_S_COMPLETE; -} - -static OM_uint32 -eapGssSmAcceptEstablished(OM_uint32 *minor, - gss_ctx_id_t ctx, - gss_cred_id_t cred, - gss_buffer_t inputToken, - gss_channel_bindings_t chanBindings, - gss_buffer_t outputToken) -{ - /* Called with already established context */ - *minor = GSSEAP_CONTEXT_ESTABLISHED; - return GSS_S_BAD_STATUS; + return GSS_S_CONTINUE_NEEDED; /* process additional extensions */ } +#endif static OM_uint32 -makeErrorToken(OM_uint32 *minor, - OM_uint32 majorStatus, - OM_uint32 minorStatus, - gss_buffer_t outputToken) +eapGssSmAcceptNegoExtFinished(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target __attribute__((__unused__)), + gss_OID mech __attribute__((__unused__)), + OM_uint32 reqFlags __attribute__((__unused__)), + OM_uint32 timeReq __attribute__((__unused__)), + gss_channel_bindings_t chanBindings __attribute__((__unused__)), + gss_buffer_t inputToken, + gss_buffer_t outputToken) { - unsigned char errorData[8]; - gss_buffer_desc errorBuffer; - - assert(GSS_ERROR(majorStatus)); - - /* - * Only return error codes that the initiator could have caused, - * to avoid information leakage. - */ - if (IS_RADIUS_ERROR(minorStatus)) { - /* Squash RADIUS error codes */ - minorStatus = GSSEAP_RADIUS_PROT_FAILURE; - } else if (!IS_WIRE_ERROR(minorStatus)) { - /* Don't return non-wire error codes */ - return GSS_S_COMPLETE; - } - - minorStatus -= ERROR_TABLE_BASE_eapg; - - store_uint32_be(majorStatus, &errorData[0]); - store_uint32_be(minorStatus, &errorData[4]); - - errorBuffer.length = sizeof(errorData); - errorBuffer.value = errorData; - - return duplicateBuffer(minor, &errorBuffer, outputToken); + *minor = 0; + return GSS_S_COMPLETE; /* advance state */ } -static struct gss_eap_acceptor_sm { - enum gss_eap_token_type inputTokenType; - enum gss_eap_token_type outputTokenType; - OM_uint32 (*processToken)(OM_uint32 *, - gss_ctx_id_t, - gss_cred_id_t, - gss_buffer_t, - gss_channel_bindings_t, - gss_buffer_t); -} eapGssAcceptorSm[] = { - { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, eapGssSmAcceptIdentity }, - { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, eapGssSmAcceptAuthenticate }, - { TOK_TYPE_EXT_REQ, TOK_TYPE_NONE, eapGssSmAcceptExtensionsReq }, - { TOK_TYPE_NONE, TOK_TYPE_EXT_RESP, eapGssSmAcceptExtensionsResp }, - { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmAcceptEstablished }, - { TOK_TYPE_NONE, TOK_TYPE_CONTEXT_ERR, NULL }, +static struct gss_eap_sm eapGssAcceptorSm[] = { #ifdef GSSEAP_ENABLE_REAUTH - { TOK_TYPE_GSS_REAUTH, TOK_TYPE_GSS_REAUTH, eapGssSmAcceptGssReauth }, + { + ITOK_TYPE_REAUTH_REQ, + ITOK_TYPE_REAUTH_RESP, + GSSEAP_STATE_INITIAL, + 0, /* critical */ + 0, /* required */ + eapGssSmAcceptGssReauth, + }, #endif + { + ITOK_TYPE_NONE, + ITOK_TYPE_EAP_REQ, + GSSEAP_STATE_INITIAL, + 1, /* critical */ + 1, /* required */ + eapGssSmAcceptIdentity, + }, + { + ITOK_TYPE_EAP_RESP, + ITOK_TYPE_EAP_REQ, + GSSEAP_STATE_AUTHENTICATE, + 1, /* critical */ + 1, /* required */ + eapGssSmAcceptAuthenticate + }, + { + ITOK_TYPE_GSS_CHANNEL_BINDINGS, + ITOK_TYPE_NONE, + GSSEAP_STATE_NEGO_EXT, + 1, /* critical */ + 1, /* required */ + eapGssSmAcceptGssChannelBindings, + }, + { + ITOK_TYPE_NONE, + ITOK_TYPE_REAUTH_CREDS, + GSSEAP_STATE_NEGO_EXT, + 0, /* critical */ + 0, /* required */ + eapGssSmAcceptReauthCreds, + }, + { + ITOK_TYPE_NONE, + ITOK_TYPE_NONE, + GSSEAP_STATE_NEGO_EXT, + 1, /* critical */ + 1, /* required */ + eapGssSmAcceptNegoExtFinished + }, }; OM_uint32 @@ -658,14 +698,8 @@ gss_accept_sec_context(OM_uint32 *minor, OM_uint32 *time_rec, gss_cred_id_t *delegated_cred_handle) { - OM_uint32 major; - OM_uint32 tmpMajor, tmpMinor; + OM_uint32 major, tmpMinor; gss_ctx_id_t ctx = *context_handle; - struct gss_eap_acceptor_sm *sm = NULL; - gss_buffer_desc innerInputToken = GSS_C_EMPTY_BUFFER; - gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER; - enum gss_eap_token_type tokType; - int initialContextToken = 0; *minor = 0; @@ -685,7 +719,6 @@ gss_accept_sec_context(OM_uint32 *minor, if (GSS_ERROR(major)) return major; - initialContextToken = 1; *context_handle = ctx; } @@ -711,58 +744,21 @@ gss_accept_sec_context(OM_uint32 *minor, GSSEAP_MUTEX_LOCK(&cred->mutex); - sm = &eapGssAcceptorSm[ctx->state]; - - major = gssEapVerifyToken(minor, ctx, input_token, - &tokType, &innerInputToken); + major = gssEapSmStep(minor, + cred, + ctx, + GSS_C_NO_NAME, + GSS_C_NO_OID, + 0, + GSS_C_INDEFINITE, + input_chan_bindings, + input_token, + output_token, + eapGssAcceptorSm, + sizeof(eapGssAcceptorSm) / sizeof(eapGssAcceptorSm[0])); if (GSS_ERROR(major)) goto cleanup; - if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) { - *minor = GSSEAP_CRED_MECH_MISMATCH; - major = GSS_S_BAD_MECH; - goto cleanup; - } - -#ifdef GSSEAP_ENABLE_REAUTH - /* - * If we're built with fast reauthentication support, it's valid - * for an initiator to send a GSS reauthentication token as its - * initial context token, causing us to short-circuit the state - * machine and process Kerberos GSS messages instead. - */ - if (tokType == TOK_TYPE_GSS_REAUTH && initialContextToken) { - ctx->state = GSSEAP_STATE_KRB_REAUTH; - } else -#endif - if (tokType != sm->inputTokenType) { - *minor = GSSEAP_WRONG_TOK_ID; - major = GSS_S_DEFECTIVE_TOKEN; - goto cleanup; - } - - do { - sm = &eapGssAcceptorSm[ctx->state]; - - major = (sm->processToken)(minor, - ctx, - cred, - &innerInputToken, - input_chan_bindings, - &innerOutputToken); - if (GSS_ERROR(major)) { - /* Possibly generate an error token */ - tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputToken); - if (GSS_ERROR(tmpMajor)) { - major = tmpMajor; - goto cleanup; - } - - sm = &eapGssAcceptorSm[GSSEAP_STATE_ERROR]; - goto send_token; - } - } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.length == 0); - if (mech_type != NULL) { if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type)) duplicateOid(&tmpMinor, ctx->mechanismUsed, mech_type); @@ -787,17 +783,6 @@ gss_accept_sec_context(OM_uint32 *minor, assert(ctx->state == GSSEAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED); -send_token: - if (innerOutputToken.value != NULL) { - tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken, - sm->outputTokenType, output_token); - if (GSS_ERROR(tmpMajor)) { - major = tmpMajor; - *minor = tmpMinor; - goto cleanup; - } - } - cleanup: if (cred != GSS_C_NO_CREDENTIAL) GSSEAP_MUTEX_UNLOCK(&cred->mutex); @@ -806,8 +791,6 @@ cleanup: if (GSS_ERROR(major)) gssEapReleaseContext(&tmpMinor, context_handle); - gss_release_buffer(&tmpMinor, &innerOutputToken); - return major; } @@ -836,25 +819,35 @@ acceptReadyKrb(OM_uint32 *minor, if (GSS_ERROR(major)) return major; - ctx->state = GSSEAP_STATE_ESTABLISHED; + ctx->state = GSSEAP_STATE_NEGO_EXT; /* skip */ *minor = 0; - return GSS_S_COMPLETE; + return GSS_S_COMPLETE; /* advance state */ } static OM_uint32 eapGssSmAcceptGssReauth(OM_uint32 *minor, - gss_ctx_id_t ctx, gss_cred_id_t cred, - gss_buffer_t inputToken, + gss_ctx_id_t ctx, + gss_name_t target __attribute__((__unused__)), + gss_OID mech, + OM_uint32 reqFlags __attribute__((__unused__)), + OM_uint32 timeReq __attribute__((__unused__)), gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, gss_buffer_t outputToken) { OM_uint32 major, tmpMinor; gss_name_t krbInitiator = GSS_C_NO_NAME; - gss_OID mech = GSS_C_NO_OID; OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE; + /* + * If we're built with fast reauthentication support, it's valid + * for an initiator to send a GSS reauthentication token as its + * initial context token, causing us to short-circuit the state + * machine and process Kerberos GSS messages instead. + */ + ctx->flags |= CTX_FLAG_KRB_REAUTH; major = gssAcceptSecContext(minor, diff --git a/gssapiP_eap.h b/gssapiP_eap.h index 18137a9..bcfdf03 100644 --- a/gssapiP_eap.h +++ b/gssapiP_eap.h @@ -141,15 +141,32 @@ struct gss_cred_id_struct #define CTX_IS_INITIATOR(ctx) (((ctx)->flags & CTX_FLAG_INITIATOR) != 0) enum gss_eap_state { - GSSEAP_STATE_IDENTITY = 0, /* identify peer */ - GSSEAP_STATE_AUTHENTICATE, /* exchange EAP messages */ - GSSEAP_STATE_EXTENSIONS_REQ, /* initiator extensions */ - GSSEAP_STATE_EXTENSIONS_RESP, /* acceptor extensions */ - GSSEAP_STATE_ESTABLISHED, /* context established */ - GSSEAP_STATE_ERROR, /* context error */ -#ifdef GSSEAP_ENABLE_REAUTH - GSSEAP_STATE_KRB_REAUTH /* fast reauthentication */ -#endif + GSSEAP_STATE_INITIAL = 0x01, /* initial state */ + GSSEAP_STATE_AUTHENTICATE = 0x02, /* exchange EAP messages */ + GSSEAP_STATE_NEGO_EXT = 0x04, /* negotiate extensions */ + GSSEAP_STATE_ESTABLISHED = 0x08, /* context established */ + GSSEAP_STATE_ALL = 0x0F +}; + +#define GSSEAP_STATE_NEXT(s) ((s) << 1) + +/* state machine entry */ +struct gss_eap_sm { + OM_uint32 inputTokenType; + OM_uint32 outputTokenType; + enum gss_eap_state validStates; + int critical; + int required; + OM_uint32 (*processToken)(OM_uint32 *, + gss_cred_id_t, + gss_ctx_id_t, + gss_name_t, + gss_OID, + OM_uint32, + OM_uint32, + gss_channel_bindings_t, + gss_buffer_t, + gss_buffer_t); }; #define CTX_IS_ESTABLISHED(ctx) ((ctx)->state == GSSEAP_STATE_ESTABLISHED) diff --git a/gsseap_err.et b/gsseap_err.et index eb5a948..0a34d12 100644 --- a/gsseap_err.et +++ b/gsseap_err.et @@ -43,8 +43,10 @@ error_code GSSEAP_BAD_TOK_HEADER, "Token header is malformed or co error_code GSSEAP_TOK_TRUNC, "Token is missing data" error_code GSSEAP_BAD_DIRECTION, "Packet was replayed in wrong direction" error_code GSSEAP_WRONG_TOK_ID, "Received token ID does not match expected token ID" -error_code GSSEAP_CRIT_EXT_UNAVAILABLE, "Critical extension unavailable" -error_code GSSEAP_MISSING_REQUIRED_EXT, "Missing required extension" +error_code GSSEAP_CRIT_ITOK_UNAVAILABLE, "Critical inner token type unavailable" +error_code GSSEAP_MISSING_REQUIRED_ITOK, "Missing required inner token" +error_code GSSEAP_DUPLICATE_ITOK, "Duplicate inner token received" +error_code GSSEAP_WRONG_ITOK, "Recieved invalid inner token for current state" error_code GSSEAP_KEY_UNAVAILABLE, "EAP key unavailable" error_code GSSEAP_KEY_TOO_SHORT, "EAP key too short" error_code GSSEAP_RADIUS_AUTH_FAILURE, "Authentication rejected by RADIUS server" diff --git a/import_sec_context.c b/import_sec_context.c index cf46d6a..0289c0c 100644 --- a/import_sec_context.c +++ b/import_sec_context.c @@ -251,7 +251,7 @@ gssEapImportContext(OM_uint32 *minor, remain -= 16; /* Validate state */ - if (ctx->state < GSSEAP_STATE_IDENTITY || + if (ctx->state < GSSEAP_STATE_INITIAL || ctx->state > GSSEAP_STATE_ESTABLISHED) return GSS_S_DEFECTIVE_TOKEN; diff --git a/init_sec_context.c b/init_sec_context.c index b80ab41..f55aec9 100644 --- a/init_sec_context.c +++ b/init_sec_context.c @@ -37,20 +37,6 @@ #include "gssapiP_eap.h" -#ifdef GSSEAP_ENABLE_REAUTH -static OM_uint32 -eapGssSmInitGssReauth(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_name_t target, - gss_OID mech, - OM_uint32 reqFlags, - OM_uint32 timeReq, - gss_channel_bindings_t chanBindings, - gss_buffer_t inputToken, - gss_buffer_t outputToken); -#endif - static OM_uint32 policyVariableToFlag(enum eapol_bool_var variable) { @@ -341,8 +327,7 @@ initBegin(OM_uint32 *minor, OM_uint32 reqFlags, OM_uint32 timeReq, gss_channel_bindings_t chanBindings, - gss_buffer_t inputToken, - gss_buffer_t outputToken) + gss_buffer_t inputToken) { OM_uint32 major; @@ -397,6 +382,116 @@ initBegin(OM_uint32 *minor, } static OM_uint32 +eapGssSmInitError(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + OM_uint32 major; + unsigned char *p; + + if (inputToken->length < 8) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + p = (unsigned char *)inputToken->value; + + major = load_uint32_be(&p[0]); + *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]); + + if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) { + major = GSS_S_FAILURE; + *minor = GSSEAP_BAD_ERROR_TOKEN; + } + + assert(GSS_ERROR(major)); + + return major; +} + +#ifdef GSSEAP_ENABLE_REAUTH +static OM_uint32 +eapGssSmInitGssReauth(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + OM_uint32 major, tmpMinor; + gss_name_t mechTarget = GSS_C_NO_NAME; + gss_OID actualMech = GSS_C_NO_OID; + OM_uint32 gssFlags, timeRec; + + assert(cred != GSS_C_NO_CREDENTIAL); + + if (ctx->state == GSSEAP_STATE_INITIAL) { + if (!gssEapCanReauthP(cred, target, timeReq)) + return GSS_S_CONTINUE_NEEDED; + + major = initBegin(minor, cred, ctx, target, mech, + reqFlags, timeReq, chanBindings, + inputToken); + if (GSS_ERROR(major)) + goto cleanup; + + ctx->flags |= CTX_FLAG_KRB_REAUTH; + } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) { + major = GSS_S_DEFECTIVE_TOKEN; + *minor = GSSEAP_WRONG_ITOK; + goto cleanup; + } + + major = gssEapMechToGlueName(minor, target, &mechTarget); + if (GSS_ERROR(major)) + goto cleanup; + + major = gssInitSecContext(minor, + cred->krbCred, + &ctx->kerberosCtx, + mechTarget, + (gss_OID)gss_mech_krb5, + reqFlags, + timeReq, + chanBindings, + inputToken, + &actualMech, + outputToken, + &gssFlags, + &timeRec); + if (GSS_ERROR(major)) + goto cleanup; + + ctx->gssFlags = gssFlags; + + if (major == GSS_S_COMPLETE) { + major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec); + if (GSS_ERROR(major)) + goto cleanup; + ctx->state = GSSEAP_STATE_NEGO_EXT; /* skip */ + } else { + major = GSS_S_COMPLETE; /* advance state */ + } + +cleanup: + gssReleaseName(&tmpMinor, &mechTarget); + + return major; +} +#endif /* GSSEAP_ENABLE_REAUTH */ + +static OM_uint32 eapGssSmInitIdentity(OM_uint32 *minor, gss_cred_id_t cred, gss_ctx_id_t ctx, @@ -411,6 +506,8 @@ eapGssSmInitIdentity(OM_uint32 *minor, OM_uint32 major; int initialContextToken; + assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0); + initialContextToken = (inputToken->length == 0); if (!initialContextToken) { *minor = GSSEAP_WRONG_SIZE; @@ -419,14 +516,15 @@ eapGssSmInitIdentity(OM_uint32 *minor, major = initBegin(minor, cred, ctx, target, mech, reqFlags, timeReq, chanBindings, - inputToken, outputToken); + inputToken); if (GSS_ERROR(major)) return major; - ctx->state = GSSEAP_STATE_AUTHENTICATE; + outputToken->length = 0; + outputToken->value = NULL; *minor = 0; - return GSS_S_CONTINUE_NEEDED; + return GSS_S_COMPLETE; /* advance state */ } static struct wpabuf emptyWpaBuffer; @@ -481,26 +579,23 @@ eapGssSmInitAuthenticate(OM_uint32 *minor, wpabuf_set(&ctx->initiatorCtx.reqData, inputToken->value, inputToken->length); - major = GSS_S_CONTINUE_NEEDED; - code = eap_peer_sm_step(ctx->initiatorCtx.eap); if (ctx->flags & CTX_FLAG_EAP_RESP) { ctx->flags &= ~(CTX_FLAG_EAP_RESP); resp = eap_get_eapRespData(ctx->initiatorCtx.eap); + major = GSS_S_CONTINUE_NEEDED; } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) { major = initReady(minor, ctx, reqFlags); if (GSS_ERROR(major)) goto cleanup; ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS); - major = GSS_S_CONTINUE_NEEDED; - ctx->state = GSSEAP_STATE_EXTENSIONS_REQ; + major = GSS_S_COMPLETE; /* advance state */ } else if (ctx->flags & CTX_FLAG_EAP_FAIL) { major = GSS_S_DEFECTIVE_CREDENTIAL; *minor = GSSEAP_PEER_AUTH_FAILURE; } else if (code == 0 && initialContextToken) { - resp = &emptyWpaBuffer; major = GSS_S_CONTINUE_NEEDED; } else { major = GSS_S_DEFECTIVE_TOKEN; @@ -512,7 +607,7 @@ cleanup: OM_uint32 tmpMajor; gss_buffer_desc respBuf; - assert(major == GSS_S_CONTINUE_NEEDED); + assert(!GSS_ERROR(major)); respBuf.length = wpabuf_len(resp); respBuf.value = (void *)wpabuf_head(resp); @@ -531,57 +626,37 @@ cleanup: } static OM_uint32 -eapGssSmInitExtensionsReq(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_name_t target, - gss_OID mech, - OM_uint32 reqFlags, - OM_uint32 timeReq, - gss_channel_bindings_t chanBindings, - gss_buffer_t inputToken, - gss_buffer_t outputToken) +eapGssSmInitGssChannelBindings(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) { OM_uint32 major; + gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER; + + if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS) + buffer = chanBindings->application_data; - major = gssEapMakeExtensions(minor, cred, ctx, chanBindings, outputToken); + major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT, + &buffer, NULL, outputToken); if (GSS_ERROR(major)) return major; assert(outputToken->value != NULL); - ctx->state = GSSEAP_STATE_EXTENSIONS_RESP; - *minor = 0; return GSS_S_CONTINUE_NEEDED; } +#ifdef GSSEAP_ENABLE_REAUTH static OM_uint32 -eapGssSmInitExtensionsResp(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_name_t target, - gss_OID mech, - OM_uint32 reqFlags, - OM_uint32 timeReq, - gss_channel_bindings_t chanBindings, - gss_buffer_t inputToken, - gss_buffer_t outputToken) -{ - OM_uint32 major; - - major = gssEapVerifyExtensions(minor, cred, ctx, chanBindings, inputToken); - if (GSS_ERROR(major)) - return major; - - ctx->state = GSSEAP_STATE_ESTABLISHED; - - *minor = 0; - return GSS_S_COMPLETE; -} - -static OM_uint32 -eapGssSmInitEstablished(OM_uint32 *minor, +eapGssSmInitReauthCreds(OM_uint32 *minor, gss_cred_id_t cred, gss_ctx_id_t ctx, gss_name_t target, @@ -592,67 +667,96 @@ eapGssSmInitEstablished(OM_uint32 *minor, gss_buffer_t inputToken, gss_buffer_t outputToken) { - /* Called with already established context */ - *minor = GSSEAP_CONTEXT_ESTABLISHED; - return GSS_S_BAD_STATUS; -} - -static OM_uint32 -eapGssSmInitError(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_name_t target, - gss_OID mech, - OM_uint32 reqFlags, - OM_uint32 timeReq, - gss_channel_bindings_t chanBindings, - gss_buffer_t inputToken, - gss_buffer_t outputToken) -{ OM_uint32 major; - unsigned char *p; - if (inputToken->length < 8) { - *minor = GSSEAP_TOK_TRUNC; - return GSS_S_DEFECTIVE_TOKEN; - } - - p = (unsigned char *)inputToken->value; - - major = load_uint32_be(&p[0]); - *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]); + major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken); + if (GSS_ERROR(major)) + return major; - if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) { - major = GSS_S_FAILURE; - *minor = GSSEAP_BAD_ERROR_TOKEN; - } + *minor = 0; + return GSS_S_CONTINUE_NEEDED; +} +#endif /* GSSEAP_ENABLE_REAUTH */ - return major; +static OM_uint32 +eapGssSmInitNegoExtFinished(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + *minor = 0; + return GSS_S_COMPLETE; /* advance state */ } -static struct gss_eap_initiator_sm { - enum gss_eap_token_type inputTokenType; - enum gss_eap_token_type outputTokenType; - OM_uint32 (*processToken)(OM_uint32 *, - gss_cred_id_t, - gss_ctx_id_t, - gss_name_t, - gss_OID, - OM_uint32, - OM_uint32, - gss_channel_bindings_t, - gss_buffer_t, - gss_buffer_t); -} eapGssInitiatorSm[] = { - { TOK_TYPE_NONE, TOK_TYPE_EAP_RESP, eapGssSmInitIdentity }, - { TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP, eapGssSmInitAuthenticate }, - { TOK_TYPE_NONE, TOK_TYPE_EXT_REQ, eapGssSmInitExtensionsReq }, - { TOK_TYPE_EXT_RESP, TOK_TYPE_NONE, eapGssSmInitExtensionsResp }, - { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmInitEstablished }, - { TOK_TYPE_CONTEXT_ERR, TOK_TYPE_NONE, eapGssSmInitError }, +static struct gss_eap_sm eapGssInitiatorSm[] = { + { + ITOK_TYPE_CONTEXT_ERR, + ITOK_TYPE_NONE, + GSSEAP_STATE_ALL, + 1, /* critical */ + 0, /* required */ + eapGssSmInitError, + }, +#ifdef GSSEAP_ENABLE_REAUTH + { + ITOK_TYPE_REAUTH_RESP, + ITOK_TYPE_REAUTH_REQ, + GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE, + 0, /* critical */ + 0, /* required */ + eapGssSmInitGssReauth, + }, +#endif + /* first-leg extensions go here, they should return GSS_S_CONTINUE_NEEDED */ + { + ITOK_TYPE_NONE, + ITOK_TYPE_NONE, + GSSEAP_STATE_INITIAL, + 1, /* critical */ + 1, /* required */ + eapGssSmInitIdentity, + }, + { + ITOK_TYPE_EAP_REQ, + ITOK_TYPE_EAP_RESP, + GSSEAP_STATE_AUTHENTICATE, + 1, /* critical */ + 1, /* required */ + eapGssSmInitAuthenticate, + }, + { + ITOK_TYPE_NONE, + ITOK_TYPE_GSS_CHANNEL_BINDINGS, + GSSEAP_STATE_NEGO_EXT, + 1, /* critical */ + 1, /* required */ + eapGssSmInitGssChannelBindings, + }, #ifdef GSSEAP_ENABLE_REAUTH - { TOK_TYPE_GSS_REAUTH, TOK_TYPE_GSS_REAUTH, eapGssSmInitGssReauth }, + { + ITOK_TYPE_REAUTH_CREDS, + ITOK_TYPE_NONE, + GSSEAP_STATE_NEGO_EXT, + 0, /* critical */ + 0, /* required */ + eapGssSmInitReauthCreds, + }, #endif + /* other extensions go here */ + { + ITOK_TYPE_NONE, + ITOK_TYPE_NONE, + GSSEAP_STATE_NEGO_EXT, + 1, /* critical */ + 1, /* required */ + eapGssSmInitNegoExtFinished + } }; OM_uint32 @@ -670,13 +774,8 @@ gss_init_sec_context(OM_uint32 *minor, OM_uint32 *ret_flags, OM_uint32 *time_rec) { - OM_uint32 major; - OM_uint32 tmpMajor, tmpMinor; + OM_uint32 major, tmpMinor; gss_ctx_id_t ctx = *context_handle; - struct gss_eap_initiator_sm *sm = NULL; - gss_buffer_desc innerInputToken; - gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER; - enum gss_eap_token_type tokType; int initialContextToken = 0; *minor = 0; @@ -722,10 +821,6 @@ gss_init_sec_context(OM_uint32 *minor, GSSEAP_MUTEX_LOCK(&cred->mutex); -#ifdef GSSEAP_ENABLE_REAUTH - if (initialContextToken && gssEapCanReauthP(cred, target_name, time_req)) - ctx->state = GSSEAP_STATE_KRB_REAUTH; -#endif if ((cred->flags & CRED_FLAG_INITIATE) == 0) { major = GSS_S_NO_CRED; @@ -733,60 +828,25 @@ gss_init_sec_context(OM_uint32 *minor, goto cleanup; } - sm = &eapGssInitiatorSm[ctx->state]; - - if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) { - major = gssEapVerifyToken(minor, ctx, input_token, - &tokType, &innerInputToken); - if (GSS_ERROR(major)) - goto cleanup; - - if (tokType == TOK_TYPE_CONTEXT_ERR) { - ctx->state = GSSEAP_STATE_ERROR; - } else if (tokType != sm->inputTokenType) { - major = GSS_S_DEFECTIVE_TOKEN; - *minor = GSSEAP_WRONG_TOK_ID; - goto cleanup; - } - } else { - innerInputToken.length = 0; - innerInputToken.value = NULL; - } - - /* - * Advance through state machine whilst empty tokens are emitted and - * the status is not GSS_S_COMPLETE or an error status. - */ - do { - sm = &eapGssInitiatorSm[ctx->state]; - - major = (sm->processToken)(minor, - cred, - ctx, - target_name, - mech_type, - req_flags, - time_req, - input_chan_bindings, - &innerInputToken, - &innerOutputToken); - if (GSS_ERROR(major)) - goto cleanup; - } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.value == NULL); + major = gssEapSmStep(minor, + cred, + ctx, + target_name, + mech_type, + req_flags, + time_req, + input_chan_bindings, + input_token, + output_token, + eapGssInitiatorSm, + sizeof(eapGssInitiatorSm) / sizeof(eapGssInitiatorSm[0])); + if (GSS_ERROR(major)) + goto cleanup; if (actual_mech_type != NULL) { if (!gssEapInternalizeOid(ctx->mechanismUsed, actual_mech_type)) duplicateOid(&tmpMinor, ctx->mechanismUsed, actual_mech_type); } - if (innerOutputToken.value != NULL) { - tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken, - sm->outputTokenType, output_token); - if (GSS_ERROR(tmpMajor)) { - major = tmpMajor; - *minor = tmpMinor; - goto cleanup; - } - } if (ret_flags != NULL) *ret_flags = ctx->gssFlags; if (time_rec != NULL) @@ -802,73 +862,5 @@ cleanup: if (GSS_ERROR(major)) gssEapReleaseContext(&tmpMinor, context_handle); - gss_release_buffer(&tmpMinor, &innerOutputToken); - return major; } - -#ifdef GSSEAP_ENABLE_REAUTH -static OM_uint32 -eapGssSmInitGssReauth(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_name_t target, - gss_OID mech, - OM_uint32 reqFlags, - OM_uint32 timeReq, - gss_channel_bindings_t chanBindings, - gss_buffer_t inputToken, - gss_buffer_t outputToken) -{ - OM_uint32 major, tmpMinor; - gss_name_t mechTarget = GSS_C_NO_NAME; - gss_OID actualMech = GSS_C_NO_OID; - OM_uint32 gssFlags, timeRec; - - assert(cred != GSS_C_NO_CREDENTIAL); - - ctx->flags |= CTX_FLAG_KRB_REAUTH; - - if (inputToken->length == 0) { - major = initBegin(minor, cred, ctx, target, mech, - reqFlags, timeReq, chanBindings, - inputToken, outputToken); - if (GSS_ERROR(major)) - goto cleanup; - } - - major = gssEapMechToGlueName(minor, target, &mechTarget); - if (GSS_ERROR(major)) - goto cleanup; - - major = gssInitSecContext(minor, - cred->krbCred, - &ctx->kerberosCtx, - mechTarget, - (gss_OID)gss_mech_krb5, - reqFlags, /* | GSS_C_DCE_STYLE, */ - timeReq, - chanBindings, - inputToken, - &actualMech, - outputToken, - &gssFlags, - &timeRec); - if (GSS_ERROR(major)) - goto cleanup; - - ctx->gssFlags = gssFlags; - - if (major == GSS_S_COMPLETE) { - major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec); - if (GSS_ERROR(major)) - goto cleanup; - ctx->state = GSSEAP_STATE_ESTABLISHED; - } - -cleanup: - gssReleaseName(&tmpMinor, &mechTarget); - - return major; -} -#endif /* GSSEAP_ENABLE_REAUTH */ diff --git a/util.h b/util.h index 004c47e..7f433e9 100644 --- a/util.h +++ b/util.h @@ -154,14 +154,26 @@ enum gss_eap_token_type { TOK_TYPE_EXPORT_NAME = 0x0401, /* RFC 2743 exported name */ TOK_TYPE_EXPORT_NAME_COMPOSITE = 0x0402, /* exported composite name */ TOK_TYPE_DELETE_CONTEXT = 0x0405, /* RFC 2743 delete context */ - TOK_TYPE_EAP_RESP = 0x0601, /* EAP response */ - TOK_TYPE_EAP_REQ = 0x0602, /* EAP request */ - TOK_TYPE_EXT_REQ = 0x0603, /* GSS EAP extensions request */ - TOK_TYPE_EXT_RESP = 0x0604, /* GSS EAP extensions response */ - TOK_TYPE_GSS_REAUTH = 0x0605, /* GSS EAP fast reauthentication token */ - TOK_TYPE_CONTEXT_ERR = 0x0606, /* context error */ + TOK_TYPE_ESTABLISH_CONTEXT = 0x0601, /* establish context */ }; +/* inner token types and flags */ +#define ITOK_TYPE_NONE 0x00000000 +#define ITOK_TYPE_CONTEXT_ERR 0x00000001 +#define ITOK_TYPE_ACCEPTOR_NAME_REQ 0x00000002 +#define ITOK_TYPE_ACCEPTOR_NAME_RESP 0x00000003 +#define ITOK_TYPE_EAP_RESP 0x00000004 +#define ITOK_TYPE_EAP_REQ 0x00000005 +#define ITOK_TYPE_GSS_CHANNEL_BINDINGS 0x00000006 +#define ITOK_TYPE_REAUTH_CREDS 0x00000007 +#define ITOK_TYPE_REAUTH_REQ 0x00000008 +#define ITOK_TYPE_REAUTH_RESP 0x00000009 + +#define ITOK_FLAG_CRITICAL 0x80000000 /* critical, wire flag */ +#define ITOK_FLAG_VERIFIED 0x40000000 /* verified, API flag */ + +#define ITOK_TYPE_MASK (~(EXT_FLAG_CRITICAL | EXT_FLAG_VERIFIED)) + OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx); OM_uint32 gssEapReleaseContext(OM_uint32 *minor, gss_ctx_id_t *pCtx); @@ -260,44 +272,6 @@ gssEapDeriveRfc3961Key(OM_uint32 *minor, krb5_enctype enctype, krb5_keyblock *pKey); -/* util_exts.c */ -#define EXT_FLAG_CRITICAL 0x80000000 /* critical, wire flag */ -#define EXT_FLAG_VERIFIED 0x40000000 /* verified, API flag */ - -#define EXT_TYPE_GSS_CHANNEL_BINDINGS 0x00000000 -#define EXT_TYPE_REAUTH_CREDS 0x00000001 -#define EXT_TYPE_MASK (~(EXT_FLAG_CRITICAL | EXT_FLAG_VERIFIED)) - -struct gss_eap_extension_provider { - OM_uint32 type; - int critical; /* client */ - int required; /* server */ - OM_uint32 (*make)(OM_uint32 *, - gss_cred_id_t, - gss_ctx_id_t, - gss_channel_bindings_t, - gss_buffer_t); - OM_uint32 (*verify)(OM_uint32 *, - gss_cred_id_t, - gss_ctx_id_t, - gss_channel_bindings_t, - const gss_buffer_t); -}; - -OM_uint32 -gssEapMakeExtensions(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_channel_bindings_t chanBindings, - gss_buffer_t buffer); - -OM_uint32 -gssEapVerifyExtensions(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_channel_bindings_t chanBindings, - const gss_buffer_t buffer); - /* util_krb.c */ #ifdef HAVE_HEIMDAL_VERSION @@ -557,7 +531,35 @@ OM_uint32 sequenceInit(OM_uint32 *minor, void **vqueue, uint64_t seqnum, int do_replay, int do_sequence, int wide_nums); +/* util_sm.c */ +struct gss_eap_sm; + +OM_uint32 +gssEapSmStep(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken, + struct gss_eap_sm *sm, + size_t smCount); + /* 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); + size_t tokenSize(const gss_OID_desc *mech, size_t body_size); diff --git a/util_context.c b/util_context.c index 6aedf17..4fb8161 100644 --- a/util_context.c +++ b/util_context.c @@ -57,7 +57,7 @@ gssEapAllocContext(OM_uint32 *minor, return GSS_S_FAILURE; } - ctx->state = GSSEAP_STATE_IDENTITY; + ctx->state = GSSEAP_STATE_INITIAL; /* * Integrity, confidentiality, sequencing and replay detection are diff --git a/util_exts.c b/util_exts.c deleted file mode 100644 index 88cec5b..0000000 --- a/util_exts.c +++ /dev/null @@ -1,484 +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. - */ - -/* - * Extension token support. - */ - -#include "gssapiP_eap.h" - -static OM_uint32 -encodeExtensions(OM_uint32 *minor, - gss_buffer_set_t extensions, - OM_uint32 *types, - gss_buffer_t buffer); - -static OM_uint32 -decodeExtensions(OM_uint32 *minor, - const gss_buffer_t buffer, - gss_buffer_set_t *pExtensions, - OM_uint32 **pTypes); - -/* - * Initiator extensions - */ -static OM_uint32 -makeGssChannelBindings(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_channel_bindings_t chanBindings, - gss_buffer_t outputToken) -{ - OM_uint32 major; - gss_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; - - return GSS_S_COMPLETE; -} - -static OM_uint32 -verifyGssChannelBindings(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_channel_bindings_t chanBindings, - gss_buffer_t inputToken) -{ - OM_uint32 major, tmpMinor; - gss_iov_buffer_desc iov[2]; - - iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE; - iov[0].buffer.length = 0; - iov[0].buffer.value = NULL; - - iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM; - iov[1].buffer = *inputToken; - - major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL, - iov, 2, TOK_TYPE_WRAP); - if (GSS_ERROR(major)) - return GSS_S_BAD_BINDINGS; - - if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS && - !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) { - major = GSS_S_BAD_BINDINGS; - *minor = GSSEAP_BINDINGS_MISMATCH; - } else { - major = GSS_S_COMPLETE; - } - - gss_release_buffer(&tmpMinor, &iov[0].buffer); - - return major; -} - -static struct gss_eap_extension_provider -eapGssInitExtensions[] = { - { - EXT_TYPE_GSS_CHANNEL_BINDINGS, - 1, /* critical */ - 1, /* required */ - makeGssChannelBindings, - verifyGssChannelBindings - }, -}; - -/* - * Acceptor extensions - */ -static OM_uint32 -makeReauthCreds(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_channel_bindings_t chanBindings, - gss_buffer_t outputToken) -{ - OM_uint32 major = GSS_S_UNAVAILABLE; - -#ifdef GSSEAP_ENABLE_REAUTH - /* - * If we're built with fast reauthentication enabled, then - * fabricate a ticket from the initiator to ourselves. - */ - major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken); -#endif - - return major; -} - -static OM_uint32 -verifyReauthCreds(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_channel_bindings_t chanBindings, - gss_buffer_t inputToken) -{ - OM_uint32 major = GSS_S_UNAVAILABLE; - -#ifdef GSSEAP_ENABLE_REAUTH - major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken); -#endif - - return major; -} - -static struct gss_eap_extension_provider -eapGssAcceptExtensions[] = { - { - EXT_TYPE_REAUTH_CREDS, - 0, /* critical */ - 0, /* required */ - makeReauthCreds, - verifyReauthCreds - }, -}; - -OM_uint32 -makeExtensions(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - const struct gss_eap_extension_provider *exts, - size_t nexts, - gss_channel_bindings_t chanBindings, - gss_buffer_t buffer) -{ - OM_uint32 major, tmpMinor; - size_t i, j; - gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET; - OM_uint32 *types; - - assert(buffer != GSS_C_NO_BUFFER); - - buffer->length = 0; - buffer->value = NULL; - - types = GSSEAP_CALLOC(nexts, sizeof(OM_uint32)); - if (types == NULL) { - major = GSS_S_FAILURE; - *minor = ENOMEM; - goto cleanup; - } - - for (i = 0, j = 0; i < nexts; i++) { - const struct gss_eap_extension_provider *ext = &exts[i]; - gss_buffer_desc extension = GSS_C_EMPTY_BUFFER; - - types[j] = ext->type; - if (ext->critical) - types[j] |= EXT_FLAG_CRITICAL; - - major = ext->make(minor, cred, ctx, chanBindings, &extension); - if (GSS_ERROR(major)) { - if (ext->critical) - goto cleanup; - else - continue; - } - - major = gss_add_buffer_set_member(minor, &extension, &extensions); - if (GSS_ERROR(major)) - goto cleanup; - - j++; - } - - assert(j == (extensions == GSS_C_NO_BUFFER_SET ? 0 : extensions->count)); - - major = encodeExtensions(minor, extensions, types, buffer); - if (GSS_ERROR(major)) - goto cleanup; - -cleanup: - gss_release_buffer_set(&tmpMinor, &extensions); - if (types != NULL) - GSSEAP_FREE(types); - - return major; -} - -OM_uint32 -gssEapMakeExtensions(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_channel_bindings_t chanBindings, - gss_buffer_t buffer) -{ - size_t nexts; - const struct gss_eap_extension_provider *exts; - - if (CTX_IS_INITIATOR(ctx)) { - exts = eapGssInitExtensions; - nexts = sizeof(eapGssInitExtensions) / sizeof(eapGssInitExtensions[0]); - } else { - exts = eapGssAcceptExtensions; - nexts = sizeof(eapGssAcceptExtensions) / sizeof(eapGssAcceptExtensions[0]); - } - - return makeExtensions(minor, cred, ctx, exts, nexts, chanBindings, buffer); -} - -static OM_uint32 -verifyExtensions(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - const struct gss_eap_extension_provider *exts, - size_t nexts, - gss_channel_bindings_t chanBindings, - const gss_buffer_t buffer) -{ - OM_uint32 major, tmpMinor; - gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET; - OM_uint32 *types = NULL; - size_t i; - - major = decodeExtensions(minor, buffer, &extensions, &types); - if (GSS_ERROR(major)) - goto cleanup; - - for (i = 0; i < nexts; i++) { - const struct gss_eap_extension_provider *ext = &exts[i]; - gss_buffer_t extension = GSS_C_NO_BUFFER; - size_t j; - - for (j = 0; j < extensions->count; j++) { - if ((types[j] & EXT_TYPE_MASK) == ext->type) { - extension = &extensions->elements[j]; - break; - } - } - - if (extension != GSS_C_NO_BUFFER) { - /* Process extension and mark as verified */ - major = ext->verify(minor, cred, ctx, chanBindings, - &extensions->elements[j]); - if (GSS_ERROR(major)) - goto cleanup; - - types[j] |= EXT_FLAG_VERIFIED; - } else if (ext->required) { - /* Required extension missing */ - major = GSS_S_UNAVAILABLE; - *minor = GSSEAP_MISSING_REQUIRED_EXT; - goto cleanup; - } - } - - /* Check we processed all critical extensions */ - for (i = 0; i < extensions->count; i++) { - if ((types[i] & EXT_FLAG_CRITICAL) && - (types[i] & EXT_FLAG_VERIFIED) == 0) { - major = GSS_S_UNAVAILABLE; - *minor = GSSEAP_CRIT_EXT_UNAVAILABLE; - goto cleanup; - } - } - - major = GSS_S_COMPLETE; - *minor = 0; - -cleanup: - gss_release_buffer_set(&tmpMinor, &extensions); - if (types != NULL) - GSSEAP_FREE(types); - - return major; -} - -OM_uint32 -gssEapVerifyExtensions(OM_uint32 *minor, - gss_cred_id_t cred, - gss_ctx_id_t ctx, - gss_channel_bindings_t chanBindings, - const gss_buffer_t buffer) -{ - size_t nexts; - const struct gss_eap_extension_provider *exts; - - if (CTX_IS_INITIATOR(ctx)) { - exts = eapGssAcceptExtensions; - nexts = sizeof(eapGssAcceptExtensions) / sizeof(eapGssAcceptExtensions[0]); - } else { - exts = eapGssInitExtensions; - nexts = sizeof(eapGssInitExtensions) / sizeof(eapGssInitExtensions[0]); - } - - return verifyExtensions(minor, cred, ctx, exts, nexts, chanBindings, buffer); -} - -static OM_uint32 -encodeExtensions(OM_uint32 *minor, - gss_buffer_set_t extensions, - OM_uint32 *types, - gss_buffer_t buffer) -{ - OM_uint32 major, tmpMinor; - size_t required = 0, i; - unsigned char *p; - - buffer->value = NULL; - buffer->length = 0; - - if (extensions != GSS_C_NO_BUFFER_SET) { - for (i = 0; i < extensions->count; i++) { - required += 8 + extensions->elements[i].length; - } - } - - /* - * We must always return a non-NULL token otherwise the calling state - * machine assumes we are finished. Hence care in case malloc(0) does - * return NULL. - */ - buffer->value = GSSEAP_MALLOC(required ? required : 1); - if (buffer->value == NULL) { - 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] & EXT_FLAG_VERIFIED) == 0); /* private flag */ - - /* - * Extensions are encoded as type-length-value, where the upper - * bit of the type indicates criticality. - */ - store_uint32_be(types[i], &p[0]); - store_uint32_be(extension->length, &p[4]); - memcpy(&p[8], extension->value, extension->length); - - p += 8 + extension->length; - } - } - - assert(p == (unsigned char *)buffer->value + required); - assert(buffer->value != NULL); - - major = GSS_S_COMPLETE; - *minor = 0; - -cleanup: - if (GSS_ERROR(major)) { - gss_release_buffer(&tmpMinor, buffer); - } - - return major; -} - -static OM_uint32 -decodeExtensions(OM_uint32 *minor, - const gss_buffer_t buffer, - gss_buffer_set_t *pExtensions, - OM_uint32 **pTypes) -{ - OM_uint32 major, tmpMinor; - gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET; - OM_uint32 *types = NULL; - unsigned char *p; - size_t remain; - - *pExtensions = GSS_C_NO_BUFFER_SET; - *pTypes = NULL; - - major = gss_create_empty_buffer_set(minor, &extensions); - if (GSS_ERROR(major)) - goto cleanup; - - if (buffer->length == 0) { - major = GSS_S_COMPLETE; - goto cleanup; - } - - p = (unsigned char *)buffer->value; - remain = buffer->length; - - do { - OM_uint32 *ntypes; - gss_buffer_desc extension; - - if (remain < 8) { - major = GSS_S_DEFECTIVE_TOKEN; - *minor = GSSEAP_TOK_TRUNC; - goto cleanup; - } - - ntypes = GSSEAP_REALLOC(types, - (extensions->count + 1) * sizeof(OM_uint32)); - if (ntypes == NULL) { - major = GSS_S_FAILURE; - *minor = ENOMEM; - goto cleanup; - } - types = ntypes; - - types[extensions->count] = load_uint32_be(&p[0]); - extension.length = load_uint32_be(&p[4]); - - if (remain < 8 + extension.length) { - major = GSS_S_DEFECTIVE_TOKEN; - *minor = GSSEAP_TOK_TRUNC; - goto cleanup; - } - extension.value = &p[8]; - - major = gss_add_buffer_set_member(minor, &extension, &extensions); - if (GSS_ERROR(major)) - goto cleanup; - - p += 8 + extension.length; - remain -= 8 + extension.length; - } while (remain != 0); - -cleanup: - if (GSS_ERROR(major)) { - gss_release_buffer_set(&tmpMinor, &extensions); - if (types != NULL) - GSSEAP_FREE(types); - } else { - *pExtensions = extensions; - *pTypes = types; - } - - return major; -} diff --git a/util_sm.c b/util_sm.c new file mode 100644 index 0000000..9c2ae38 --- /dev/null +++ b/util_sm.c @@ -0,0 +1,312 @@ +/* + * 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. + */ + +/* + * Context establishment state machine. + */ + +#include "gssapiP_eap.h" + +static OM_uint32 +makeErrorToken(OM_uint32 *minor, + OM_uint32 majorStatus, + OM_uint32 minorStatus, + gss_buffer_set_t *outputToken) +{ + unsigned char errorData[8]; + gss_buffer_desc errorBuffer; + + assert(GSS_ERROR(majorStatus)); + + /* + * Only return error codes that the initiator could have caused, + * to avoid information leakage. + */ + if (IS_RADIUS_ERROR(minorStatus)) { + /* Squash RADIUS error codes */ + minorStatus = GSSEAP_RADIUS_PROT_FAILURE; + } else if (!IS_WIRE_ERROR(minorStatus)) { + /* Don't return non-wire error codes */ + return GSS_S_COMPLETE; + } + + minorStatus -= ERROR_TABLE_BASE_eapg; + + store_uint32_be(majorStatus, &errorData[0]); + store_uint32_be(minorStatus, &errorData[4]); + + errorBuffer.length = sizeof(errorData); + errorBuffer.value = errorData; + + return gss_add_buffer_set_member(minor, &errorBuffer, outputToken); +} + +OM_uint32 +gssEapSmStep(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken, + struct gss_eap_sm *sm, + size_t smCount) +{ + OM_uint32 major, tmpMajor, tmpMinor; + 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; + size_t i, j; + enum gss_eap_state inputState = ctx->state; + + assert(smCount > 0); + + *minor = 0; + + outputToken->length = 0; + outputToken->value = NULL; + + if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) { + enum gss_eap_token_type tokType; + + major = gssEapVerifyToken(minor, ctx, inputToken, &tokType, + &unwrappedInputToken); + if (GSS_ERROR(major)) + goto cleanup; + + if (tokType != TOK_TYPE_ESTABLISH_CONTEXT) { + major = GSS_S_DEFECTIVE_TOKEN; + *minor = GSSEAP_WRONG_TOK_ID; + goto cleanup; + } + } else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) { + major = GSS_S_DEFECTIVE_TOKEN; + *minor = GSSEAP_WRONG_SIZE; + goto cleanup; + } + + if (ctx->state == GSSEAP_STATE_ESTABLISHED) { + major = GSS_S_BAD_STATUS; + *minor = GSSEAP_CONTEXT_ESTABLISHED; + goto cleanup; + } + + assert(ctx->state < GSSEAP_STATE_ESTABLISHED); + + major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken, + &innerInputTokens, &inputTokenTypes); + if (GSS_ERROR(major)) + goto cleanup; + + major = gss_create_empty_buffer_set(minor, &innerOutputTokens); + if (GSS_ERROR(major)) + goto cleanup; + + assert(innerOutputTokens->count == 0); + assert(innerOutputTokens->elements == NULL); + + innerOutputTokens->elements = (gss_buffer_desc *)GSSEAP_CALLOC(smCount, + sizeof(gss_buffer_desc)); + if (innerOutputTokens->elements == NULL) { + major = GSS_S_FAILURE; + *minor = ENOMEM; + goto cleanup; + } + + outputTokenTypes = (OM_uint32 *)GSSEAP_CALLOC(smCount, sizeof(OM_uint32)); + if (outputTokenTypes == NULL) { + major = GSS_S_FAILURE; + *minor = ENOMEM; + goto cleanup; + } + + /* + * Process all the tokens that are valid for the current state. If + * the processToken function returns GSS_S_COMPLETE, the state is + * advanced until there is a token to send or the ESTABLISHED state + * is reached. + */ + do { + major = GSS_S_COMPLETE; + + for (i = 0; i < smCount; i++) { + struct gss_eap_sm *smp = &sm[i]; + int processToken = 0; + gss_buffer_t innerInputToken = GSS_C_NO_BUFFER; + OM_uint32 *inputTokenType = NULL; + gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER; + + if ((smp->validStates & ctx->state) == 0) + continue; + + if (innerInputTokens == GSS_C_NO_BUFFER_SET) { + processToken = ((smp->validStates & GSSEAP_STATE_INITIAL) != 0); + } else if (inputState != ctx->state) { + processToken = (smp->inputTokenType == ITOK_TYPE_NONE); + } else { + for (j = 0; j < innerInputTokens->count; j++) { + processToken = (smp->inputTokenType == inputTokenTypes[j]); + if (innerInputToken != GSS_C_NO_BUFFER && processToken) { + major = GSS_S_DEFECTIVE_TOKEN; + *minor = GSSEAP_DUPLICATE_ITOK; + break; + } + innerInputToken = &innerInputTokens->elements[j]; + inputTokenType = &inputTokenTypes[j]; + } + } + +#ifdef GSSEAP_DEBUG + fprintf(stderr, "GSS-EAP: state %d processToken %d inputTokenType %08x " + "innerInputToken %p innerOutputTokensCount %zd\n", + ctx->state, processToken, smp->inputTokenType, + innerInputToken, innerOutputTokens->count); +#endif + + if (processToken) { + major = smp->processToken(minor, cred, ctx, target, mech, reqFlags, + timeReq, chanBindings, innerInputToken, + &innerOutputToken); + if (GSS_ERROR(major)) + break; + + if (inputTokenType != NULL) + *inputTokenType |= ITOK_FLAG_VERIFIED; + + if (innerOutputToken.value != NULL) { + innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken; + assert(smp->outputTokenType != ITOK_TYPE_NONE); + outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType; + if (smp->critical) + outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL; + innerOutputTokens->count++; + } + if (major == GSS_S_COMPLETE) + break; + } else if (smp->required && smp->inputTokenType != ITOK_TYPE_NONE) { + major = GSS_S_DEFECTIVE_TOKEN; + *minor = GSSEAP_MISSING_REQUIRED_ITOK; + break; + } + } + + if (major != GSS_S_COMPLETE) + break; /* GSS_S_CONTINUE_NEEDED or error */ + + assert(ctx->state < GSSEAP_STATE_ESTABLISHED); + + ctx->state = GSSEAP_STATE_NEXT(ctx->state); + + if (innerOutputTokens->count != 0) { + major = GSS_S_CONTINUE_NEEDED; + break; /* send any tokens if we have them */ + } + } while (ctx->state != GSSEAP_STATE_ESTABLISHED); + + assert(innerOutputTokens->count <= smCount); + + /* Check we understood all critical tokens */ + if (!GSS_ERROR(major) && innerInputTokens != GSS_C_NO_BUFFER_SET) { + for (j = 0; j < innerInputTokens->count; j++) { + if ((inputTokenTypes[j] & ITOK_FLAG_CRITICAL) && + (inputTokenTypes[j] & ITOK_FLAG_VERIFIED) == 0) { + major = GSS_S_UNAVAILABLE; + *minor = GSSEAP_CRIT_ITOK_UNAVAILABLE; + goto cleanup; + } + } + } + + /* Emit an error token if we are the acceptor */ + if (GSS_ERROR(major)) { + if (CTX_IS_INITIATOR(ctx)) + goto cleanup; /* return error directly to caller */ + + /* replace any emitted tokens with error token */ + gss_release_buffer_set(&tmpMinor, &innerOutputTokens); + + tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputTokens); + if (GSS_ERROR(tmpMajor)) { + major = tmpMajor; + *minor = tmpMinor; + goto cleanup; + } + + outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL; + } + +#ifdef GSSEAP_DEBUG + for (i = 0; i < innerOutputTokens->count; i++) { + fprintf(stderr, "GSS-EAP: type %d length %zd value %p\n", + outputTokenTypes[i], + innerOutputTokens->elements[i].length, + innerOutputTokens->elements[i].value); + } +#endif + + /* Format composite output token */ + if (innerOutputTokens->count != 0 || /* inner tokens to send */ + !CTX_IS_INITIATOR(ctx) || /* any leg acceptor */ + ctx->state != GSSEAP_STATE_ESTABLISHED) { /* non-last leg initiator */ + tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens, + outputTokenTypes, &unwrappedOutputToken); + if (tmpMajor == GSS_S_COMPLETE) { + tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken, + TOK_TYPE_ESTABLISH_CONTEXT, outputToken); + if (GSS_ERROR(tmpMajor)) { + major = tmpMajor; + *minor = tmpMinor; + goto cleanup; + } + } + } + + assert(GSS_ERROR(major) || + (major == GSS_S_CONTINUE_NEEDED && ctx->state < GSSEAP_STATE_ESTABLISHED) || + (major == GSS_S_COMPLETE && ctx->state == GSSEAP_STATE_ESTABLISHED)); + +cleanup: + gss_release_buffer_set(&tmpMinor, &innerInputTokens); + gss_release_buffer_set(&tmpMinor, &innerOutputTokens); + if (inputTokenTypes != NULL) + GSSEAP_FREE(inputTokenTypes); + if (outputTokenTypes != NULL) + gss_release_buffer(&tmpMinor, &unwrappedOutputToken); + GSSEAP_FREE(outputTokenTypes); + + return major; +} diff --git a/util_token.c b/util_token.c index 5231ae2..dab1251 100644 --- a/util_token.c +++ b/util_token.c @@ -30,7 +30,7 @@ * SUCH DAMAGE. */ /* - * Copyright 1993 by OpenVision Technologies, Inc. + * Portions Copyright 1993 by OpenVision Technologies, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without fee, @@ -57,6 +57,149 @@ #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, + OM_uint32 **pTypes) +{ + OM_uint32 major, tmpMinor; + gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET; + OM_uint32 *types = NULL; + unsigned char *p; + size_t remain; + + *pExtensions = GSS_C_NO_BUFFER_SET; + *pTypes = NULL; + + major = gss_create_empty_buffer_set(minor, &extensions); + if (GSS_ERROR(major)) + goto cleanup; + + if (buffer->length == 0) { + major = GSS_S_COMPLETE; + goto cleanup; + } + + p = (unsigned char *)buffer->value; + remain = buffer->length; + + do { + OM_uint32 *ntypes; + gss_buffer_desc extension; + + if (remain < 8) { + major = GSS_S_DEFECTIVE_TOKEN; + *minor = GSSEAP_TOK_TRUNC; + goto cleanup; + } + + ntypes = GSSEAP_REALLOC(types, + (extensions->count + 1) * sizeof(OM_uint32)); + if (ntypes == NULL) { + major = GSS_S_FAILURE; + *minor = ENOMEM; + goto cleanup; + } + types = ntypes; + + types[extensions->count] = load_uint32_be(&p[0]); + extension.length = load_uint32_be(&p[4]); + + if (remain < 8 + extension.length) { + major = GSS_S_DEFECTIVE_TOKEN; + *minor = GSSEAP_TOK_TRUNC; + goto cleanup; + } + extension.value = &p[8]; + + major = gss_add_buffer_set_member(minor, &extension, &extensions); + if (GSS_ERROR(major)) + goto cleanup; + + p += 8 + extension.length; + remain -= 8 + extension.length; + } while (remain != 0); + +cleanup: + if (GSS_ERROR(major)) { + gss_release_buffer_set(&tmpMinor, &extensions); + if (types != NULL) + GSSEAP_FREE(types); + } else { + *pExtensions = extensions; + *pTypes = types; + } + + return major; +} + /* * $Id: util_token.c 23457 2009-12-08 00:04:48Z tlyu $ */ -- 2.1.4