initial TLV refactor
authorLuke Howard <lukeh@padl.com>
Tue, 8 Mar 2011 02:32:56 +0000 (13:32 +1100)
committerLuke Howard <lukeh@padl.com>
Tue, 8 Mar 2011 13:38:41 +0000 (00:38 +1100)
Makefile.am
accept_sec_context.c
gssapiP_eap.h
gsseap_err.et
import_sec_context.c
init_sec_context.c
util.h
util_context.c
util_exts.c [deleted file]
util_sm.c [new file with mode: 0644]
util_token.c

index 17757f4..f9be930 100644 (file)
@@ -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) $<
 
index d192d71..6d65647 100644 (file)
 #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,
index 18137a9..bcfdf03 100644 (file)
@@ -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)
index eb5a948..0a34d12 100644 (file)
@@ -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"
index cf46d6a..0289c0c 100644 (file)
@@ -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;
 
index b80ab41..f55aec9 100644 (file)
 
 #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 (file)
--- 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);
 
index 6aedf17..4fb8161 100644 (file)
@@ -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 (file)
index 88cec5b..0000000
+++ /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 (file)
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;
+}
index 5231ae2..dab1251 100644 (file)
@@ -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,
 
 #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 $
  */