add an extra assert
[mech_eap.orig] / util_sm.c
index d800e82..de730ce 100644 (file)
--- a/util_sm.c
+++ b/util_sm.c
@@ -45,6 +45,7 @@
                ((status) == GSS_S_COMPLETE && (ctx)->state == GSSEAP_STATE_ESTABLISHED)); \
     } while (0)
 
+#ifdef GSSEAP_DEBUG
 static const char *
 gssEapStateToString(enum gss_eap_state state)
 {
@@ -77,19 +78,19 @@ gssEapStateToString(enum gss_eap_state state)
     return s;
 }
 
-#ifdef GSSEAP_DEBUG
 void
 gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state)
 {
-    assert(state > ctx->state);
+    assert(state >= GSSEAP_STATE_INITIAL);
     assert(state <= GSSEAP_STATE_ESTABLISHED);
 
     fprintf(stderr, "GSS-EAP: state transition %s->%s\n",
-            gssEapStateToString(ctx->state), gssEapStateToString(state));
+            gssEapStateToString(GSSEAP_SM_STATE(ctx)),
+            gssEapStateToString(state));
 
     ctx->state = state;
 }
-#endif
+#endif /* GSSEAP_DEBUG */
 
 static OM_uint32
 makeErrorToken(OM_uint32 *minor,
@@ -97,11 +98,16 @@ makeErrorToken(OM_uint32 *minor,
                OM_uint32 minorStatus,
                gss_buffer_set_t *outputToken)
 {
+    OM_uint32 major;
     unsigned char errorData[8];
     gss_buffer_desc errorBuffer;
 
     assert(GSS_ERROR(majorStatus));
 
+    major = gss_create_empty_buffer_set(minor, outputToken);
+    if (GSS_ERROR(major))
+        return major;
+
     /*
      * Only return error codes that the initiator could have caused,
      * to avoid information leakage.
@@ -122,7 +128,11 @@ makeErrorToken(OM_uint32 *minor,
     errorBuffer.length = sizeof(errorData);
     errorBuffer.value = errorData;
 
-    return gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
+    major = gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
+    if (GSS_ERROR(major))
+        return major;
+
+    return GSS_S_COMPLETE;
 }
 
 static OM_uint32
@@ -186,7 +196,7 @@ gssEapSmStep(OM_uint32 *minor,
              gss_channel_bindings_t chanBindings,
              gss_buffer_t inputToken,
              gss_buffer_t outputToken,
-             struct gss_eap_sm *sm,
+             struct gss_eap_sm *sm, /* ordered by state */
              size_t smCount)
 {
     OM_uint32 major, tmpMajor, tmpMinor;
@@ -227,7 +237,7 @@ gssEapSmStep(OM_uint32 *minor,
         initialContextToken = 1;
     }
 
-    if (ctx->state == GSSEAP_STATE_ESTABLISHED) {
+    if (CTX_IS_ESTABLISHED(ctx)) {
         major = GSS_S_BAD_STATUS;
         *minor = GSSEAP_CONTEXT_ESTABLISHED;
         goto cleanup;
@@ -257,27 +267,43 @@ gssEapSmStep(OM_uint32 *minor,
         if ((smp->validStates & ctx->state) == 0)
             continue;
 
+        /*
+         * We special case the first call to gss_init_sec_context so that
+         * all token providers have the opportunity to generate an initial
+         * context token. Providers where inputTokenType is ITOK_TYPE_NONE
+         * are always called and generally act on state transition boundaries,
+         * for example to advance the state after a series of optional tokens
+         * (as is the case with the extension token exchange) or to generate
+         * a new token after the state was advanced by a provider which did
+         * not emit a token.
+         */
         if (smp->inputTokenType == ITOK_TYPE_NONE || initialContextToken) {
             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) {
-                    processToken = 1;
-                    if (innerInputToken != GSS_C_NO_BUFFER) {
+                    if (processToken) {
+                        /* Check for duplicate inner tokens */
                         major = GSS_S_DEFECTIVE_TOKEN;
                         *minor = GSSEAP_DUPLICATE_ITOK;
                         break;
                     }
+                    processToken = 1;
+                    innerInputToken = &innerInputTokens->elements[j];
+                    inputTokenType = &inputTokenTypes[j];
                 }
-                innerInputToken = &innerInputTokens->elements[j];
-                inputTokenType = &inputTokenTypes[j];
             }
+            if (GSS_ERROR(major))
+                break;
         }
 
         if (processToken) {
             enum gss_eap_state oldState = ctx->state;
 
             smFlags = 0;
+            if (inputTokenType != NULL && (*inputTokenType & ITOK_FLAG_CRITICAL))
+                smFlags |= SM_FLAG_INPUT_TOKEN_CRITICAL;
 
             major = smp->processToken(minor, cred, ctx, target, mech, reqFlags,
                                       timeReq, chanBindings, innerInputToken,
@@ -287,14 +313,18 @@ gssEapSmStep(OM_uint32 *minor,
 
             if (inputTokenType != NULL)
                 *inputTokenType |= ITOK_FLAG_VERIFIED;
-            if (ctx->state != oldState)
+            if (smFlags & SM_FLAG_RESTART) {
+                assert(ctx->state < oldState);
+                i = 0;
+            } else if (ctx->state != oldState) {
                 smFlags |= SM_FLAG_TRANSITED;
+            }
 
             if (innerOutputToken.value != NULL) {
                 innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken;
                 assert(smp->outputTokenType != ITOK_TYPE_NONE);
                 outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType;
-                if (smp->itokFlags & SM_ITOK_FLAG_CRITICAL)
+                if (smFlags & SM_FLAG_OUTPUT_TOKEN_CRITICAL)
                     outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
                 innerOutputTokens->count++;
             }
@@ -310,6 +340,7 @@ gssEapSmStep(OM_uint32 *minor,
             }
         } else if ((smp->itokFlags & SM_ITOK_FLAG_REQUIRED) &&
             smp->inputTokenType != ITOK_TYPE_NONE) {
+            /* Check for required inner tokens */
             major = GSS_S_DEFECTIVE_TOKEN;
             *minor = GSSEAP_MISSING_REQUIRED_ITOK;
             break;
@@ -318,7 +349,7 @@ gssEapSmStep(OM_uint32 *minor,
 
     assert(innerOutputTokens->count <= smCount);
 
-    /* Check we understood all critical tokens */
+    /* 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) &&
@@ -330,7 +361,7 @@ gssEapSmStep(OM_uint32 *minor,
         }
     }
 
-    /* Emit an error token if we are the acceptor */
+    /* Optionaly 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 */
@@ -345,13 +376,14 @@ gssEapSmStep(OM_uint32 *minor,
             goto cleanup;
         }
 
-        outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
+        if (innerOutputTokens->count != 0)
+            outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
     }
 
-    /* Format composite output token */
+    /* Format output token from inner tokens */
     if (innerOutputTokens->count != 0 ||            /* inner tokens to send */
         !CTX_IS_INITIATOR(ctx) ||                   /* any leg acceptor */
-        ctx->state != GSSEAP_STATE_ESTABLISHED) {   /* non-last leg initiator */
+        !CTX_IS_ESTABLISHED(ctx)) {                 /* non-last leg initiator */
         tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens,
                                            outputTokenTypes, &unwrappedOutputToken);
         if (tmpMajor == GSS_S_COMPLETE) {
@@ -365,6 +397,9 @@ gssEapSmStep(OM_uint32 *minor,
         }
     }
 
+    /* If the context is established, empty tokens only to be emitted by initiator */
+    assert(!CTX_IS_ESTABLISHED(ctx) || ((outputToken->length == 0) == CTX_IS_INITIATOR(ctx)));
+
     SM_ASSERT_VALID(ctx, major);
 
 cleanup: