make extension tokens extensible
authorLuke Howard <lukeh@padl.com>
Sun, 10 Oct 2010 17:16:05 +0000 (19:16 +0200)
committerLuke Howard <lukeh@padl.com>
Sun, 10 Oct 2010 17:16:05 +0000 (19:16 +0200)
mech_eap/accept_sec_context.c
mech_eap/gssapiP_eap.h
mech_eap/init_sec_context.c
mech_eap/util.h
mech_eap/util_exts.c [new file with mode: 0644]

index b1119f2..debe76a 100644 (file)
@@ -405,11 +405,11 @@ cleanup:
 }
 
 static OM_uint32
-acceptGssChannelBindings(OM_uint32 *minor,
-                         gss_ctx_id_t ctx,
+verifyGssChannelBindings(OM_uint32 *minor,
                          gss_cred_id_t cred,
-                         gss_buffer_t inputToken,
-                         gss_channel_bindings_t chanBindings)
+                         gss_ctx_id_t ctx,
+                         gss_channel_bindings_t chanBindings,
+                         gss_buffer_t inputToken)
 {
     OM_uint32 major, tmpMinor;
     gss_iov_buffer_desc iov[2];
@@ -430,7 +430,7 @@ acceptGssChannelBindings(OM_uint32 *minor,
         !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
         major = GSS_S_BAD_BINDINGS;
     } else {
-        major = GSS_S_CONTINUE_NEEDED;
+        major = GSS_S_COMPLETE;
     }
 
     gss_release_buffer(&tmpMinor, &iov[0].buffer);
@@ -438,6 +438,11 @@ acceptGssChannelBindings(OM_uint32 *minor,
     return major;
 }
 
+static struct gss_eap_extension_provider
+eapGssVerifyInitExtensions[] = {
+    { EXT_TYPE_GSS_CHANNEL_BINDINGS, 1, verifyGssChannelBindings },
+};
+
 static OM_uint32
 eapGssSmAcceptExtensionsReq(OM_uint32 *minor,
                             gss_ctx_id_t ctx,
@@ -448,11 +453,10 @@ eapGssSmAcceptExtensionsReq(OM_uint32 *minor,
 {
     OM_uint32 major;
 
-    outputToken->length = 0;
-    outputToken->value = NULL;
-
-    major = acceptGssChannelBindings(minor, ctx, cred, inputToken,
-                                     chanBindings);
+    major = gssEapVerifyExtensions(minor, cred, ctx, eapGssVerifyInitExtensions,
+                                   sizeof(eapGssVerifyInitExtensions) /
+                                        sizeof(eapGssVerifyInitExtensions[0]),
+                                   chanBindings, inputToken);
     if (GSS_ERROR(major))
         return major;
 
@@ -462,6 +466,31 @@ eapGssSmAcceptExtensionsReq(OM_uint32 *minor,
 }
 
 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 struct gss_eap_extension_provider
+eapGssMakeAcceptExtensions[] = {
+    { EXT_TYPE_REAUTH_CREDS, 0, makeReauthCreds },
+};
+
+static OM_uint32
 eapGssSmAcceptExtensionsResp(OM_uint32 *minor,
                              gss_ctx_id_t ctx,
                              gss_cred_id_t cred,
@@ -469,31 +498,14 @@ eapGssSmAcceptExtensionsResp(OM_uint32 *minor,
                              gss_channel_bindings_t chanBindings,
                              gss_buffer_t outputToken)
 {
-    OM_uint32 major, tmpMinor;
-    gss_buffer_desc credsToken = GSS_C_EMPTY_BUFFER;
+    OM_uint32 major;
 
-#ifdef GSSEAP_ENABLE_REAUTH
-    /*
-     * If we're built with fast reauthentication enabled, then
-     * fabricate a ticket from the initiator to ourselves.
-     * Otherwise return an empty token.
-     */
-    major = gssEapMakeReauthCreds(minor, ctx, cred, &credsToken);
+    major = gssEapMakeExtensions(minor, cred, ctx, eapGssMakeAcceptExtensions,
+                                 sizeof(eapGssMakeAcceptExtensions) /
+                                    sizeof(eapGssMakeAcceptExtensions[0]),
+                                 chanBindings, outputToken);
     if (GSS_ERROR(major))
         return major;
-#else
-    credsToken.value = "";
-#endif /* GSSEAP_ENABLE_REAUTH */
-
-    major = duplicateBuffer(minor, &credsToken, outputToken);
-    if (GSS_ERROR(major)) {
-        gss_release_buffer(&tmpMinor, &credsToken);
-        return major;
-    }
-
-#ifdef GSSEAP_ENABLE_REAUTH
-    gss_release_buffer(&tmpMinor, &credsToken);
-#endif
 
     ctx->state = EAP_STATE_ESTABLISHED;
 
index 5ff9bd2..473aa84 100644 (file)
@@ -199,7 +199,6 @@ struct gss_ctx_id_struct {
 #define KEY_USAGE_ACCEPTOR_SIGN             23
 #define KEY_USAGE_INITIATOR_SEAL            24
 #define KEY_USAGE_INITIATOR_SIGN            25
-#define KEY_USAGE_CHANNEL_BINDINGS          64
 
 /* wrap_iov.c */
 OM_uint32
index 68c55eb..80abb05 100644 (file)
@@ -487,7 +487,8 @@ cleanup:
 }
 
 static OM_uint32
-initGssChannelBindings(OM_uint32 *minor,
+makeGssChannelBindings(OM_uint32 *minor,
+                       gss_cred_id_t cred,
                        gss_ctx_id_t ctx,
                        gss_channel_bindings_t chanBindings,
                        gss_buffer_t outputToken)
@@ -495,7 +496,6 @@ initGssChannelBindings(OM_uint32 *minor,
     OM_uint32 major;
     gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
 
-
     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
         buffer = chanBindings->application_data;
 
@@ -504,9 +504,14 @@ initGssChannelBindings(OM_uint32 *minor,
     if (GSS_ERROR(major))
         return major;                       
 
-    return GSS_S_CONTINUE_NEEDED;
+    return GSS_S_COMPLETE;
 }
 
+static struct gss_eap_extension_provider
+eapGssMakeInitExtensions[] = {
+    { EXT_TYPE_GSS_CHANNEL_BINDINGS, 1, makeGssChannelBindings },
+};
+
 static OM_uint32
 eapGssSmInitExtensionsReq(OM_uint32 *minor,
                           gss_cred_id_t cred,
@@ -519,27 +524,42 @@ eapGssSmInitExtensionsReq(OM_uint32 *minor,
                           gss_buffer_t inputToken,
                           gss_buffer_t outputToken)
 {
-    OM_uint32 major, tmpMinor;
-    gss_buffer_desc cbToken = GSS_C_EMPTY_BUFFER;
+    OM_uint32 major;
 
-    major = initGssChannelBindings(minor, ctx, chanBindings, &cbToken);
+    major = gssEapMakeExtensions(minor, cred, ctx, eapGssMakeInitExtensions,
+                                 sizeof(eapGssMakeInitExtensions) /
+                                    sizeof(eapGssMakeInitExtensions[0]),
+                                 chanBindings, outputToken);
     if (GSS_ERROR(major))
         return major;
 
-    ctx->state = EAP_STATE_EXTENSIONS_RESP;
-
-    major = duplicateBuffer(minor, &cbToken, outputToken);
-    if (GSS_ERROR(major)) {
-        gss_release_buffer(&tmpMinor, &cbToken);
-        return major;
-    }
+    assert(outputToken->value != NULL);
 
-    gss_release_buffer(&tmpMinor, &cbToken);
+    ctx->state = EAP_STATE_EXTENSIONS_RESP;
 
     return GSS_S_CONTINUE_NEEDED;
 }
 
 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)
+{
+#ifdef GSSEAP_ENABLE_REAUTH
+    return gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
+#else
+    return GSS_S_UNAVAILABLE;
+#endif
+}
+
+static struct gss_eap_extension_provider
+eapGssVerifyAcceptExtensions[] = {
+    { EXT_TYPE_REAUTH_CREDS, 0, verifyReauthCreds },
+};
+
+static OM_uint32
 eapGssSmInitExtensionsResp(OM_uint32 *minor,
                            gss_cred_id_t cred,
                            gss_ctx_id_t ctx,
@@ -551,13 +571,14 @@ eapGssSmInitExtensionsResp(OM_uint32 *minor,
                            gss_buffer_t inputToken,
                            gss_buffer_t outputToken)
 {
-#ifdef GSSEAP_ENABLE_REAUTH
     OM_uint32 major;
 
-    major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
+    major = gssEapVerifyExtensions(minor, cred, ctx, eapGssVerifyAcceptExtensions,
+                                   sizeof(eapGssVerifyAcceptExtensions) /
+                                        sizeof(eapGssVerifyAcceptExtensions[0]),
+                                   chanBindings, inputToken);
     if (GSS_ERROR(major))
         return major;
-#endif
 
     ctx->state = EAP_STATE_ESTABLISHED;
 
index 58a4941..9184ebf 100644 (file)
@@ -243,6 +243,42 @@ gssEapDeriveRfc3961Key(OM_uint32 *minor,
                        krb5_enctype enctype,
                        krb5_keyblock *pKey);
 
+/* util_exts.c */
+#define EXT_FLAG_CRITICAL               0x80000000
+#define EXT_FLAG_VERIFIED               0x40000000
+
+#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;
+    OM_uint32 (*callback)(OM_uint32 *,
+                          gss_cred_id_t,
+                          gss_ctx_id_t,
+                          gss_channel_bindings_t,
+                          gss_buffer_t);
+};
+
+OM_uint32
+gssEapMakeExtensions(OM_uint32 *minor,
+                     gss_cred_id_t cred,
+                     gss_ctx_id_t ctx,
+                     const struct gss_eap_extension_provider *exts,
+                     size_t count,
+                     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,
+                       const struct gss_eap_extension_provider *exts,
+                       size_t count,
+                       gss_channel_bindings_t chanBindings,
+                       const gss_buffer_t buffer);
+
 /* util_krb.c */
 OM_uint32
 gssEapKerberosInit(OM_uint32 *minor, krb5_context *context);
diff --git a/mech_eap/util_exts.c b/mech_eap/util_exts.c
new file mode 100644 (file)
index 0000000..906430c
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+#include "gssapiP_eap.h"
+
+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) {
+        *minor = ENOMEM;
+        major = GSS_S_FAILURE;
+        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);
+
+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;
+            goto cleanup;
+        }
+
+        ntypes = GSSEAP_REALLOC(types,
+                                (extensions->count + 1) * sizeof(OM_uint32));
+        if (ntypes == NULL) {
+            *minor = ENOMEM;
+            major = GSS_S_FAILURE;
+            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;
+            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;
+}
+
+OM_uint32
+gssEapMakeExtensions(OM_uint32 *minor,
+                     gss_cred_id_t cred,
+                     gss_ctx_id_t ctx,
+                     const struct gss_eap_extension_provider *exts,
+                     size_t count,
+                     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(count, sizeof(OM_uint32));
+    if (types == NULL) {
+        *minor = ENOMEM;
+        major = GSS_S_FAILURE;
+        goto cleanup;
+    }
+
+    for (i = 0, j = 0; i < count; 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->callback(minor, cred, ctx, chanBindings, &extension);
+        if (GSS_ERROR(major)) {
+            if (ext->critical)
+                continue;
+            else
+                goto cleanup;
+        }
+
+        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
+gssEapVerifyExtensions(OM_uint32 *minor,
+                       gss_cred_id_t cred,
+                       gss_ctx_id_t ctx,
+                       const struct gss_eap_extension_provider *exts,
+                       size_t count,
+                       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 < count; 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->callback(minor, cred, ctx, chanBindings,
+                                  &extensions->elements[j]);
+            if (GSS_ERROR(major))
+                goto cleanup;
+
+            types[j] |= EXT_FLAG_VERIFIED;
+        } else if (ext->critical) {
+            /* Critical extension missing */
+            *minor = ENOENT;
+            major = GSS_S_UNAVAILABLE;
+            gssEapSaveStatusInfo(*minor,
+                                 "Missing critical GSS EAP extension %08x",
+                                 ext->type);
+            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) {
+            *minor = ENOSYS;
+            major = GSS_S_UNAVAILABLE;
+            gssEapSaveStatusInfo(*minor,
+                                 "Received unknown critical GSS EAP extension %08x",
+                                 (types[i] & EXT_TYPE_MASK));
+            goto cleanup;
+        }
+    }
+
+    *minor = 0;
+    major = GSS_S_COMPLETE;
+
+cleanup:
+    gss_release_buffer_set(&tmpMinor, &extensions);
+    if (types != NULL)
+        GSSEAP_FREE(types);
+
+    return major;
+}