integrity protect extension token exchange
authorLuke Howard <lukeh@padl.com>
Thu, 19 May 2011 15:14:48 +0000 (17:14 +0200)
committerLuke Howard <lukeh@padl.com>
Sat, 16 Jul 2011 11:48:27 +0000 (11:48 +0000)
mech_eap/accept_sec_context.c
mech_eap/gssapiP_eap.h
mech_eap/init_sec_context.c
mech_eap/util.h
mech_eap/util_context.c
mech_eap/util_sm.c
mech_eap/util_token.c

index fe54b11..c2daf5a 100644 (file)
@@ -662,20 +662,26 @@ eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
                                  gss_buffer_t outputToken GSSEAP_UNUSED,
                                  OM_uint32 *smFlags GSSEAP_UNUSED)
 {
-    OM_uint32 major, tmpMinor;
+    OM_uint32 major;
     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;
+    iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM | GSS_IOV_BUFFER_FLAG_ALLOCATED;
+
+    /* XXX necessary because decrypted in place and we verify it later */
+    major = duplicateBuffer(minor, inputToken, &iov[1].buffer);
+    if (GSS_ERROR(major))
+        return major;
 
     major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
                                     iov, 2, TOK_TYPE_WRAP);
-    if (GSS_ERROR(major))
+    if (GSS_ERROR(major)) {
+        gssEapReleaseIov(iov, 2);
         return major;
+    }
 
     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS &&
         !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
@@ -686,11 +692,36 @@ eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
         *minor = 0;
     }
 
-    gss_release_buffer(&tmpMinor, &iov[0].buffer);
+    gssEapReleaseIov(iov, 2);
 
     return major;
 }
 
+static OM_uint32
+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 = gssEapVerifyTokenMIC(minor, ctx, inputToken);
+    if (GSS_ERROR(major))
+        return major;
+
+    GSSEAP_SM_TRANSITION_NEXT(ctx);
+
+    *minor = 0;
+    return GSS_S_CONTINUE_NEEDED;
+}
+
 #ifdef GSSEAP_ENABLE_REAUTH
 static OM_uint32
 eapGssSmAcceptReauthCreds(OM_uint32 *minor,
@@ -722,42 +753,28 @@ eapGssSmAcceptReauthCreds(OM_uint32 *minor,
 #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)
+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)
 {
-    GSSEAP_SM_TRANSITION_NEXT(ctx);
-
-    *minor = 0;
+    OM_uint32 major;
 
-    return GSS_S_CONTINUE_NEEDED;
-}
+    major = gssEapMakeTokenMIC(minor, ctx, outputToken);
+    if (GSS_ERROR(major))
+        return major;
 
-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)
-{
     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
 
     *minor = 0;
-    *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
+    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
 
     return GSS_S_COMPLETE;
 }
@@ -817,11 +834,11 @@ static struct gss_eap_sm eapGssAcceptorSm[] = {
         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
     {
@@ -834,10 +851,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
     },
 };
 
index 8bad9a8..fc22246 100644 (file)
@@ -207,6 +207,8 @@ struct gss_ctx_id_struct
         #define reauthCtx            ctxU.reauth
 #endif
     } ctxU;
+    const struct gss_eap_token_buffer_set *inputTokens;
+    const struct gss_eap_token_buffer_set *outputTokens;
 };
 
 #define TOK_FLAG_SENDER_IS_ACCEPTOR         0x01
index a8b7db0..1945f09 100644 (file)
@@ -770,6 +770,33 @@ eapGssSmInitGssChannelBindings(OM_uint32 *minor,
     return GSS_S_CONTINUE_NEEDED;
 }
 
+static OM_uint32
+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 = gssEapMakeTokenMIC(minor, ctx, outputToken);
+    if (GSS_ERROR(major))
+        return major;
+
+    GSSEAP_SM_TRANSITION_NEXT(ctx);
+
+    *minor = 0;
+    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
+
+    return GSS_S_CONTINUE_NEEDED;
+}
 #ifdef GSSEAP_ENABLE_REAUTH
 static OM_uint32
 eapGssSmInitReauthCreds(OM_uint32 *minor,
@@ -798,39 +825,24 @@ 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)
+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)
 {
-    GSSEAP_SM_TRANSITION_NEXT(ctx);
-
-    *minor = 0;
-    *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
+    OM_uint32 major;
 
-    return GSS_S_CONTINUE_NEEDED;
-}
+    major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
+    if (GSS_ERROR(major))
+        return major;
 
-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)
-{
     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
 
     *minor = 0;
@@ -904,10 +916,10 @@ static struct gss_eap_sm eapGssInitiatorSm[] = {
     },
     {
         ITOK_TYPE_NONE,
-        ITOK_TYPE_NONE,
+        ITOK_TYPE_INITIATOR_MIC,
         GSSEAP_STATE_INITIATOR_EXTS,
-        0,
-        eapGssSmInitCompleteInitiatorExts
+        SM_ITOK_FLAG_REQUIRED,
+        eapGssSmInitInitiatorMIC
     },
 #ifdef GSSEAP_ENABLE_REAUTH
     {
@@ -920,11 +932,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
     }
 };
 
index ae2045c..1d4e47c 100644 (file)
@@ -177,7 +177,9 @@ enum gss_eap_token_type {
 #define ITOK_TYPE_REAUTH_RESP           0x00000009 /* optional */
 #define ITOK_TYPE_VERSION_INFO          0x0000000A /* optional */
 #define ITOK_TYPE_VENDOR_INFO           0x0000000B /* optional */
-#define ITOK_TYPE_GSS_FLAGS             0x0000000C
+#define ITOK_TYPE_GSS_FLAGS             0x0000000C /* optional */
+#define ITOK_TYPE_INITIATOR_MIC         0x0000000D /* critical, required, if not reauth */
+#define ITOK_TYPE_ACCEPTOR_MIC          0x0000000E /* TBD */
 
 #define ITOK_FLAG_CRITICAL              0x80000000  /* critical, wire flag */
 #define ITOK_FLAG_VERIFIED              0x40000000  /* verified, API flag */
@@ -208,6 +210,16 @@ gssEapContextTime(OM_uint32 *minor,
                   gss_ctx_id_t context_handle,
                   OM_uint32 *time_rec);
 
+OM_uint32
+gssEapMakeTokenMIC(OM_uint32 *minor,
+                   gss_ctx_id_t ctx,
+                   gss_buffer_t tokenMIC);
+
+OM_uint32
+gssEapVerifyTokenMIC(OM_uint32 *minor,
+                     gss_ctx_id_t ctx,
+                     const gss_buffer_t tokenMIC);
+
 /* util_cred.c */
 OM_uint32 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred);
 OM_uint32 gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred);
@@ -641,16 +653,29 @@ void
 gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state);
 
 /* util_token.c */
+struct gss_eap_token_buffer_set {
+    gss_buffer_set_desc buffers; /* pointers only */
+    OM_uint32 *types;
+};
+
 OM_uint32
 gssEapEncodeInnerTokens(OM_uint32 *minor,
-                        gss_buffer_set_t extensions,
-                        OM_uint32 *types,
+                        struct gss_eap_token_buffer_set *tokens,
                         gss_buffer_t buffer);
 OM_uint32
 gssEapDecodeInnerTokens(OM_uint32 *minor,
                         const gss_buffer_t buffer,
-                        gss_buffer_set_t *pExtensions,
-                        OM_uint32 **pTypes);
+                        struct gss_eap_token_buffer_set *tokens);
+
+OM_uint32
+gssEapReleaseInnerTokens(OM_uint32 *minor,
+                         struct gss_eap_token_buffer_set *tokens,
+                         int freeBuffers);
+
+OM_uint32
+gssEapAllocInnerTokens(OM_uint32 *minor,
+                       size_t count,
+                       struct gss_eap_token_buffer_set *tokens);
 
 size_t
 tokenSize(const gss_OID_desc *mech, size_t body_size);
index e9a9308..5c6bbc8 100644 (file)
@@ -227,3 +227,129 @@ gssEapContextTime(OM_uint32 *minor,
 
     return GSS_S_COMPLETE;
 }
+
+static OM_uint32
+gssEapMakeOrVerifyTokenMIC(OM_uint32 *minor,
+                           gss_ctx_id_t ctx,
+                           gss_buffer_t tokenMIC,
+                           int verifyMIC)
+{
+    OM_uint32 major;
+    gss_iov_buffer_desc *iov;
+    size_t i = 0, j;
+    enum gss_eap_token_type tokType;
+    OM_uint32 micTokType;
+    unsigned char wireTokType[2];
+    unsigned char *innerTokTypes;
+    const struct gss_eap_token_buffer_set *tokens;
+
+    tokens = verifyMIC ? ctx->inputTokens : ctx->outputTokens;
+
+    assert(tokens != NULL);
+
+    iov = GSSEAP_CALLOC(2 + (2 * tokens->buffers.count) + 1, sizeof(*iov));
+    if (iov == NULL) {
+        *minor = ENOMEM;
+        return GSS_S_FAILURE;
+    }
+
+    innerTokTypes = GSSEAP_MALLOC(4 * tokens->buffers.count);
+    if (innerTokTypes == NULL) {
+        GSSEAP_FREE(iov);
+        *minor = ENOMEM;
+        return GSS_S_FAILURE;
+    }
+
+    /* Mechanism OID */
+    assert(ctx->mechanismUsed != GSS_C_NO_OID);
+    iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
+    iov[i].buffer.length = ctx->mechanismUsed->length;
+    iov[i].buffer.value = ctx->mechanismUsed->elements;
+    i++;
+
+    /* Token type */
+    if (CTX_IS_INITIATOR(ctx) ^ verifyMIC) {
+        tokType = TOK_TYPE_INITIATOR_CONTEXT;
+        micTokType = ITOK_TYPE_INITIATOR_MIC;
+    } else {
+        tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
+        micTokType = ITOK_TYPE_ACCEPTOR_MIC;
+    }
+    store_uint16_be(tokType, wireTokType);
+
+    iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
+    iov[i].buffer.length = sizeof(wireTokType);
+    iov[i].buffer.value = wireTokType;
+    i++;
+
+    for (j = 0; j < tokens->buffers.count; j++) {
+        if (verifyMIC &&
+            (tokens->types[j] & ITOK_TYPE_MASK) == micTokType)
+            continue; /* will use this slot for trailer */
+
+        iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
+        iov[i].buffer.length = 4;
+        iov[i].buffer.value = &innerTokTypes[j * 4];
+        store_uint32_be(tokens->types[j] & ~(ITOK_FLAG_VERIFIED),
+                        iov[i].buffer.value);
+        i++;
+
+        iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
+        iov[i].buffer = tokens->buffers.elements[j];
+        i++;
+    }
+
+    if (verifyMIC) {
+        assert(tokenMIC->length >= 16);
+
+        assert(i < 2 + (2 * tokens->buffers.count));
+
+        iov[i].type = GSS_IOV_BUFFER_TYPE_HEADER;
+        iov[i].buffer.length = 16;
+        iov[i].buffer.value = tokenMIC->value;
+        i++;
+
+        iov[i].type = GSS_IOV_BUFFER_TYPE_TRAILER;
+        iov[i].buffer.length = tokenMIC->length - 16;
+        iov[i].buffer.value = (unsigned char *)tokenMIC->value + 16;
+        i++;
+
+        major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
+                                        iov, i, TOK_TYPE_MIC);
+    } else {
+        iov[i++].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+        major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL,
+                                   iov, i, TOK_TYPE_MIC);
+        if (!GSS_ERROR(major))
+            *tokenMIC = iov[i - 1].buffer;
+    }
+
+    gssEapReleaseIov(iov, tokens->buffers.count);
+    GSSEAP_FREE(innerTokTypes);
+
+    return major;
+}
+
+OM_uint32
+gssEapMakeTokenMIC(OM_uint32 *minor,
+                   gss_ctx_id_t ctx,
+                   gss_buffer_t tokenMIC)
+{
+    tokenMIC->length = 0;
+    tokenMIC->value = NULL;
+
+    return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, FALSE);
+}
+
+OM_uint32
+gssEapVerifyTokenMIC(OM_uint32 *minor,
+                     gss_ctx_id_t ctx,
+                     const gss_buffer_t tokenMIC)
+{
+    if (tokenMIC->length < 16) {
+        *minor = GSSEAP_TOK_TRUNC;
+        return GSS_S_BAD_SIG;
+    }
+
+    return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, TRUE);
+}
index ca69923..8d36085 100644 (file)
@@ -98,18 +98,14 @@ static OM_uint32
 makeErrorToken(OM_uint32 *minor,
                OM_uint32 majorStatus,
                OM_uint32 minorStatus,
-               gss_buffer_set_t *outputToken)
+               struct gss_eap_token_buffer_set *token)
 {
-    OM_uint32 major;
+    OM_uint32 major, tmpMinor;
     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.
@@ -127,64 +123,26 @@ makeErrorToken(OM_uint32 *minor,
     store_uint32_be(majorStatus, &errorData[0]);
     store_uint32_be(minorStatus, &errorData[4]);
 
-    errorBuffer.length = sizeof(errorData);
-    errorBuffer.value = errorData;
-
-    major = gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
-    if (GSS_ERROR(major))
+    major = gssEapAllocInnerTokens(&tmpMinor, 1, token);
+    if (GSS_ERROR(major)) {
+        *minor = tmpMinor;
         return major;
-
-    return GSS_S_COMPLETE;
-}
-
-static OM_uint32
-allocInnerTokens(OM_uint32 *minor,
-                 size_t count,
-                 gss_buffer_set_t *pTokens,
-                 OM_uint32 **pTokenTypes)
-{
-    OM_uint32 major, tmpMinor;
-    gss_buffer_set_t tokens = GSS_C_NO_BUFFER_SET;
-    OM_uint32 *tokenTypes = NULL;
-
-    major = gss_create_empty_buffer_set(minor, &tokens);
-    if (GSS_ERROR(major))
-        goto cleanup;
-
-    assert(tokens->count == 0);
-    assert(tokens->elements == NULL);
-
-    tokens->elements = (gss_buffer_desc *)GSSEAP_CALLOC(count, sizeof(gss_buffer_desc));
-    if (tokens->elements == NULL) {
-        major = GSS_S_FAILURE;
-        *minor = ENOMEM;
-        goto cleanup;
-    }
-
-    tokenTypes = (OM_uint32 *)GSSEAP_CALLOC(count, sizeof(OM_uint32));
-    if (tokenTypes == NULL) {
-        major = GSS_S_FAILURE;
-        *minor = ENOMEM;
-        goto cleanup;
     }
 
-    major = GSS_S_COMPLETE;
-    *minor = 0;
+    errorBuffer.length = sizeof(errorData);
+    errorBuffer.value = errorData;
 
-cleanup:
+    major = duplicateBuffer(&tmpMinor, &errorBuffer, &token->buffers.elements[0]);
     if (GSS_ERROR(major)) {
-        gss_release_buffer_set(&tmpMinor, &tokens);
-        tokens = GSS_C_NO_BUFFER_SET;
-        if (tokenTypes != NULL) {
-            GSSEAP_FREE(tokenTypes);
-            tokenTypes = NULL;
-        }
+        gssEapReleaseInnerTokens(&tmpMinor, token, 1);
+        *minor = tmpMinor;
+        return major;
     }
 
-    *pTokens = tokens;
-    *pTokenTypes = tokenTypes;
+    token->types[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
 
-    return major;
+    *minor = 0;
+    return GSS_S_COMPLETE;
 }
 
 OM_uint32
@@ -202,11 +160,10 @@ gssEapSmStep(OM_uint32 *minor,
              size_t smCount)
 {
     OM_uint32 major, tmpMajor, tmpMinor;
+    struct gss_eap_token_buffer_set inputTokens = { { 0, GSS_C_NO_BUFFER }, NULL };
+    struct gss_eap_token_buffer_set outputTokens = { { 0, GSS_C_NO_BUFFER }, NULL };
     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;
@@ -247,17 +204,17 @@ gssEapSmStep(OM_uint32 *minor,
 
     assert(ctx->state < GSSEAP_STATE_ESTABLISHED);
 
-    major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken,
-                                    &innerInputTokens, &inputTokenTypes);
+    major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken, &inputTokens);
     if (GSS_ERROR(major))
         goto cleanup;
 
-    assert(innerInputTokens != GSS_C_NO_BUFFER_SET);
-
-    major = allocInnerTokens(minor, smCount, &innerOutputTokens, &outputTokenTypes);
+    major = gssEapAllocInnerTokens(minor, smCount, &outputTokens);
     if (GSS_ERROR(major))
         goto cleanup;
 
+    ctx->inputTokens = &inputTokens;
+    ctx->outputTokens = &outputTokens;
+
     /* Process all the tokens that are valid for the current state. */
     for (i = 0; i < smCount; i++) {
         struct gss_eap_sm *smp = &sm[i];
@@ -283,8 +240,8 @@ gssEapSmStep(OM_uint32 *minor,
             processToken = 1;
         } else if ((smFlags & SM_FLAG_TRANSITED) == 0) {
             /* Don't regurgitate a token which belonds to a previous state. */
-            for (j = 0; j < innerInputTokens->count; j++) {
-                if ((inputTokenTypes[j] & ITOK_TYPE_MASK) == smp->inputTokenType) {
+            for (j = 0; j < inputTokens.buffers.count; j++) {
+                if ((inputTokens.types[j] & ITOK_TYPE_MASK) == smp->inputTokenType) {
                     if (processToken) {
                         /* Check for duplicate inner tokens */
                         major = GSS_S_DEFECTIVE_TOKEN;
@@ -292,8 +249,8 @@ gssEapSmStep(OM_uint32 *minor,
                         break;
                     }
                     processToken = 1;
-                    innerInputToken = &innerInputTokens->elements[j];
-                    inputTokenType = &inputTokenTypes[j];
+                    innerInputToken = &inputTokens.buffers.elements[j];
+                    inputTokenType = &inputTokens.types[j];
                 }
             }
             if (GSS_ERROR(major))
@@ -321,18 +278,18 @@ gssEapSmStep(OM_uint32 *minor,
                 smFlags |= SM_FLAG_TRANSITED;
 
             if (innerOutputToken.value != NULL) {
-                innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken;
+                outputTokens.buffers.elements[outputTokens.buffers.count] = innerOutputToken;
                 assert(smp->outputTokenType != ITOK_TYPE_NONE);
-                outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType;
+                outputTokens.types[outputTokens.buffers.count] = smp->outputTokenType;
                 if (smFlags & SM_FLAG_OUTPUT_TOKEN_CRITICAL)
-                    outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
-                innerOutputTokens->count++;
+                    outputTokens.types[outputTokens.buffers.count] |= ITOK_FLAG_CRITICAL;
+                outputTokens.buffers.count++;
             }
             /*
              * 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)) {
+                 ((smFlags & SM_FLAG_FORCE_SEND_TOKEN) || outputTokens.buffers.count != 0)) {
                 SM_ASSERT_VALID(ctx, major);
                 break;
             }
@@ -345,13 +302,13 @@ gssEapSmStep(OM_uint32 *minor,
         }
     }
 
-    assert(innerOutputTokens->count <= smCount);
+    assert(outputTokens.buffers.count <= smCount);
 
     /* Check we understood all critical tokens sent by peer */
     if (!GSS_ERROR(major)) {
-        for (j = 0; j < innerInputTokens->count; j++) {
-            if ((inputTokenTypes[j] & ITOK_FLAG_CRITICAL) &&
-                (inputTokenTypes[j] & ITOK_FLAG_VERIFIED) == 0) {
+        for (j = 0; j < inputTokens.buffers.count; j++) {
+            if ((inputTokens.types[j] & ITOK_FLAG_CRITICAL) &&
+                (inputTokens.types[j] & ITOK_FLAG_VERIFIED) == 0) {
                 major = GSS_S_UNAVAILABLE;
                 *minor = GSSEAP_CRIT_ITOK_UNAVAILABLE;
                 goto cleanup;
@@ -365,25 +322,21 @@ gssEapSmStep(OM_uint32 *minor,
             goto cleanup; /* return error directly to caller */
 
         /* replace any emitted tokens with error token */
-        gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
+        gssEapReleaseInnerTokens(&tmpMinor, &outputTokens, 1);
 
-        tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputTokens);
+        tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &outputTokens);
         if (GSS_ERROR(tmpMajor)) {
             major = tmpMajor;
             *minor = tmpMinor;
             goto cleanup;
         }
-
-        if (innerOutputTokens->count != 0)
-            outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
     }
 
     /* Format output token from inner tokens */
-    if (innerOutputTokens->count != 0 ||            /* inner tokens to send */
+    if (outputTokens.buffers.count != 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);
+        tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, &outputTokens, &unwrappedOutputToken);
         if (tmpMajor == GSS_S_COMPLETE) {
             if (CTX_IS_INITIATOR(ctx))
                 tokType = TOK_TYPE_INITIATOR_CONTEXT;
@@ -406,13 +359,13 @@ gssEapSmStep(OM_uint32 *minor,
     SM_ASSERT_VALID(ctx, major);
 
 cleanup:
-    gss_release_buffer_set(&tmpMinor, &innerInputTokens);
-    gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
-    if (inputTokenTypes != NULL)
-        GSSEAP_FREE(inputTokenTypes);
-    if (outputTokenTypes != NULL)
+    gssEapReleaseInnerTokens(&tmpMinor, &inputTokens, 0);
+    gssEapReleaseInnerTokens(&tmpMinor, &inputTokens, 1);
+
     gss_release_buffer(&tmpMinor, &unwrappedOutputToken);
-        GSSEAP_FREE(outputTokenTypes);
+
+    ctx->inputTokens = NULL;
+    ctx->outputTokens = NULL;
 
     return major;
 }
index a929198..eb0dd1b 100644 (file)
@@ -59,8 +59,7 @@
 
 OM_uint32
 gssEapEncodeInnerTokens(OM_uint32 *minor,
-                        gss_buffer_set_t extensions,
-                        OM_uint32 *types,
+                        struct gss_eap_token_buffer_set *tokens,
                         gss_buffer_t buffer)
 {
     OM_uint32 major, tmpMinor;
@@ -70,10 +69,8 @@ gssEapEncodeInnerTokens(OM_uint32 *minor,
     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;
-        }
+    for (i = 0; i < tokens->buffers.count; i++) {
+        required += 8 + tokens->buffers.elements[i].length;
     }
 
     /*
@@ -91,22 +88,20 @@ gssEapEncodeInnerTokens(OM_uint32 *minor,
     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];
+    for (i = 0; i < tokens->buffers.count; i++) {
+        gss_buffer_t tokenBuffer = &tokens->buffers.elements[i];
 
-            assert((types[i] & ITOK_FLAG_VERIFIED) == 0); /* private flag */
+        assert((tokens->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);
+         /*
+          * Extensions are encoded as type-length-value, where the upper
+          * bit of the type indicates criticality.
+          */
+        store_uint32_be(tokens->types[i], &p[0]);
+        store_uint32_be(tokenBuffer->length, &p[4]);
+        memcpy(&p[8], tokenBuffer->value, tokenBuffer->length);
 
-            p += 8 + extension->length;
-        }
+        p += 8 + tokenBuffer->length;
     }
 
     assert(p == (unsigned char *)buffer->value + required);
@@ -126,21 +121,15 @@ cleanup:
 OM_uint32
 gssEapDecodeInnerTokens(OM_uint32 *minor,
                         const gss_buffer_t buffer,
-                        gss_buffer_set_t *pExtensions,
-                        OM_uint32 **pTypes)
+                        struct gss_eap_token_buffer_set *tokens)
 {
     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;
+    tokens->buffers.count = 0;
+    tokens->buffers.elements = NULL;
+    tokens->types = NULL;
 
     if (buffer->length == 0) {
         major = GSS_S_COMPLETE;
@@ -152,7 +141,7 @@ gssEapDecodeInnerTokens(OM_uint32 *minor,
 
     do {
         OM_uint32 *ntypes;
-        gss_buffer_desc extension;
+        gss_buffer_desc tokenBuffer, *newTokenBuffers;
 
         if (remain < 8) {
             major = GSS_S_DEFECTIVE_TOKEN;
@@ -160,42 +149,48 @@ gssEapDecodeInnerTokens(OM_uint32 *minor,
             goto cleanup;
         }
 
-        ntypes = GSSEAP_REALLOC(types,
-                                (extensions->count + 1) * sizeof(OM_uint32));
+        ntypes = GSSEAP_REALLOC(tokens->types,
+                                (tokens->buffers.count + 1) * sizeof(OM_uint32));
         if (ntypes == NULL) {
             major = GSS_S_FAILURE;
             *minor = ENOMEM;
             goto cleanup;
         }
-        types = ntypes;
+        tokens->types = ntypes;
 
-        types[extensions->count] = load_uint32_be(&p[0]);
-        extension.length = load_uint32_be(&p[4]);
+        tokens->types[tokens->buffers.count] = load_uint32_be(&p[0]);
+        tokenBuffer.length = load_uint32_be(&p[4]);
 
-        if (remain < 8 + extension.length) {
+        if (remain < 8 + tokenBuffer.length) {
             major = GSS_S_DEFECTIVE_TOKEN;
             *minor = GSSEAP_TOK_TRUNC;
             goto cleanup;
         }
-        extension.value = &p[8];
+        tokenBuffer.value = &p[8];
 
-        major = gss_add_buffer_set_member(minor, &extension, &extensions);
-        if (GSS_ERROR(major))
+        newTokenBuffers = GSSEAP_REALLOC(tokens->buffers.elements,
+                                         (tokens->buffers.count + 1) * sizeof(gss_buffer_desc));
+        if (newTokenBuffers == NULL) {
+            major = GSS_S_FAILURE;
+            *minor = ENOMEM;
             goto cleanup;
+        }
+
+        tokens->buffers.elements = newTokenBuffers;
+        tokens->buffers.elements[tokens->buffers.count] = tokenBuffer;
+        tokens->buffers.count++;
+
+        p      += 8 + tokenBuffer.length;
+        remain -= 8 + tokenBuffer.length;
 
-        p      += 8 + extension.length;
-        remain -= 8 + extension.length;
     } while (remain != 0);
 
+    major = GSS_S_COMPLETE;
+    *minor = 0;
+
 cleanup:
-    if (GSS_ERROR(major)) {
-        gss_release_buffer_set(&tmpMinor, &extensions);
-        if (types != NULL)
-            GSSEAP_FREE(types);
-    } else {
-        *pExtensions = extensions;
-        *pTypes = types;
-    }
+    if (GSS_ERROR(major))
+        gssEapReleaseInnerTokens(&tmpMinor, tokens, 0);
 
     return major;
 }
@@ -415,3 +410,70 @@ verifyTokenHeader(OM_uint32 *minor,
     *minor = 0;
     return GSS_S_COMPLETE;
 }
+
+OM_uint32
+gssEapAllocInnerTokens(OM_uint32 *minor,
+                       size_t count,
+                       struct gss_eap_token_buffer_set *tokens)
+{
+    OM_uint32 major;
+
+    tokens->buffers.count = 0;
+    tokens->buffers.elements = (gss_buffer_desc *)GSSEAP_CALLOC(count, sizeof(gss_buffer_desc));
+    if (tokens->buffers.elements == NULL) {
+        major = GSS_S_FAILURE;
+        *minor = ENOMEM;
+        goto cleanup;
+    }
+
+    tokens->types = (OM_uint32 *)GSSEAP_CALLOC(count, sizeof(OM_uint32));
+    if (tokens->types == NULL) {
+        major = GSS_S_FAILURE;
+        *minor = ENOMEM;
+        goto cleanup;
+    }
+
+    major = GSS_S_COMPLETE;
+    *minor = 0;
+
+cleanup:
+    if (GSS_ERROR(major)) {
+        if (tokens->buffers.elements != NULL) {
+            GSSEAP_FREE(tokens->buffers.elements);
+            tokens->buffers.elements = NULL;
+        }
+        if (tokens->types != NULL) {
+            GSSEAP_FREE(tokens->types);
+            tokens->types = NULL;
+        }
+    }
+
+    return major;
+}
+
+OM_uint32
+gssEapReleaseInnerTokens(OM_uint32 *minor,
+                         struct gss_eap_token_buffer_set *tokens,
+                         int freeBuffers)
+{
+    OM_uint32 tmpMinor;
+    size_t i;
+
+    if (tokens->buffers.elements != NULL) {
+        if (freeBuffers) {
+            for (i = 0; i < tokens->buffers.count; i++)
+                gss_release_buffer(&tmpMinor, &tokens->buffers.elements[i]);
+        }
+        GSSEAP_FREE(tokens->buffers.elements);
+        tokens->buffers.elements = NULL;
+    }
+    tokens->buffers.count = 0;
+
+    if (tokens->types != NULL) {
+        GSSEAP_FREE(tokens->types);
+        tokens->types = NULL;
+    }
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}