Merge branch 'master' into tlv
authorLuke Howard <lukeh@padl.com>
Sat, 12 Mar 2011 04:19:25 +0000 (15:19 +1100)
committerLuke Howard <lukeh@padl.com>
Sat, 12 Mar 2011 04:19:25 +0000 (15:19 +1100)
18 files changed:
Makefile.am
NOTES
README
accept_sec_context.c
export_sec_context.c
gssapiP_eap.h
gsseap_err.et
import_sec_context.c
init_sec_context.c
unwrap_iov.c
util.h
util_context.c
util_exts.c [deleted file]
util_radius.cpp
util_radius.h
util_reauth.c
util_sm.c [new file with mode: 0644]
util_token.c

index 17757f4..eaa3fc1 100644 (file)
@@ -7,9 +7,9 @@ gssdir = $(libdir)/gss
 gss_LTLIBRARIES = mech_eap.la
 
 mech_eap_la_CPPFLAGS = -DBUILD_GSSEAP_LIB -DSYSCONFDIR=\"${sysconfdir}\" -DDATAROOTDIR=\"${datarootdir}\"
-mech_eap_la_CFLAGS   = -g -Wall -fno-strict-aliasing \
+mech_eap_la_CFLAGS   = -g -Wunused-parameter -Wall -fno-strict-aliasing \
                        @KRB5_CFLAGS@ @EAP_CFLAGS@ @RADSEC_CFLAGS@ @TARGET_CFLAGS@
-mech_eap_la_CXXFLAGS = -g -Wall \
+mech_eap_la_CXXFLAGS = -g -Wunused-parameter -Wall \
                        @KRB5_CFLAGS@ @EAP_CFLAGS@ @RADSEC_CFLAGS@ \
                        @SHIBRESOLVER_CXXFLAGS@ @SHIBSP_CXXFLAGS@ @TARGET_CFLAGS@
 mech_eap_la_LDFLAGS  = -avoid-version -module \
@@ -74,7 +74,6 @@ mech_eap_la_SOURCES =                         \
        util_cksum.c                            \
        util_cred.c                             \
        util_crypt.c                            \
-       util_exts.c                             \
        util_krb.c                              \
        util_lucid.c                            \
        util_mech.c                             \
@@ -84,6 +83,7 @@ mech_eap_la_SOURCES =                         \
        util_radius.cpp                         \
        util_saml.cpp                           \
        util_shib.cpp                           \
+       util_sm.c                               \
        util_token.c                            \
        verify_mic.c                            \
        wrap.c                                  \
@@ -114,8 +114,6 @@ endif
 gsseap_err.h gsseap_err.c: gsseap_err.et
        $(COMPILE_ET) $<
 
-
-
 radsec_err.h radsec_err.c: radsec_err.et
        $(COMPILE_ET) $<
 
diff --git a/NOTES b/NOTES
index cce432f..849ce4e 100644 (file)
--- a/NOTES
+++ b/NOTES
@@ -1,2 +1,9 @@
 - gss_xxx routines acquire lock, gssXxx don't
 
+- git
+
+If you do want to update with a rebase, deletethe branch from the
+server first then push the rebased branch
+
+to delete a branch from a server git push origin :branch_to_del
+
diff --git a/README b/README
index c02d8ce..7b4d867 100644 (file)
--- a/README
+++ b/README
@@ -2,7 +2,7 @@ Overview
 ========
 
 This is an implementation of the GSS EAP mechanism, as described in
-draft-ietf-abfab-gss-eap-00.txt.
+draft-ietf-abfab-gss-eap-01.txt.
 
 Building
 ========
@@ -17,37 +17,88 @@ so not all features will be available.
 Installing
 ==========
 
+GSS mechglue
+------------
+
 When installing, be sure to edit $prefix/etc/gss/mech to register
 the EAP mechanisms. A sample configuration file is in this directory.
+You may need to specify an absolute path.
+
+RADIUS client library
+---------------------
 
 Make sure your RADIUS library is configured to talk to the server of
-your choice: see the example radsec.conf in this directory.
+your choice: see the example radsec.conf in this directory. If you
+want to use TCP or TLS, you'll need to run radsecproxy in front of
+your RADIUS server.
 
-On the RADIUS server side, you need to install dictionary.ukerna and
-include it from the main dictionary file.
+RADIUS server
+-------------
 
-If you want the acceptor be able to identify the user, the RADIUS
-server needs to echo back the EAP username from the inner tunnel;
-for privacy, mech_eap only sends the realm in the EAP Identity
-response. To configure this with FreeRADIUS, add:
+These instructions apply to FreeRADIUS only, which is downloadable
+from http://freeradius.org/. After configure, make, install, do the
+following:
 
-        update outer.reply {
-            User-Name = "%{request:User-Name}"
-        }
+On the RADIUS server side, you need to install dictionary.ukerna to
+$prefix/etc/raddb and include it from the main dictionary file, by
+adding:
+
+    $INCLUDE dictionary.ukerna
+
+to $prefix/etc/raddb/dictionary. Make sure these files are world-
+readable; they weren't in my installation.
 
-to $prefix/etc/raddb/sites-enabled/inner-tunnel, and ensure that
+Edit $prefix/etc/raddb/users to add your test user and password:
 
-    virtual_server = "inner-tunnel"
+    bob@PROJECT-MOONSHOT.ORG Cleartext-Password := secret 
 
-is set in eap.conf for the desired EAP types.
+Add an entry for your acceptor to $prefix/etc/raddb/clients.conf:
 
-To test the SAML assertion code path, you can place a fixed SAML
-assertion in the update reply block of the default configuration.
+    client somehost {
+        ipaddr = 127.0.0.1
+        secret = testing123
+        require_message_authenticator = yes
+    }
 
-        update reply {
-            SAML-AAA-Assertion = '<saml:Assertion ...'
-            SAML-AAA-Assertion += '...'
+Edit $prefix/etc/raddb/eap.conf and set:
+
+    eap {
+...
+        default_eap_type = ttls
+...
+        tls {
+            certdir = ...
+            cadir = ...
+            private_key_file = ...
+            certificate_file = ...
+        }
+        ttls {
+            default_eap_type = mschapv2
+            copy_request_to_tunnel = no
+            use_tunneled_reply = no
+            virtual_server = "inner-tunnel"
         }
+...
+    }
+
+to enable EAP-TTLS.
+
+If you want the acceptor be able to identify the user, the RADIUS
+server needs to echo back the EAP username from the inner tunnel;
+for privacy, mech_eap only sends the realm in the EAP Identity
+response. To configure this with FreeRADIUS, add:
+
+    update outer.reply {
+        User-Name = "%{request:User-Name}"
+    }
+
+If you want to add a SAML assertion, do this with "update reply"
+in $prefix/etc/raddb/sites-available/default:
+
+    update reply {
+        SAML-AAA-Assertion = '<saml:Assertion ...'
+        SAML-AAA-Assertion += '...'
+    }
 
 You'll need to split it into multiple lines because of the RADIUS
 attribute size limit.
@@ -69,3 +120,14 @@ Note: for SASL you will be prompted for a username and password.
 % client -C -p 5556 -s host -m EAP-AES128 <host>
 % server -c -p 5556 -s host -h <host>
 
+To test fast reauthentication support, add the following to
+/etc/krb5.conf:
+
+[appdefaults]
+        eap_gss = {
+                reauth_use_ccache = TRUE
+        }
+
+This will store a Kerberos ticket for a GSS-EAP authenticated user
+in a credentials cache, which can then be used for re-authentication
+to the same acceptor. You must have a valid keytab configured.
index d192d71..5da5ea6 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,
+                        gss_OID mech,
+                        OM_uint32 reqFlags,
+                        OM_uint32 timeReq,
                         gss_channel_bindings_t chanBindings,
-                        gss_buffer_t outputToken);
+                        gss_buffer_t inputToken,
+                        gss_buffer_t outputToken,
+                        OM_uint32 *smFlags);
 #endif
 
 /*
@@ -119,35 +124,87 @@ acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
     return GSS_S_COMPLETE;
 }
 
+static OM_uint32
+eapGssSmAcceptAcceptorName(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 *smFlags)
+{
+    OM_uint32 major;
+
+    /* XXX TODO import and validate name from inputToken */
+
+    if (ctx->acceptorName != GSS_C_NO_NAME) {
+        /* Send desired target name to acceptor */
+        major = gssEapDisplayName(minor, ctx->acceptorName,
+                                  outputToken, NULL);
+        if (GSS_ERROR(major))
+            return major;
+    }
+
+    return GSS_S_CONTINUE_NEEDED;
+}
+
+#ifdef GSSEAP_DEBUG
+static OM_uint32
+eapGssSmAcceptVendorInfo(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 *smFlags)
+{
+    fprintf(stderr, "GSS-EAP: vendor: %.*s\n",
+            (int)inputToken->length, (char *)inputToken->value);
+
+    return GSS_S_CONTINUE_NEEDED;
+}
+#endif
+
+
 /*
  * Emit a identity EAP request to force the initiator (peer) to identify
  * itself.
  */
 static OM_uint32
 eapGssSmAcceptIdentity(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,
+                       gss_OID mech,
+                       OM_uint32 reqFlags,
+                       OM_uint32 timeReq,
                        gss_channel_bindings_t chanBindings,
-                       gss_buffer_t outputToken)
+                       gss_buffer_t inputToken,
+                       gss_buffer_t outputToken,
+                       OM_uint32 *smFlags)
 {
     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;
     }
 
-    assert(ctx->acceptorName == GSS_C_NO_NAME);
-
-    if (cred->name != GSS_C_NO_NAME) {
-        major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
-        if (GSS_ERROR(major))
-            return major;
-    }
-
     reqData = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, 0,
                             EAP_CODE_REQUEST, 0);
     if (reqData == NULL) {
@@ -162,11 +219,13 @@ eapGssSmAcceptIdentity(OM_uint32 *minor,
     if (GSS_ERROR(major))
         return major;
 
-    ctx->state = GSSEAP_STATE_AUTHENTICATE;
-
     wpabuf_free(reqData);
 
+    GSSEAP_SM_TRANSITION_NEXT(ctx);
+
     *minor = 0;
+    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
+
     return GSS_S_CONTINUE_NEEDED;
 }
 
@@ -406,11 +465,16 @@ 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,
+                           gss_OID mech,
+                           OM_uint32 reqFlags,
+                           OM_uint32 timeReq,
                            gss_channel_bindings_t chanBindings,
-                           gss_buffer_t outputToken)
+                           gss_buffer_t inputToken,
+                           gss_buffer_t outputToken,
+                           OM_uint32 *smFlags)
 {
     OM_uint32 major, tmpMinor;
     struct rs_connection *rconn;
@@ -478,9 +542,8 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
 
     frresp = rs_packet_frpkt(resp);
     switch (frresp->code) {
-    case PW_AUTHENTICATION_ACK:
     case PW_ACCESS_CHALLENGE:
-        major = GSS_S_CONTINUE_NEEDED;
+    case PW_AUTHENTICATION_ACK:
         break;
     case PW_AUTHENTICATION_REJECT:
         *minor = GSSEAP_RADIUS_AUTH_FAILURE;
@@ -519,11 +582,12 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
         if (GSS_ERROR(major))
             goto cleanup;
 
-        ctx->state = GSSEAP_STATE_EXTENSIONS_REQ;
+        GSSEAP_SM_TRANSITION_NEXT(ctx);
     }
 
-    *minor = 0;
     major = GSS_S_CONTINUE_NEEDED;
+    *minor = 0;
+    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
 
 cleanup:
     if (request != NULL)
@@ -535,114 +599,188 @@ 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,
+                                 gss_OID mech,
+                                 OM_uint32 reqFlags,
+                                 OM_uint32 timeReq,
+                                 gss_channel_bindings_t chanBindings,
+                                 gss_buffer_t inputToken,
+                                 gss_buffer_t outputToken,
+                                 OM_uint32 *smFlags)
 {
-    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;
+        *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,
+                          gss_OID mech,
+                          OM_uint32 reqFlags,
+                          OM_uint32 timeReq,
+                          gss_channel_bindings_t chanBindings,
+                          gss_buffer_t inputToken,
+                          gss_buffer_t outputToken,
+                          OM_uint32 *smFlags)
 {
     OM_uint32 major;
 
-    major = gssEapMakeExtensions(minor, cred, ctx, chanBindings, outputToken);
-    if (GSS_ERROR(major))
-        return major;
-
-    ctx->state = GSSEAP_STATE_ESTABLISHED;
+    /*
+     * If we're built with fast reauthentication enabled, then
+     * fabricate a ticket from the initiator to ourselves.
+     */
+    major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken);
+    if (major == GSS_S_UNAVAILABLE)
+        major = GSS_S_COMPLETE;
+    if (major == GSS_S_COMPLETE)
+        major = GSS_S_CONTINUE_NEEDED;
 
-    *minor = 0;
-    return GSS_S_COMPLETE;
+    return major;
 }
+#endif
 
 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)
+eapGssSmAcceptCompleteInitiatorExts(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 *smFlags)
 {
-    /* Called with already established context */
-    *minor = GSSEAP_CONTEXT_ESTABLISHED;
-    return GSS_S_BAD_STATUS;
+    GSSEAP_SM_TRANSITION_NEXT(ctx);
+
+    *minor = 0;
+
+    return GSS_S_CONTINUE_NEEDED;
 }
 
 static OM_uint32
-makeErrorToken(OM_uint32 *minor,
-               OM_uint32 majorStatus,
-               OM_uint32 minorStatus,
-               gss_buffer_t outputToken)
+eapGssSmAcceptCompleteAcceptorExts(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 *smFlags)
 {
-    unsigned char errorData[8];
-    gss_buffer_desc errorBuffer;
+    GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
 
-    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;
+    *minor = 0;
+    *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
 
-    return duplicateBuffer(minor, &errorBuffer, outputToken);
+    return GSS_S_COMPLETE;
 }
 
-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[] = {
+    {
+        ITOK_TYPE_ACCEPTOR_NAME_REQ,
+        ITOK_TYPE_ACCEPTOR_NAME_RESP,
+        GSSEAP_STATE_INITIAL,
+        0,
+        eapGssSmAcceptAcceptorName
+    },
+#ifdef GSSEAP_DEBUG
+    {
+        ITOK_TYPE_VENDOR_INFO,
+        ITOK_TYPE_NONE,
+        GSSEAP_STATE_INITIAL,
+        0,
+        eapGssSmAcceptVendorInfo,
+    },
+#endif
+#ifdef GSSEAP_ENABLE_REAUTH
+    {
+        ITOK_TYPE_REAUTH_REQ,
+        ITOK_TYPE_REAUTH_RESP,
+        GSSEAP_STATE_INITIAL,
+        0,
+        eapGssSmAcceptGssReauth,
+    },
+#endif
+    {
+        ITOK_TYPE_NONE,
+        ITOK_TYPE_EAP_REQ,
+        GSSEAP_STATE_INITIAL,
+        SM_ITOK_FLAG_REQUIRED,
+        eapGssSmAcceptIdentity,
+    },
+    {
+        ITOK_TYPE_EAP_RESP,
+        ITOK_TYPE_EAP_REQ,
+        GSSEAP_STATE_AUTHENTICATE,
+        SM_ITOK_FLAG_REQUIRED,
+        eapGssSmAcceptAuthenticate
+    },
+    {
+        ITOK_TYPE_GSS_CHANNEL_BINDINGS,
+        ITOK_TYPE_NONE,
+        GSSEAP_STATE_INITIATOR_EXTS,
+        SM_ITOK_FLAG_REQUIRED,
+        eapGssSmAcceptGssChannelBindings,
+    },
+    {
+        ITOK_TYPE_NONE,
+        ITOK_TYPE_NONE,
+        GSSEAP_STATE_INITIATOR_EXTS,
+        0,
+        eapGssSmAcceptCompleteInitiatorExts,
+    },
 #ifdef GSSEAP_ENABLE_REAUTH
-    { TOK_TYPE_GSS_REAUTH,  TOK_TYPE_GSS_REAUTH,    eapGssSmAcceptGssReauth          },
+    {
+        ITOK_TYPE_NONE,
+        ITOK_TYPE_REAUTH_CREDS,
+        GSSEAP_STATE_ACCEPTOR_EXTS,
+        0,
+        eapGssSmAcceptReauthCreds,
+    },
 #endif
+    {
+        ITOK_TYPE_NONE,
+        ITOK_TYPE_NONE,
+        GSSEAP_STATE_ACCEPTOR_EXTS,
+        0,
+        eapGssSmAcceptCompleteAcceptorExts
+    },
 };
 
 OM_uint32
@@ -658,14 +796,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 +817,6 @@ gss_accept_sec_context(OM_uint32 *minor,
         if (GSS_ERROR(major))
             return major;
 
-        initialContextToken = 1;
         *context_handle = ctx;
     }
 
@@ -711,57 +842,26 @@ gss_accept_sec_context(OM_uint32 *minor,
 
     GSSEAP_MUTEX_LOCK(&cred->mutex);
 
-    sm = &eapGssAcceptorSm[ctx->state];
-
-    major = gssEapVerifyToken(minor, ctx, input_token,
-                              &tokType, &innerInputToken);
-    if (GSS_ERROR(major))
-        goto cleanup;
-
-    if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
-        *minor = GSSEAP_CRED_MECH_MISMATCH;
-        major = GSS_S_BAD_MECH;
-        goto cleanup;
+    if (cred->name != GSS_C_NO_NAME) {
+        major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
+        if (GSS_ERROR(major))
+            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;
+    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;
-    }
-
-    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))
@@ -785,18 +885,7 @@ 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;
-        }
-    }
+    assert(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
 
 cleanup:
     if (cred != GSS_C_NO_CREDENTIAL)
@@ -806,8 +895,6 @@ cleanup:
     if (GSS_ERROR(major))
         gssEapReleaseContext(&tmpMinor, context_handle);
 
-    gss_release_buffer(&tmpMinor, &innerOutputToken);
-
     return major;
 }
 
@@ -826,35 +913,38 @@ acceptReadyKrb(OM_uint32 *minor,
     if (GSS_ERROR(major))
         return major;
 
-    if (cred->name != GSS_C_NO_NAME) {
-        major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
-        if (GSS_ERROR(major))
-            return major;
-    }
-
     major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec);
     if (GSS_ERROR(major))
         return major;
 
-    ctx->state = GSSEAP_STATE_ESTABLISHED;
-
     *minor = 0;
     return GSS_S_COMPLETE;
 }
 
 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,
+                        gss_OID mech,
+                        OM_uint32 reqFlags,
+                        OM_uint32 timeReq,
                         gss_channel_bindings_t chanBindings,
-                        gss_buffer_t outputToken)
+                        gss_buffer_t inputToken,
+                        gss_buffer_t outputToken,
+                        OM_uint32 *smFlags)
 {
     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,
@@ -871,10 +961,19 @@ eapGssSmAcceptGssReauth(OM_uint32 *minor,
     if (major == GSS_S_COMPLETE) {
         major = acceptReadyKrb(minor, ctx, cred,
                                krbInitiator, mech, timeRec);
+        if (major == GSS_S_COMPLETE) {
+            GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
+        }
+        ctx->gssFlags = gssFlags;
+    } else if (GSS_ERROR(major) &&
+        (*smFlags & SM_FLAG_INPUT_TOKEN_CRITICAL) == 0) {
+        /* pretend reauthentication attempt never happened */
+        gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER);
+        ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
+        GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
+        major = GSS_S_CONTINUE_NEEDED;
     }
 
-    ctx->gssFlags = gssFlags;
-
     gssReleaseName(&tmpMinor, &krbInitiator);
 
     return major;
index e50b6e5..2ea2c5c 100644 (file)
@@ -165,7 +165,7 @@ gssEapExportSecContext(OM_uint32 *minor,
     p = (unsigned char *)token->value;
 
     store_uint32_be(EAP_EXPORT_CONTEXT_V1, &p[0]);        /* version */
-    store_uint32_be(ctx->state,            &p[4]);
+    store_uint32_be(GSSEAP_SM_STATE(ctx),  &p[4]);
     store_uint32_be(ctx->flags,            &p[8]);
     store_uint32_be(ctx->gssFlags,         &p[12]);
     p = store_oid(ctx->mechanismUsed,      &p[16]);
index 18137a9..b7740da 100644 (file)
@@ -140,18 +140,6 @@ 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
-};
-
 #define CTX_IS_ESTABLISHED(ctx)             ((ctx)->state == GSSEAP_STATE_ESTABLISHED)
 
 /* Initiator context flags */
index eb5a948..f76b7d9 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"
@@ -67,6 +69,7 @@ error_code GSSEAP_BAD_CONTEXT_OPTION,           "Bad context option"
 error_code GSSEAP_BAD_SERVICE_NAME,             "Name is not a valid service name"
 error_code GSSEAP_BAD_INITIATOR_NAME,           "Initiator identity must be a valid name"
 error_code GSSEAP_NO_HOSTNAME,                  "Could not determine local host name"
+error_code GSSEAP_NO_ACCEPTOR_NAME,             "Could not determine acceptor identity"
 
 #
 # Credential errors
@@ -93,6 +96,7 @@ error_code GSSEAP_BAD_PRF_KEY,                  "PRF key usage type is unknown"
 #
 error_code GSSEAP_LIBEAP_INIT_FAILURE,          "Failed to initialize EAP library"
 error_code GSSEAP_PEER_SM_INIT_FAILURE,         "Failed to create EAP state machine"
+error_code GSSEAP_PEER_SM_STEP_FAILURE,         "Failed to step EAP state machine"
 error_code GSSEAP_PEER_AUTH_FAILURE,            "EAP peer authentication failure"
 error_code GSSEAP_PEER_BAD_MESSAGE,             "Received bad EAP message"
 
index cf46d6a..08c5005 100644 (file)
@@ -251,8 +251,8 @@ gssEapImportContext(OM_uint32 *minor,
     remain -= 16;
 
     /* Validate state */
-    if (ctx->state < GSSEAP_STATE_IDENTITY ||
-        ctx->state > GSSEAP_STATE_ESTABLISHED)
+    if (GSSEAP_SM_STATE(ctx) < GSSEAP_STATE_INITIAL ||
+        GSSEAP_SM_STATE(ctx) > GSSEAP_STATE_ESTABLISHED)
         return GSS_S_DEFECTIVE_TOKEN;
 
     /* Only acceptor can export partial context tokens */
index b80ab41..e2aa259 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)
 {
@@ -340,9 +326,7 @@ initBegin(OM_uint32 *minor,
           gss_OID mech,
           OM_uint32 reqFlags,
           OM_uint32 timeReq,
-          gss_channel_bindings_t chanBindings,
-          gss_buffer_t inputToken,
-          gss_buffer_t outputToken)
+          gss_channel_bindings_t chanBindings)
 {
     OM_uint32 major;
 
@@ -364,16 +348,18 @@ initBegin(OM_uint32 *minor,
     if (GSS_ERROR(major))
         return major;
 
-    GSSEAP_MUTEX_LOCK(&target->mutex);
+    if (target != GSS_C_NO_NAME) {
+        GSSEAP_MUTEX_LOCK(&target->mutex);
+
+        major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
+        if (GSS_ERROR(major)) {
+            GSSEAP_MUTEX_UNLOCK(&target->mutex);
+            return major;
+        }
 
-    major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
-    if (GSS_ERROR(major)) {
         GSSEAP_MUTEX_UNLOCK(&target->mutex);
-        return major;
     }
 
-    GSSEAP_MUTEX_UNLOCK(&target->mutex);
-
     if (mech == GSS_C_NULL_OID) {
         major = gssEapDefaultMech(minor, &ctx->mechanismUsed);
     } else if (gssEapIsConcreteMechanismOid(mech)) {
@@ -397,6 +383,182 @@ 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 *smFlags)
+{
+    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 *smFlags)
+{
+    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 (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
+        if (!gssEapCanReauthP(cred, target, timeReq))
+            return GSS_S_CONTINUE_NEEDED;
+
+        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 | GSS_C_MUTUAL_FLAG,
+                              timeReq,
+                              chanBindings,
+                              inputToken,
+                              &actualMech,
+                              outputToken,
+                              &gssFlags,
+                              &timeRec);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    ctx->gssFlags = gssFlags;
+
+    if (major == GSS_S_COMPLETE) {
+        assert(GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE);
+
+        major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
+        if (GSS_ERROR(major))
+            goto cleanup;
+        GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
+    } else {
+        GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
+    }
+
+cleanup:
+    gssReleaseName(&tmpMinor, &mechTarget);
+
+    return major;
+}
+#endif /* GSSEAP_ENABLE_REAUTH */
+
+#ifdef GSSEAP_DEBUG
+static OM_uint32
+eapGssSmInitVendorInfo(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 *smFlags)
+{
+    OM_uint32 major;
+
+    major = makeStringBuffer(minor, "JANET(UK)", outputToken);
+    if (GSS_ERROR(major))
+        return major;
+
+    return GSS_S_CONTINUE_NEEDED;
+}
+#endif
+
+static OM_uint32
+eapGssSmInitAcceptorName(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 *smFlags)
+{
+    OM_uint32 major;
+
+    if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL &&
+        ctx->acceptorName != GSS_C_NO_NAME) {
+
+        /* Send desired target name to acceptor */
+        major = gssEapDisplayName(minor, ctx->acceptorName,
+                                  outputToken, NULL);
+        if (GSS_ERROR(major))
+            return major;
+    } else if (inputToken != GSS_C_NO_BUFFER &&
+               ctx->acceptorName == GSS_C_NO_NAME) {
+        /* Accept target name hint from acceptor */
+        major = gssEapImportName(minor, inputToken,
+                                 GSS_C_NT_USER_NAME, &ctx->acceptorName);
+        if (GSS_ERROR(major))
+            return major;
+    }
+
+    /*
+     * Currently, other parts of the code assume that the acceptor name
+     * is available, hence this check.
+     */
+    if (ctx->acceptorName == GSS_C_NO_NAME) {
+        *minor = GSSEAP_NO_ACCEPTOR_NAME;
+        return GSS_S_FAILURE;
+    }
+
+    return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
 eapGssSmInitIdentity(OM_uint32 *minor,
                      gss_cred_id_t cred,
                      gss_ctx_id_t ctx,
@@ -406,31 +568,52 @@ eapGssSmInitIdentity(OM_uint32 *minor,
                      OM_uint32 timeReq,
                      gss_channel_bindings_t chanBindings,
                      gss_buffer_t inputToken,
-                     gss_buffer_t outputToken)
+                     gss_buffer_t outputToken,
+                     OM_uint32 *smFlags)
 {
-    OM_uint32 major;
-    int initialContextToken;
+    struct eap_config eapConfig;
 
-    initialContextToken = (inputToken->length == 0);
-    if (!initialContextToken) {
-        *minor = GSSEAP_WRONG_SIZE;
-        return GSS_S_DEFECTIVE_TOKEN;
+#ifdef GSSEAP_ENABLE_REAUTH
+    if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
+        OM_uint32 tmpMinor;
+
+        /* server didn't support reauthentication, sent EAP request */
+        gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER);
+        ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
+        GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
+    } else
+#endif
+        *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
+
+    assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
+    assert(inputToken == GSS_C_NO_BUFFER);
+
+    memset(&eapConfig, 0, sizeof(eapConfig));
+
+    ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
+                                             &gssEapPolicyCallbacks,
+                                             ctx,
+                                             &eapConfig);
+    if (ctx->initiatorCtx.eap == NULL) {
+        *minor = GSSEAP_PEER_SM_INIT_FAILURE;
+        return GSS_S_FAILURE;
     }
 
-    major = initBegin(minor, cred, ctx, target, mech,
-                      reqFlags, timeReq, chanBindings,
-                      inputToken, outputToken);
-    if (GSS_ERROR(major))
-        return major;
+    ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
+
+    /* poke EAP state machine */
+    if (eap_peer_sm_step(ctx->initiatorCtx.eap) != 0) {
+        *minor = GSSEAP_PEER_SM_STEP_FAILURE;
+        return GSS_S_FAILURE;
+    }
 
-    ctx->state = GSSEAP_STATE_AUTHENTICATE;
+    GSSEAP_SM_TRANSITION_NEXT(ctx);
 
     *minor = 0;
+
     return GSS_S_CONTINUE_NEEDED;
 }
 
-static struct wpabuf emptyWpaBuffer;
-
 static OM_uint32
 eapGssSmInitAuthenticate(OM_uint32 *minor,
                          gss_cred_id_t cred,
@@ -441,40 +624,24 @@ eapGssSmInitAuthenticate(OM_uint32 *minor,
                          OM_uint32 timeReq,
                          gss_channel_bindings_t chanBindings,
                          gss_buffer_t inputToken,
-                         gss_buffer_t outputToken)
+                         gss_buffer_t outputToken,
+                         OM_uint32 *smFlags)
 {
     OM_uint32 major;
     OM_uint32 tmpMinor;
     int code;
     struct wpabuf *resp = NULL;
-    int initialContextToken;
 
     *minor = 0;
 
-    initialContextToken = (inputToken == GSS_C_NO_BUFFER ||
-                           inputToken->length == 0);
+    assert(inputToken != GSS_C_NO_BUFFER);
 
     major = peerConfigInit(minor, cred, ctx);
     if (GSS_ERROR(major))
         goto cleanup;
 
-    if (ctx->initiatorCtx.eap == NULL) {
-        struct eap_config eapConfig;
-
-        memset(&eapConfig, 0, sizeof(eapConfig));
-
-        ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
-                                                 &gssEapPolicyCallbacks,
-                                                 ctx,
-                                                 &eapConfig);
-        if (ctx->initiatorCtx.eap == NULL) {
-            major = GSS_S_FAILURE;
-            *minor = GSSEAP_PEER_SM_INIT_FAILURE;
-            goto cleanup;
-        }
-
-        ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
-    }
+    assert(ctx->initiatorCtx.eap != NULL);
+    assert(ctx->flags & CTX_FLAG_EAP_PORT_ENABLED);
 
     ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
 
@@ -495,13 +662,10 @@ eapGssSmInitAuthenticate(OM_uint32 *minor,
 
         ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
         major = GSS_S_CONTINUE_NEEDED;
-        ctx->state = GSSEAP_STATE_EXTENSIONS_REQ;
+        GSSEAP_SM_TRANSITION_NEXT(ctx);
     } 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;
         *minor = GSSEAP_PEER_BAD_MESSAGE;
@@ -522,6 +686,8 @@ cleanup:
             major = tmpMajor;
             *minor = tmpMinor;
         }
+
+        *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
     }
 
     wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
@@ -531,57 +697,40 @@ 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 *smFlags)
 {
     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;
-}
-
-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;
+    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
 
-    major = gssEapVerifyExtensions(minor, cred, ctx, chanBindings, inputToken);
-    if (GSS_ERROR(major))
-        return major;
-
-    ctx->state = GSSEAP_STATE_ESTABLISHED;
-
-    *minor = 0;
-    return GSS_S_COMPLETE;
+    return GSS_S_CONTINUE_NEEDED;
 }
 
+#ifdef GSSEAP_ENABLE_REAUTH
 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,
@@ -590,69 +739,144 @@ eapGssSmInitEstablished(OM_uint32 *minor,
                         OM_uint32 timeReq,
                         gss_channel_bindings_t chanBindings,
                         gss_buffer_t inputToken,
-                        gss_buffer_t outputToken)
+                        gss_buffer_t outputToken,
+                        OM_uint32 *smFlags)
 {
-    /* Called with already established context */
-    *minor = GSSEAP_CONTEXT_ESTABLISHED;
-    return GSS_S_BAD_STATUS;
+    OM_uint32 major;
+
+    if (ctx->gssFlags & GSS_C_MUTUAL_FLAG) {
+        major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
+        if (GSS_ERROR(major))
+            return major;
+    }
+
+    *minor = 0;
+    return GSS_S_CONTINUE_NEEDED;
 }
+#endif /* GSSEAP_ENABLE_REAUTH */
 
 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)
+eapGssSmInitCompleteInitiatorExts(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 *smFlags)
 {
-    OM_uint32 major;
-    unsigned char *p;
+    GSSEAP_SM_TRANSITION_NEXT(ctx);
 
-    if (inputToken->length < 8) {
-        *minor = GSSEAP_TOK_TRUNC;
-        return GSS_S_DEFECTIVE_TOKEN;
-    }
+    *minor = 0;
+    *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
 
-    p = (unsigned char *)inputToken->value;
+    return GSS_S_CONTINUE_NEEDED;
+}
 
-    major = load_uint32_be(&p[0]);
-    *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
+static OM_uint32
+eapGssSmInitCompleteAcceptorExts(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 *smFlags)
+{
+    GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
 
-    if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
-        major = GSS_S_FAILURE;
-        *minor = GSSEAP_BAD_ERROR_TOKEN;
-    }
+    *minor = 0;
 
-    return major;
+    return GSS_S_COMPLETE;
 }
 
-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 & ~(GSSEAP_STATE_INITIAL),
+        0,
+        eapGssSmInitError
+    },
+    {
+        ITOK_TYPE_ACCEPTOR_NAME_RESP,
+        ITOK_TYPE_ACCEPTOR_NAME_REQ,
+        GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
+        0,
+        eapGssSmInitAcceptorName
+    },
+#ifdef GSSEAP_DEBUG
+    {
+        ITOK_TYPE_NONE,
+        ITOK_TYPE_VENDOR_INFO,
+        GSSEAP_STATE_INITIAL,
+        0,
+        eapGssSmInitVendorInfo
+    },
+#endif
+#ifdef GSSEAP_ENABLE_REAUTH
+    {
+        ITOK_TYPE_REAUTH_RESP,
+        ITOK_TYPE_REAUTH_REQ,
+        GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
+        0,
+        eapGssSmInitGssReauth
+    },
+#endif
+    {
+        ITOK_TYPE_NONE,
+        ITOK_TYPE_NONE,
+#ifdef GSSEAP_ENABLE_REAUTH
+        GSSEAP_STATE_REAUTHENTICATE |
+#endif
+        GSSEAP_STATE_INITIAL,
+        SM_ITOK_FLAG_REQUIRED,
+        eapGssSmInitIdentity
+    },
+    {
+        ITOK_TYPE_EAP_REQ,
+        ITOK_TYPE_EAP_RESP,
+        GSSEAP_STATE_AUTHENTICATE,
+        SM_ITOK_FLAG_REQUIRED,
+        eapGssSmInitAuthenticate
+    },
+    {
+        ITOK_TYPE_NONE,
+        ITOK_TYPE_GSS_CHANNEL_BINDINGS,
+        GSSEAP_STATE_INITIATOR_EXTS,
+        SM_ITOK_FLAG_REQUIRED,
+        eapGssSmInitGssChannelBindings
+    },
+    {
+        ITOK_TYPE_NONE,
+        ITOK_TYPE_NONE,
+        GSSEAP_STATE_INITIATOR_EXTS,
+        0,
+        eapGssSmInitCompleteInitiatorExts
+    },
 #ifdef GSSEAP_ENABLE_REAUTH
-    { TOK_TYPE_GSS_REAUTH,  TOK_TYPE_GSS_REAUTH,    eapGssSmInitGssReauth           },
+    {
+        ITOK_TYPE_REAUTH_CREDS,
+        ITOK_TYPE_NONE,
+        GSSEAP_STATE_ACCEPTOR_EXTS,
+        0,
+        eapGssSmInitReauthCreds
+    },
 #endif
+    /* other extensions go here */
+    {
+        ITOK_TYPE_NONE,
+        ITOK_TYPE_NONE,
+        GSSEAP_STATE_ACCEPTOR_EXTS,
+        0,
+        eapGssSmInitCompleteAcceptorExts
+    }
 };
 
 OM_uint32
@@ -670,14 +894,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;
 
@@ -696,7 +914,13 @@ gss_init_sec_context(OM_uint32 *minor,
 
         ctx->flags |= CTX_FLAG_INITIATOR;
 
-        initialContextToken = 1;
+        major = initBegin(minor, cred, ctx, target_name, mech_type,
+                          req_flags, time_req, input_chan_bindings);
+        if (GSS_ERROR(major)) {
+            gssEapReleaseContext(minor, &ctx);
+            return major;
+        }
+
         *context_handle = ctx;
     }
 
@@ -722,10 +946,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,66 +953,31 @@ 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)
         gssEapContextTime(&tmpMinor, ctx, time_rec);
 
-    assert(ctx->state == GSSEAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED);
+    assert(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
 
 cleanup:
     if (cred != GSS_C_NO_CREDENTIAL)
@@ -802,73 +987,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 */
index d5c3a52..fa9a6a5 100644 (file)
@@ -70,7 +70,7 @@ unwrapToken(OM_uint32 *minor,
 #ifdef HAVE_HEIMDAL_VERSION
             krb5_crypto krbCrypto,
 #else
-            krb5_keyblock *unused __attribute__((__unused__)),
+            krb5_keyblock *unused,
 #endif
             int *conf_state,
             gss_qop_t *qop_state,
diff --git a/util.h b/util.h
index 004c47e..e6c08ef 100644 (file)
--- a/util.h
+++ b/util.h
@@ -154,14 +154,29 @@ 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_INITIATOR_CONTEXT       = 0x0601,  /* initiator-sent context token */
+    TOK_TYPE_ACCEPTOR_CONTEXT        = 0x0602,  /* acceptor-sent context token */
 };
 
+/* inner token types and flags */
+#define ITOK_TYPE_NONE                  0x00000000
+#define ITOK_TYPE_CONTEXT_ERR           0x00000001 /* critical */
+#define ITOK_TYPE_ACCEPTOR_NAME_REQ     0x00000002 /* TBD */
+#define ITOK_TYPE_ACCEPTOR_NAME_RESP    0x00000003 /* TBD */
+#define ITOK_TYPE_EAP_RESP              0x00000004 /* critical, required, if not reauth */
+#define ITOK_TYPE_EAP_REQ               0x00000005 /* critical, required, if not reauth */
+#define ITOK_TYPE_GSS_CHANNEL_BINDINGS  0x00000006 /* critical, required, if not reauth */
+#define ITOK_TYPE_REAUTH_CREDS          0x00000007 /* optional */
+#define ITOK_TYPE_REAUTH_REQ            0x00000008 /* optional */
+#define ITOK_TYPE_REAUTH_RESP           0x00000009 /* optional */
+#define ITOK_TYPE_VERSION_INFO          0x0000000A /* optional */
+#define ITOK_TYPE_VENDOR_INFO           0x0000000B /* optional */
+
+#define ITOK_FLAG_CRITICAL              0x80000000  /* critical, wire flag */
+#define ITOK_FLAG_VERIFIED              0x40000000  /* verified, API flag */
+
+#define ITOK_TYPE_MASK                  (~(ITOK_FLAG_CRITICAL | ITOK_FLAG_VERIFIED))
+
 OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
 OM_uint32 gssEapReleaseContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
 
@@ -260,44 +275,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 +534,89 @@ OM_uint32
 sequenceInit(OM_uint32 *minor, void **vqueue, uint64_t seqnum,
              int do_replay, int do_sequence, int wide_nums);
 
+/* util_sm.c */
+enum gss_eap_state {
+    GSSEAP_STATE_INITIAL        = 0x01,     /* initial state */
+    GSSEAP_STATE_AUTHENTICATE   = 0x02,     /* exchange EAP messages */
+    GSSEAP_STATE_INITIATOR_EXTS = 0x04,     /* initiator extensions */
+    GSSEAP_STATE_ACCEPTOR_EXTS  = 0x08,     /* acceptor extensions */
+#ifdef GSSEAP_ENABLE_REAUTH
+    GSSEAP_STATE_REAUTHENTICATE = 0x10,     /* GSS reauthentication messages */
+#endif
+    GSSEAP_STATE_ESTABLISHED    = 0x20,     /* context established */
+    GSSEAP_STATE_ALL            = 0x3F
+};
+
+#define GSSEAP_STATE_NEXT(s)    ((s) << 1)
+
+#define GSSEAP_SM_STATE(ctx)                ((ctx)->state)
+
+#ifdef GSSEAP_DEBUG
+void gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state);
+#define GSSEAP_SM_TRANSITION(ctx, state)    gssEapSmTransition((ctx), (state))
+#else
+#define GSSEAP_SM_TRANSITION(ctx, newstate)    do { (ctx)->state = (newstate); } while (0)
+#endif
+
+#define GSSEAP_SM_TRANSITION_NEXT(ctx)      GSSEAP_SM_TRANSITION((ctx), GSSEAP_STATE_NEXT(GSSEAP_SM_STATE((ctx))))
+
+/* state machine entry */
+struct gss_eap_sm {
+    OM_uint32 inputTokenType;
+    OM_uint32 outputTokenType;
+    enum gss_eap_state validStates;
+    OM_uint32 itokFlags;
+    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,
+                              OM_uint32 *);
+};
+
+/* state machine flags, set by handler */
+#define SM_FLAG_FORCE_SEND_TOKEN            0x00000001  /* send token even if no inner tokens */
+#define SM_FLAG_OUTPUT_TOKEN_CRITICAL       0x00000002  /* output token is critical */
+
+/* state machine flags, set by state machine */
+#define SM_FLAG_INPUT_TOKEN_CRITICAL        0x10000000  /* input token was critical */
+
+#define SM_ITOK_FLAG_REQUIRED               0x00000001  /* received tokens must be present */
+
+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);
+
+void
+gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state);
+
 /* util_token.c */
+OM_uint32
+gssEapEncodeInnerTokens(OM_uint32 *minor,
+                        gss_buffer_set_t extensions,
+                        OM_uint32 *types,
+                        gss_buffer_t buffer);
+OM_uint32
+gssEapDecodeInnerTokens(OM_uint32 *minor,
+                        const gss_buffer_t buffer,
+                        gss_buffer_set_t *pExtensions,
+                        OM_uint32 **pTypes);
+
 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;
-}
index 90ce876..f730ddb 100644 (file)
@@ -446,9 +446,25 @@ gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id,
 bool
 gss_eap_radius_attr_provider::init(void)
 {
+    struct rs_context *radContext;
+
     gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS,
                                        "urn:ietf:params:gss-eap:radius-avp",
                                        createAttrContext);
+
+#if 1
+    /*
+     * This hack is necessary in order to force the loading of the global
+     * dictionary, otherwise accepting reauthentication tokens fails unless
+     * the acceptor has already accepted a normal authentication token.
+     */
+    if (rs_context_create(&radContext, RS_DICT_FILE) != 0) {
+        return false;
+    }
+
+    rs_context_destroy(radContext);
+#endif
+
     return true;
 }
 
@@ -660,10 +676,11 @@ avpImport(VALUE_PAIR **pVp,
     remain -= 4;
 
     da = dict_attrbyvalue(attrid);
-    if (da == NULL)
-        goto fail;
-
-    vp = pairalloc(da);
+    if (da != NULL) {
+        vp = pairalloc(da);
+    } else {
+        vp = paircreate(attrid, PW_TYPE_STRING);
+    }
     if (vp == NULL) {
         throw new std::bad_alloc;
         goto fail;
@@ -707,7 +724,9 @@ avpImport(VALUE_PAIR **pVp,
     return true;
 
 fail:
-    pairbasicfree(vp);
+    if (vp != NULL)
+        pairbasicfree(vp);
+    *pVp = NULL;
     return false;
 }
 
index cc54f9d..eaec68e 100644 (file)
@@ -149,7 +149,7 @@ gssEapRadiusMapError(OM_uint32 *minor,
                      struct rs_error *err);
 
 #define RS_CONFIG_FILE      SYSCONFDIR "/radsec.conf"
-#define RS_DICT_FILE        DATAROOTDIR "/freeradius/dictionary"
+#define RS_DICT_FILE        SYSCONFDIR "/raddb/dictionary"
 
 #define VENDORPEC_MS                        311 /* RFC 2548 */
 
index 37506e0..a03f285 100644 (file)
@@ -785,10 +785,10 @@ cleanup:
  */
 OM_uint32
 gssEapReauthComplete(OM_uint32 *minor,
-                    gss_ctx_id_t ctx,
-                    gss_cred_id_t cred,
-                    const gss_OID mech,
-                    OM_uint32 timeRec)
+                     gss_ctx_id_t ctx,
+                     gss_cred_id_t cred,
+                     const gss_OID mech,
+                     OM_uint32 timeRec)
 {
     OM_uint32 major, tmpMinor;
     gss_buffer_set_t keyData = GSS_C_NO_BUFFER_SET;
diff --git a/util_sm.c b/util_sm.c
new file mode 100644 (file)
index 0000000..ca69923
--- /dev/null
+++ b/util_sm.c
@@ -0,0 +1,418 @@
+/*
+ * 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"
+
+/* private flags */
+#define SM_FLAG_TRANSITED                   0x80000000
+
+#define SM_ASSERT_VALID(ctx, status)        do { \
+        assert(GSS_ERROR((status)) || \
+               ((status) == GSS_S_CONTINUE_NEEDED && ((ctx)->state > GSSEAP_STATE_INITIAL && (ctx)->state < GSSEAP_STATE_ESTABLISHED)) || \
+               ((status) == GSS_S_COMPLETE && (ctx)->state == GSSEAP_STATE_ESTABLISHED)); \
+    } while (0)
+
+#ifdef GSSEAP_DEBUG
+static const char *
+gssEapStateToString(enum gss_eap_state state)
+{
+    const char *s;
+
+    switch (state) {
+    case GSSEAP_STATE_INITIAL:
+        s = "INITIAL";
+        break;
+    case GSSEAP_STATE_AUTHENTICATE:
+        s = "AUTHENTICATE";
+        break;
+    case GSSEAP_STATE_INITIATOR_EXTS:
+        s = "INITIATOR_EXTS";
+        break;
+    case GSSEAP_STATE_ACCEPTOR_EXTS:
+        s = "ACCEPTOR_EXTS";
+        break;
+#ifdef GSSEAP_ENABLE_REAUTH
+    case GSSEAP_STATE_REAUTHENTICATE:
+        s = "REAUTHENTICATE";
+        break;
+#endif
+    case GSSEAP_STATE_ESTABLISHED:
+        s = "ESTABLISHED";
+        break;
+    default:
+        s = "INVALID";
+        break;
+    }
+
+    return s;
+}
+
+void
+gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state)
+{
+    assert(state >= GSSEAP_STATE_INITIAL);
+    assert(state <= GSSEAP_STATE_ESTABLISHED);
+
+    fprintf(stderr, "GSS-EAP: state transition %s->%s\n",
+            gssEapStateToString(GSSEAP_SM_STATE(ctx)),
+            gssEapStateToString(state));
+
+    ctx->state = state;
+}
+#endif /* GSSEAP_DEBUG */
+
+static OM_uint32
+makeErrorToken(OM_uint32 *minor,
+               OM_uint32 majorStatus,
+               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.
+     */
+    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;
+
+    major = gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
+    if (GSS_ERROR(major))
+        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;
+
+cleanup:
+    if (GSS_ERROR(major)) {
+        gss_release_buffer_set(&tmpMinor, &tokens);
+        tokens = GSS_C_NO_BUFFER_SET;
+        if (tokenTypes != NULL) {
+            GSSEAP_FREE(tokenTypes);
+            tokenTypes = NULL;
+        }
+    }
+
+    *pTokens = tokens;
+    *pTokenTypes = tokenTypes;
+
+    return major;
+}
+
+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, /* ordered by state */
+             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;
+    unsigned int smFlags = 0;
+    size_t i, j;
+    int initialContextToken = 0;
+    enum gss_eap_token_type tokType;
+
+    assert(smCount > 0);
+
+    *minor = 0;
+
+    outputToken->length = 0;
+    outputToken->value = NULL;
+
+    if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
+        major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
+                                  &unwrappedInputToken);
+        if (GSS_ERROR(major))
+            goto cleanup;
+
+        if (tokType != (CTX_IS_INITIATOR(ctx)
+                    ? TOK_TYPE_ACCEPTOR_CONTEXT : TOK_TYPE_INITIATOR_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;
+    } else {
+        initialContextToken = 1;
+    }
+
+    if (CTX_IS_ESTABLISHED(ctx)) {
+        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;
+
+    assert(innerInputTokens != GSS_C_NO_BUFFER_SET);
+
+    major = allocInnerTokens(minor, smCount, &innerOutputTokens, &outputTokenTypes);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    /* Process all the tokens that are valid for the current state. */
+    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;
+
+        /*
+         * 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) {
+                    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];
+                }
+            }
+            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,
+                                      &innerOutputToken, &smFlags);
+            if (GSS_ERROR(major))
+                break;
+
+            if (inputTokenType != NULL)
+                *inputTokenType |= ITOK_FLAG_VERIFIED;
+            if (ctx->state < oldState)
+                i = 0; /* restart */
+            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 (smFlags & SM_FLAG_OUTPUT_TOKEN_CRITICAL)
+                    outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
+                innerOutputTokens->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)) {
+                SM_ASSERT_VALID(ctx, major);
+                break;
+            }
+        } 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;
+        }
+    }
+
+    assert(innerOutputTokens->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) {
+                major = GSS_S_UNAVAILABLE;
+                *minor = GSSEAP_CRIT_ITOK_UNAVAILABLE;
+                goto cleanup;
+            }
+        }
+    }
+
+    /* 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 */
+
+        /* 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;
+        }
+
+        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 */
+        !CTX_IS_INITIATOR(ctx) ||                   /* any leg acceptor */
+        !CTX_IS_ESTABLISHED(ctx)) {                 /* non-last leg initiator */
+        tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens,
+                                           outputTokenTypes, &unwrappedOutputToken);
+        if (tmpMajor == GSS_S_COMPLETE) {
+            if (CTX_IS_INITIATOR(ctx))
+                tokType = TOK_TYPE_INITIATOR_CONTEXT;
+            else
+                tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
+
+            tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
+                                       tokType, outputToken);
+            if (GSS_ERROR(tmpMajor)) {
+                major = tmpMajor;
+                *minor = tmpMinor;
+                goto cleanup;
+            }
+        }
+    }
+
+    /* 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:
+    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..a929198 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 $
  */
@@ -164,6 +307,8 @@ der_read_length(unsigned char **buf, ssize_t *bufsize)
 size_t
 tokenSize(const gss_OID_desc *mech, size_t body_size)
 {
+    assert(mech != GSS_C_NO_OID);
+
     /* set body_size to sequence contents size */
     body_size += 4 + (size_t) mech->length;         /* NEED overflow check */
     return 1 + der_length_size(body_size) + body_size;