Merge remote branch 'origin/master' into HEAD
[moonshot.git] / moonshot / mech_eap / util_token.c
index a929198..5e69b26 100644 (file)
 #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,
@@ -172,7 +106,7 @@ gssEapDecodeInnerTokens(OM_uint32 *minor,
         types[extensions->count] = load_uint32_be(&p[0]);
         extension.length = load_uint32_be(&p[4]);
 
-        if (remain < 8 + extension.length) {
+        if (remain < ITOK_HEADER_LENGTH + extension.length) {
             major = GSS_S_DEFECTIVE_TOKEN;
             *minor = GSSEAP_TOK_TRUNC;
             goto cleanup;
@@ -183,8 +117,8 @@ gssEapDecodeInnerTokens(OM_uint32 *minor,
         if (GSS_ERROR(major))
             goto cleanup;
 
-        p      += 8 + extension.length;
-        remain -= 8 + extension.length;
+        p      += ITOK_HEADER_LENGTH + extension.length;
+        remain -= ITOK_HEADER_LENGTH + extension.length;
     } while (remain != 0);
 
 cleanup:
@@ -201,6 +135,215 @@ cleanup:
 }
 
 /*
+ * Add some data to the initiator/acceptor conversation.
+ */
+static OM_uint32
+recordTokens(OM_uint32 *minor,
+             gss_ctx_id_t ctx,
+             gss_buffer_t tokens,
+             size_t tokensCount)
+{
+    unsigned char *buf;
+    size_t i, size, offset;
+
+    size = ctx->conversation.length;
+
+    for (i = 0; i < tokensCount; i++)
+        size += tokens[i].length;
+
+    buf = GSSEAP_REALLOC(ctx->conversation.value, size);
+    if (buf == NULL) {
+        *minor = ENOMEM;
+        return GSS_S_FAILURE;
+    }
+
+    offset = ctx->conversation.length;
+
+    ctx->conversation.length = size;
+    ctx->conversation.value = buf;
+
+    for (i = 0; i < tokensCount; i++) {
+        memcpy(buf + offset, tokens[i].value, tokens[i].length);
+        offset += tokens[i].length;
+    }
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
+/*
+ * Record the context token header.
+ */
+OM_uint32
+gssEapRecordContextTokenHeader(OM_uint32 *minor,
+                               gss_ctx_id_t ctx,
+                               enum gss_eap_token_type tokType)
+{
+    unsigned char wireOidHeader[2], wireTokType[2];
+    gss_buffer_desc buffers[3];
+
+    assert(ctx->mechanismUsed != GSS_C_NO_OID);
+
+    wireOidHeader[0] = 0x06;
+    wireOidHeader[1] = ctx->mechanismUsed->length;
+    buffers[0].length = sizeof(wireOidHeader);
+    buffers[0].value  = wireOidHeader;
+
+    buffers[1].length = ctx->mechanismUsed->length;
+    buffers[1].value  = ctx->mechanismUsed->elements;
+
+    store_uint16_be(tokType, wireTokType);
+    buffers[2].length = sizeof(wireTokType);
+    buffers[2].value = wireTokType;
+
+    return recordTokens(minor, ctx, buffers, sizeof(buffers)/sizeof(buffers[0]));
+}
+
+/*
+ * Record an inner context token.
+ */
+OM_uint32
+gssEapRecordInnerContextToken(OM_uint32 *minor,
+                              gss_ctx_id_t ctx,
+                              gss_buffer_t innerToken,
+                              OM_uint32 itokType)
+{
+    gss_buffer_desc buffers[2];
+    unsigned char itokHeader[ITOK_HEADER_LENGTH];
+
+    assert(innerToken != GSS_C_NO_BUFFER);
+
+    store_uint32_be(itokType,           &itokHeader[0]);
+    store_uint32_be(innerToken->length, &itokHeader[4]);
+    buffers[0].length = sizeof(itokHeader);
+    buffers[0].value  = itokHeader;
+
+    buffers[1] = *innerToken;
+
+    return recordTokens(minor, ctx, buffers, sizeof(buffers)/sizeof(buffers[0]));
+}
+
+OM_uint32
+gssEapVerifyContextToken(OM_uint32 *minor,
+                         gss_ctx_id_t ctx,
+                         const gss_buffer_t inputToken,
+                         enum gss_eap_token_type tokType,
+                         gss_buffer_t innerInputToken)
+{
+    OM_uint32 major;
+    size_t bodySize;
+    unsigned char *p = (unsigned char *)inputToken->value;
+    gss_OID_desc oidBuf;
+    gss_OID oid;
+    enum gss_eap_token_type actualTokType;
+    gss_buffer_desc tokenBuf;
+
+    if (ctx->mechanismUsed != GSS_C_NO_OID) {
+        oid = ctx->mechanismUsed;
+    } else {
+        oidBuf.elements = NULL;
+        oidBuf.length = 0;
+        oid = &oidBuf;
+    }
+
+    major = verifyTokenHeader(minor, oid, &bodySize, &p,
+                              inputToken->length, &actualTokType);
+    if (GSS_ERROR(major))
+        return major;
+
+    if (actualTokType != tokType) {
+        *minor = GSSEAP_WRONG_TOK_ID;
+        return GSS_S_DEFECTIVE_TOKEN;
+    }
+
+    if (ctx->mechanismUsed == GSS_C_NO_OID) {
+        major = gssEapCanonicalizeOid(minor, oid, 0, &ctx->mechanismUsed);
+        if (GSS_ERROR(major))
+            return major;
+    }
+
+    innerInputToken->length = bodySize;
+    innerInputToken->value = p;
+
+    /*
+     * Add OID, tokenType, body to conversation; variable length
+     * header omitted. A better API to verifyTokenHeader would
+     * avoid this ugly pointer arithmetic. XXX FIXME
+     */
+    tokenBuf.value = p - (2 + oid->length + 2);
+    tokenBuf.length = 2 + oid->length + 2 + bodySize;
+
+    major = recordTokens(minor, ctx, &tokenBuf, 1);
+    if (GSS_ERROR(major))
+        return major;
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
+OM_uint32
+gssEapEncodeSupportedExts(OM_uint32 *minor,
+                          OM_uint32 *types,
+                          size_t typesCount,
+                          gss_buffer_t outputToken)
+{
+    size_t i;
+    unsigned char *p;
+
+    outputToken->value = GSSEAP_MALLOC(4 * typesCount);
+    if (outputToken->value == NULL) {
+        *minor = ENOMEM;
+        return GSS_S_FAILURE;
+    }
+    p = (unsigned char *)outputToken->value;
+
+    outputToken->length = 4 * typesCount;
+
+    for (i = 0; i < typesCount; i++) {
+        store_uint32_be(types[i], p);
+        p += 4;
+    }
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
+OM_uint32
+gssEapProcessSupportedExts(OM_uint32 *minor,
+                           gss_buffer_t inputToken,
+                           struct gss_eap_itok_map *map,
+                           size_t mapCount,
+                           OM_uint32 *flags)
+{
+    size_t i;
+    unsigned char *p;
+
+    if ((inputToken->length % 4) != 0) {
+        *minor = GSSEAP_TOK_TRUNC;
+        return GSS_S_DEFECTIVE_TOKEN;
+    }
+
+    p = (unsigned char *)inputToken->value;
+
+    for (i = 0; i < inputToken->length / 4; i++) {
+        OM_uint32 type = load_uint32_be(p);
+        size_t j;
+
+        for (j = 0; j < mapCount; j++) {
+            if (map->type == type) {
+                *flags |= map->flag;
+                break;
+            }
+        }
+
+        p += 4;
+    }
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
+/*
  * $Id: util_token.c 23457 2009-12-08 00:04:48Z tlyu $
  */
 
@@ -305,12 +448,8 @@ der_read_length(unsigned char **buf, ssize_t *bufsize)
 /* returns the length of a token, given the mech oid and the body size */
 
 size_t
-tokenSize(const gss_OID_desc *mech, size_t body_size)
+tokenSize(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;
 }
 
@@ -319,20 +458,11 @@ tokenSize(const gss_OID_desc *mech, size_t body_size)
 
 void
 makeTokenHeader(
-    const gss_OID_desc *mech,
     size_t body_size,
-    unsigned char **buf,
-    enum gss_eap_token_type tok_type)
+    unsigned char **buf)
 {
     *(*buf)++ = 0x60;
-    der_write_length(buf, (tok_type == -1) ?2:4 + mech->length + body_size);
-    *(*buf)++ = 0x06;
-    *(*buf)++ = (unsigned char)mech->length;
-    memcpy(*buf, mech->elements, mech->length);
-    *buf += mech->length;
-    assert(tok_type != TOK_TYPE_NONE);
-    *(*buf)++ = (unsigned char)((tok_type>>8) & 0xff);
-    *(*buf)++ = (unsigned char)(tok_type & 0xff);
+    der_write_length(buf, body_size);
 }
 
 /*