More work on SAML code
[mech_eap.orig] / accept_sec_context.c
index e254ade..cefb5a9 100644 (file)
 
 #include "gssapiP_eap.h"
 
+#ifdef BUILTIN_EAP
+#define EAP_KEY_AVAILABLE(ctx)  ((ctx)->acceptorCtx.eapPolInterface->eapKeyAvailable)
+#define EAP_KEY_DATA(ctx)       ((ctx)->acceptorCtx.eapPolInterface->eapKeyData)
+#define EAP_KEY_LENGTH(ctx)     ((ctx)->acceptorCtx.eapPolInterface->eapKeyDataLen)
+#else
+#define EAP_KEY_AVAILABLE(ctx)  0
+#define EAP_KEY_DATA(ctx)       NULL
+#define EAP_KEY_LENGTH(ctx)     0
+#endif /* BUILTIN_EAP */
+
+static OM_uint32
+acceptReady(OM_uint32 *minor, gss_ctx_id_t ctx);
+
+#ifdef BUILTIN_EAP
 #define EAP_MAX_METHODS 8
 
 #define EAP_TTLS_AUTH_PAP 1
@@ -39,7 +53,6 @@
 #define EAP_TTLS_AUTH_MSCHAP 4
 #define EAP_TTLS_AUTH_MSCHAPV2 8
 
-#if 1
 struct eap_user {
         struct {
                 int vendor;
@@ -136,6 +149,8 @@ int eap_sm_method_pending(struct eap_sm *sm);
 const u8 * eap_get_identity(struct eap_sm *sm, size_t *len);
 struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
 
+#include <eap_server/eap_i.h>
+
 static OM_uint32
 initTls(OM_uint32 *minor,
         gss_ctx_id_t ctx)
@@ -211,42 +226,6 @@ serverGetEapReqIdText(void *ctx,
     *len = 0;
     return NULL;
 }
-#endif
-
-static OM_uint32
-serverDeriveKey(OM_uint32 *minor,
-                gss_ctx_id_t ctx)
-{
-    OM_uint32 major;
-    krb5_context krbContext;
-
-    GSSEAP_KRB_INIT(&krbContext);
-
-    /* Cache encryption type derived from selected mechanism OID */
-    major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
-    if (GSS_ERROR(major))
-        return major;
-
-    if (ctx->encryptionType != ENCTYPE_NULL &&
-        ctx->acceptorCtx.eapPolInterface->eapKeyAvailable) {
-        major = gssEapDeriveRFC3961Key(minor,
-                                       ctx->acceptorCtx.eapPolInterface->eapKeyData,
-                                       ctx->acceptorCtx.eapPolInterface->eapKeyDataLen,
-                                       ctx->encryptionType,
-                                       &ctx->rfc3961Key);
-        if (GSS_ERROR(major))
-            return major;
-    } else {
-        /*
-         * draft-howlett-eap-gss says that integrity/confidentialty should
-         * always be advertised as available, but if we have no keying
-         * material it seems confusing to the caller to advertise this.
-         */
-        ctx->gssFlags &= ~(GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
-    }
-
-    return GSS_S_COMPLETE;
-}
 
 static OM_uint32
 eapGssSmAcceptAuthenticate(OM_uint32 *minor,
@@ -256,26 +235,23 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
                            gss_channel_bindings_t chanBindings,
                            gss_buffer_t outputToken)
 {
-    OM_uint32 major, tmpMinor, tmpMajor;
+    OM_uint32 major;
+    OM_uint32 tmpMinor, tmpMajor;
     int code;
     struct wpabuf respData;
-    struct eap_config *config = (struct eap_config *)&ctx->acceptorCtx.eapConfig;
     static struct eapol_callbacks cb = { serverGetEapUser, serverGetEapReqIdText };
-
-    wpabuf_set(&respData, inputToken->value, inputToken->length);
-    ctx->acceptorCtx.eapPolInterface->eapRespData = &respData;
-    ctx->acceptorCtx.eapPolInterface->eapResp = TRUE;
-
     if (ctx->acceptorCtx.eap == NULL) {
-        /* initial context token */
-        config->eap_server = 1;
-        config->ssl_ctx = ctx->acceptorCtx.tlsContext;
+        struct eap_config eapConfig;
 
         major = initTls(minor, ctx);
         if (GSS_ERROR(major))
             goto cleanup;
 
-        ctx->acceptorCtx.eap = eap_server_sm_init(ctx, &cb, config);
+        memset(&eapConfig, 0, sizeof(eapConfig));
+        eapConfig.eap_server = 1;
+        eapConfig.ssl_ctx = ctx->acceptorCtx.tlsContext;
+
+        ctx->acceptorCtx.eap = eap_server_sm_init(ctx, &cb, &eapConfig);
         if (ctx->acceptorCtx.eap == NULL) {
             major = GSS_S_FAILURE;
             goto cleanup;
@@ -286,12 +262,18 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
         ctx->acceptorCtx.eapPolInterface->eapRestart = TRUE;
     }
 
-    if (ctx->acceptorName == GSS_C_NO_NAME && cred->name != GSS_C_NO_NAME) {
+    if (ctx->acceptorName == GSS_C_NO_NAME &&
+        cred != GSS_C_NO_CREDENTIAL &&
+        cred->name != GSS_C_NO_NAME) {
         major = gss_duplicate_name(minor, cred->name, &ctx->acceptorName);
         if (GSS_ERROR(major))
             goto cleanup;
     }
 
+    wpabuf_set(&respData, inputToken->value, inputToken->length);
+    ctx->acceptorCtx.eapPolInterface->eapRespData = &respData;
+    ctx->acceptorCtx.eapPolInterface->eapResp = TRUE;
+
     code = eap_server_sm_step(ctx->acceptorCtx.eap);
 
     if (ctx->acceptorCtx.eapPolInterface->eapReq) {
@@ -300,13 +282,13 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
     }
 
     if (ctx->acceptorCtx.eapPolInterface->eapSuccess) {
-        major = serverDeriveKey(minor, ctx);
+        ctx->acceptorCtx.eapPolInterface->eapSuccess = 0;
+        major = acceptReady(minor, ctx);
         if (GSS_ERROR(major))
             goto cleanup;
 
-        ctx->acceptorCtx.eapPolInterface->eapSuccess = 0;
-        ctx->state = EAP_STATE_ESTABLISHED;
-        major = GSS_S_COMPLETE;
+        ctx->state = EAP_STATE_GSS_CHANNEL_BINDINGS;
+        major = GSS_S_CONTINUE_NEEDED;
     } else if (ctx->acceptorCtx.eapPolInterface->eapFail) {
         ctx->acceptorCtx.eapPolInterface->eapFail = 0;
         major = GSS_S_FAILURE;
@@ -323,11 +305,82 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
         tmpMajor = duplicateBuffer(&tmpMinor, &buf, outputToken);
         if (GSS_ERROR(tmpMajor)) {
             major = tmpMajor;
+            *minor = tmpMinor;
             goto cleanup;
         }
     }
 
 cleanup:
+    ctx->acceptorCtx.eapPolInterface->eapRespData = NULL;
+
+    return major;
+}
+#else
+static OM_uint32
+eapGssSmAcceptAuthenticate(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)
+{
+    OM_uint32 major, tmpMinor;
+
+cleanup:
+    return major;
+}
+#endif /* BUILTIN_EAP */
+
+static OM_uint32
+eapGssSmAcceptGssChannelBindings(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)
+{
+    OM_uint32 major, tmpMinor;
+    gss_iov_buffer_desc iov[2];
+
+    outputToken->length = 0;
+    outputToken->value = NULL;
+
+    if (chanBindings == GSS_C_NO_CHANNEL_BINDINGS) {
+        ctx->state = EAP_STATE_ESTABLISHED;
+        return GSS_S_COMPLETE;
+    }
+
+    if (inputToken->length < 14) {
+        return GSS_S_DEFECTIVE_TOKEN;
+    }
+
+    iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
+    iov[0].buffer.length = 0;
+    iov[0].buffer.value = NULL;
+
+    if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
+        iov[0].buffer = chanBindings->application_data;
+
+    iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER;
+    iov[1].buffer.length = 16;
+    iov[1].buffer.value = (unsigned char *)inputToken->value - 2;
+
+    assert(load_uint16_be(iov[1].buffer.value) == TOK_TYPE_GSS_CB);
+
+    iov[2].type = GSS_IOV_BUFFER_TYPE_TRAILER;
+    iov[2].buffer.length = inputToken->length - 14;
+    iov[2].buffer.value = (unsigned char *)inputToken->value + 14;
+
+    major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
+                                    iov, 3, TOK_TYPE_GSS_CB);
+    if (major == GSS_S_COMPLETE) {
+        ctx->state = EAP_STATE_ESTABLISHED;
+    }
+
+#if 0
+    gss_release_buffer(&tmpMinor, &iov[0].buffer);
+#endif
+
     return major;
 }
 
@@ -344,7 +397,7 @@ eapGssSmAcceptEstablished(OM_uint32 *minor,
     return GSS_S_BAD_STATUS;
 }
 
-static struct eap_gss_acceptor_sm {
+static struct gss_eap_acceptor_sm {
     enum gss_eap_token_type inputTokenType;
     enum gss_eap_token_type outputTokenType;
     OM_uint32 (*processToken)(OM_uint32 *,
@@ -354,11 +407,13 @@ static struct eap_gss_acceptor_sm {
                               gss_channel_bindings_t,
                               gss_buffer_t);
 } eapGssAcceptorSm[] = {
-    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  eapGssSmAcceptAuthenticate   },
-    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  NULL                         },
-    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  NULL                         },
-    { TOK_TYPE_GSS_CB,      TOK_TYPE_NONE,     NULL                         },
-    { TOK_TYPE_NONE,        TOK_TYPE_NONE,     eapGssSmAcceptEstablished    },
+    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  eapGssSmAcceptAuthenticate       },
+#if 0
+    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  NULL                             },
+    { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  NULL                             },
+#endif
+    { TOK_TYPE_GSS_CB,      TOK_TYPE_NONE,     eapGssSmAcceptGssChannelBindings },
+    { TOK_TYPE_NONE,        TOK_TYPE_NONE,     eapGssSmAcceptEstablished        },
 };
 
 OM_uint32
@@ -374,9 +429,10 @@ gss_accept_sec_context(OM_uint32 *minor,
                        OM_uint32 *time_rec,
                        gss_cred_id_t *delegated_cred_handle)
 {
-    OM_uint32 major, tmpMinor;
+    OM_uint32 major;
+    OM_uint32 tmpMajor, tmpMinor;
     gss_ctx_id_t ctx = *context_handle;
-    struct eap_gss_acceptor_sm *sm = NULL;
+    struct gss_eap_acceptor_sm *sm = NULL;
     gss_buffer_desc innerInputToken, innerOutputToken;
 
     *minor = 0;
@@ -412,7 +468,15 @@ gss_accept_sec_context(OM_uint32 *minor,
     if (GSS_ERROR(major))
         goto cleanup;
 
+    /* If credentials were provided, check they're usable with this mech */
+    if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
+        major = GSS_S_BAD_MECH;
+        goto cleanup;
+    }
+
     do {
+        sm = &eapGssAcceptorSm[ctx->state];
+
         major = (sm->processToken)(minor,
                                    ctx,
                                    cred,
@@ -423,28 +487,34 @@ gss_accept_sec_context(OM_uint32 *minor,
             goto cleanup;
     } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.length == 0);
 
-    if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) {
-        major = gss_duplicate_name(minor, ctx->initiatorName, src_name);
-        if (GSS_ERROR(major))
-            goto cleanup;
-    }
     if (mech_type != NULL) {
         if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type))
             duplicateOid(&tmpMinor, ctx->mechanismUsed, mech_type);
     }
     if (innerOutputToken.length != 0) {
-        major = gssEapMakeToken(minor, ctx, &innerOutputToken,
-                                sm->outputTokenType, output_token);
-        if (GSS_ERROR(major))
+        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)
-        gss_context_time(&tmpMinor, ctx, time_rec);
     if (delegated_cred_handle != NULL)
         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
 
+    if (major == GSS_S_COMPLETE) {
+        if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) {
+            major = gss_duplicate_name(&tmpMinor, ctx->initiatorName, src_name);
+            if (GSS_ERROR(major))
+                goto cleanup;
+        }
+        if (time_rec != NULL)
+            gss_context_time(&tmpMinor, ctx, time_rec);
+    }
+
     assert(ctx->state == EAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED);
 
 cleanup:
@@ -457,3 +527,50 @@ cleanup:
 
     return major;
 }
+
+/*
+ * Mark a context as ready for cryptographic operations
+ */
+static OM_uint32
+acceptReady(OM_uint32 *minor, gss_ctx_id_t ctx)
+{
+    OM_uint32 major;
+
+    /* Cache encryption type derived from selected mechanism OID */
+    major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
+    if (GSS_ERROR(major))
+        return major;
+
+    if (ctx->encryptionType != ENCTYPE_NULL &&
+        EAP_KEY_AVAILABLE(ctx)) {
+        major = gssEapDeriveRfc3961Key(minor,
+                                       EAP_KEY_DATA(ctx),
+                                       EAP_KEY_LENGTH(ctx),
+                                       ctx->encryptionType,
+                                       &ctx->rfc3961Key);
+        if (GSS_ERROR(major))
+            return major;
+
+        major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
+                                           &ctx->checksumType);
+        if (GSS_ERROR(major))
+            return major;
+    } else {
+        /*
+         * draft-howlett-eap-gss says that integrity/confidentialty should
+         * always be advertised as available, but if we have no keying
+         * material it seems confusing to the caller to advertise this.
+         */
+        ctx->gssFlags &= ~(GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
+    }
+
+    major = sequenceInit(minor,
+                         &ctx->seqState, ctx->recvSeq,
+                         ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
+                         ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
+                         TRUE);
+    if (GSS_ERROR(major))
+        return major;
+
+    return GSS_S_COMPLETE;
+}