Implement gssEapDeriveRFC3961Key
authorLuke Howard <lukeh@padl.com>
Wed, 8 Sep 2010 13:50:43 +0000 (15:50 +0200)
committerLuke Howard <lukeh@padl.com>
Wed, 8 Sep 2010 13:50:43 +0000 (15:50 +0200)
Makefile.am
compare_name.c
gssapiP_eap.h
pseudo_random.c
unwrap_iov.c
util.h
util_context.c
util_crypt.c
util_krb.c [new file with mode: 0644]
util_name.c
wrap_iov.c

index 37aa441..b24dd6b 100644 (file)
@@ -55,6 +55,7 @@ libmech_eap_la_SOURCES =                      \
        util_cksum.c                            \
        util_cred.c                             \
        util_crypt.c                            \
+       util_krb.c                              \
        util_mech.c                             \
        util_name.c                             \
        util_oid.c                              \
index 6c4af98..0c557f2 100644 (file)
@@ -38,5 +38,8 @@ gss_compare_name(OM_uint32 *minor,
                  gss_name_t name2,
                  int *name_equal)
 {
-    GSSEAP_NOT_IMPLEMENTED;
+    OM_uint32 major;
+    krb5_context context;
+
+    GSSEAP_KRB_INIT(&context);
 }
index bc87bc5..6ab69df 100644 (file)
@@ -117,11 +117,10 @@ struct gss_ctx_id_struct {
     enum eap_gss_state state;
     OM_uint32 flags;
     OM_uint32 gssFlags;
-    krb5_context kerberosCtx;
     gss_OID mechanismUsed;
     krb5_enctype encryptionType;
     krb5_cksumtype checksumType;
-    krb5_keyblock *rfc3961Key;
+    krb5_keyblock rfc3961Key;
     gss_name_t initiatorName;
     gss_name_t acceptorName;
     time_t expiryTime;
@@ -139,10 +138,10 @@ struct gss_ctx_id_struct {
 #define TOK_FLAG_WRAP_CONFIDENTIAL          0x02
 #define TOK_FLAG_ACCEPTOR_SUBKEY            0x04
 
-#define KEY_USAGE_ACCEPTOR_SEAL             512
-#define KEY_USAGE_ACCEPTOR_SIGN             513
-#define KEY_USAGE_INITIATOR_SEAL            514
-#define KEY_USAGE_INITIATOR_SIGN            515
+#define KEY_USAGE_ACCEPTOR_SEAL             22
+#define KEY_USAGE_ACCEPTOR_SIGN             23
+#define KEY_USAGE_INITIATOR_SEAL            24
+#define KEY_USAGE_INITIATOR_SIGN            25
 
 /* wrap_iov.c */
 OM_uint32
index 723a27b..e054693 100644 (file)
@@ -73,6 +73,7 @@ gss_pseudo_random(OM_uint32 *minor,
     size_t prflen;
     krb5_data t, ns;
     unsigned char *p;
+    krb5_context krbContext;
 
     prf_out->length = 0;
     prf_out->value = NULL;
@@ -80,6 +81,8 @@ gss_pseudo_random(OM_uint32 *minor,
     if (!CTX_IS_ESTABLISHED(ctx))
         return GSS_S_NO_CONTEXT;
 
+    GSSEAP_KRB_INIT(&krbContext);
+
     t.length = 0;
     t.data = NULL;
 
@@ -99,7 +102,7 @@ gss_pseudo_random(OM_uint32 *minor,
     }
     prf_out->length = desired_output_len;
 
-    code = krb5_c_prf_length(ctx->kerberosCtx,
+    code = krb5_c_prf_length(krbContext,
                              ctx->encryptionType,
                              &prflen);
     if (code != 0)
@@ -125,7 +128,7 @@ gss_pseudo_random(OM_uint32 *minor,
     while (desired_output_len > 0) {
         store_uint32_be(i, ns.data);
 
-        code = krb5_c_prf(ctx->kerberosCtx, ctx->rfc3961Key, &ns, &t);
+        code = krb5_c_prf(krbContext, &ctx->rfc3961Key, &ns, &t);
         if (code != 0)
             goto cleanup;
 
@@ -139,8 +142,8 @@ gss_pseudo_random(OM_uint32 *minor,
 cleanup:
     if (code != 0)
         gss_release_buffer(&tmpMinor, prf_out);
-    krb5_free_data_contents(ctx->kerberosCtx, &ns);
-    krb5_free_data_contents(ctx->kerberosCtx, &t);
+    krb5_free_data_contents(krbContext, &ns);
+    krb5_free_data_contents(krbContext, &t);
 
     *minor = code;
     return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
index 05ac8d9..4061bee 100644 (file)
@@ -82,6 +82,9 @@ unwrapToken(OM_uint32 *minor,
     int valid = 0;
     krb5_cksumtype cksumtype;
     int conf_flag = 0;
+    krb5_context krbContext;
+
+    GSSEAP_KRB_INIT(&krbContext);
 
     *minor = 0;
 
@@ -135,7 +138,7 @@ unwrapToken(OM_uint32 *minor,
         rrc = load_uint16_be(ptr + 6);
         seqnum = load_uint64_be(ptr + 8);
 
-        code = krb5_c_crypto_length(ctx->kerberosCtx,
+        code = krb5_c_crypto_length(krbContext,
                                     ctx->encryptionType,
                                     conf_flag ? KRB5_CRYPTO_TYPE_TRAILER :
                                     KRB5_CRYPTO_TYPE_CHECKSUM,
@@ -167,9 +170,9 @@ unwrapToken(OM_uint32 *minor,
             unsigned char *althdr;
 
             /* Decrypt */
-            code = gssEapDecrypt(ctx->kerberosCtx,
+            code = gssEapDecrypt(krbContext,
                                  ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0),
-                                 ec, rrc, ctx->rfc3961Key,
+                                 ec, rrc, &ctx->rfc3961Key,
                                  keyUsage, 0, iov, iov_count);
             if (code != 0) {
                 *minor = code;
@@ -198,8 +201,8 @@ unwrapToken(OM_uint32 *minor,
             store_uint16_be(0, ptr + 4);
             store_uint16_be(0, ptr + 6);
 
-            code = gssEapVerify(ctx->kerberosCtx, cksumtype, rrc,
-                                ctx->rfc3961Key, keyUsage,
+            code = gssEapVerify(krbContext, cksumtype, rrc,
+                                &ctx->rfc3961Key, keyUsage,
                                 iov, iov_count, &valid);
             if (code != 0 || valid == FALSE) {
                 *minor = code;
@@ -217,8 +220,8 @@ unwrapToken(OM_uint32 *minor,
             goto defective;
         seqnum = load_uint64_be(ptr + 8);
 
-        code = gssEapVerify(ctx->kerberosCtx, cksumtype, 0,
-                            ctx->rfc3961Key, keyUsage,
+        code = gssEapVerify(krbContext, cksumtype, 0,
+                            &ctx->rfc3961Key, keyUsage,
                             iov, iov_count, &valid);
         if (code != 0 || valid == FALSE) {
             *minor = code;
@@ -284,13 +287,15 @@ unwrapStream(OM_uint32 *minor,
 {
     unsigned char *ptr;
     OM_uint32 code = 0, major = GSS_S_FAILURE;
-    krb5_context context = ctx->kerberosCtx;
+    krb5_context krbContext;
     int conf_req_flag, toktype2;
     int i = 0, j;
     gss_iov_buffer_desc *tiov = NULL;
     gss_iov_buffer_t stream, data = NULL;
     gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer;
 
+    GSSEAP_KRB_INIT(&krbContext);
+
     assert(toktype == TOK_TYPE_WRAP);
 
     if (toktype != TOK_TYPE_WRAP || (ctx->gssFlags & GSS_C_DCE_STYLE)) {
@@ -375,7 +380,7 @@ unwrapStream(OM_uint32 *minor,
         }
 
         if (conf_req_flag) {
-            code = krb5_c_crypto_length(context, ctx->encryptionType,
+            code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
                                         KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen);
             if (code != 0)
                 goto cleanup;
@@ -383,7 +388,7 @@ unwrapStream(OM_uint32 *minor,
         }
 
         /* no PADDING for CFX, EC is used instead */
-        code = krb5_c_crypto_length(context, ctx->encryptionType,
+        code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
                                     conf_req_flag
                                       ? KRB5_CRYPTO_TYPE_TRAILER
                                       : KRB5_CRYPTO_TYPE_CHECKSUM,
diff --git a/util.h b/util.h
index 972f721..5a276f4 100644 (file)
--- a/util.h
+++ b/util.h
@@ -122,6 +122,24 @@ gssEapIsIntegrityOnly(gss_iov_buffer_desc *iov, int iov_count);
 int
 gssEapAllocIov(gss_iov_buffer_t iov, size_t size);
 
+OM_uint32
+gssEapDeriveRFC3961Key(OM_uint32 *minor,
+                       gss_buffer_t msk,
+                       krb5_enctype enctype,
+                       krb5_keyblock *pKey);
+
+/* util_krb.c */
+OM_uint32
+gssEapKerberosInit(OM_uint32 *minor, krb5_context *context);
+
+#define GSSEAP_KRB_INIT(ctx) do {                   \
+        OM_uint32 tmpMajor;                         \
+        tmpMajor  = gssEapKerberosInit(minor, ctx); \
+        if (GSS_ERROR(tmpMajor)) {                  \
+            return tmpMajor;                        \
+        }                                           \
+    } while (0)
+
 /* util_mech.c */
 void
 gssEapInternalizeOid(const gss_OID oid,
@@ -236,11 +254,22 @@ verifyTokenHeader(const gss_OID_desc * mech,
 #include <pthread.h>
 
 #define GSSEAP_MUTEX                    pthread_mutex_t
+#define GSSEAP_MUTEX_INITIALIZER        PTHREAD_MUTEX_INITIALIZER
+
 #define GSSEAP_MUTEX_INIT(m)            pthread_mutex_init((m), NULL)
 #define GSSEAP_MUTEX_DESTROY(m)         pthread_mutex_destroy((m))
 #define GSSEAP_MUTEX_LOCK(m)            pthread_mutex_lock((m))
 #define GSSEAP_MUTEX_UNLOCK(m)          pthread_mutex_unlock((m))
 
+#define GSSEAP_THREAD_KEY               pthread_key_t
+#define GSSEAP_KEY_CREATE(k, d)         pthread_key_create((k), (d))
+#define GSSEAP_GETSPECIFIC(k)           pthread_getspecific((k))
+#define GSSEAP_SETSPECIFIC(k, d)        pthread_setspecific((k), (d))
+
+#define GSSEAP_THREAD_ONCE              pthread_once_t
+#define GSSEAP_ONCE(o, i)               pthread_once((o), (i))
+#define GSSEAP_ONCE_INITIALIZER         PTHREAD_ONCE_INIT
+
 /* Helper functions */
 static inline void
 store_uint16_be(uint16_t val, void *vp)
index fab4313..b6dec71 100644 (file)
@@ -53,12 +53,6 @@ gssEapAllocContext(OM_uint32 *minor,
         return GSS_S_FAILURE;
     }
 
-    *minor = krb5_init_context(&ctx->kerberosCtx);
-    if (*minor != 0) {
-        gssEapReleaseContext(&tmpMinor, &ctx);
-        return GSS_S_FAILURE;
-    }
-
     *pCtx = ctx;
 
     return GSS_S_COMPLETE;
@@ -82,25 +76,21 @@ gssEapReleaseContext(OM_uint32 *minor,
 {
     OM_uint32 major, tmpMinor;
     gss_ctx_id_t ctx = *pCtx;
+    krb5_context krbContext = NULL;
 
     if (ctx == GSS_C_NO_CONTEXT) {
         return GSS_S_COMPLETE;
     }
 
+    gssEapKerberosInit(&tmpMinor, &krbContext);
+
     if (CTX_IS_INITIATOR(ctx)) {
         releaseInitiatorContext(&ctx->initiatorCtx);
     } else {
         releaseAcceptorContext(&ctx->acceptorCtx);
     }
 
-    if (ctx->rfc3961Key != NULL) {
-        krb5_free_keyblock(ctx->kerberosCtx, ctx->rfc3961Key);
-    }
-
-    if (ctx->kerberosCtx != NULL) {
-        krb5_free_context(ctx->kerberosCtx);
-    }
-
+    krb5_free_keyblock_contents(krbContext, &ctx->rfc3961Key);
     gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
     gssEapReleaseName(&tmpMinor, &ctx->acceptorName);
     gss_release_oid(&tmpMinor, &ctx->mechanismUsed);
index e4d5d0d..00a3cb1 100644 (file)
@@ -394,3 +394,79 @@ gssEapAllocIov(gss_iov_buffer_t iov, size_t size)
 
     return 0;
 }
+
+static char
+keyDerivationConstant[] = "rfc4121-gss-eap";
+
+OM_uint32
+gssEapDeriveRFC3961Key(OM_uint32 *minor,
+                       gss_buffer_t msk,
+                       krb5_enctype enctype,
+                       krb5_keyblock *pKey)
+{
+    krb5_context context;
+    krb5_data data, prf;
+    krb5_keyblock kd;
+    krb5_error_code code;
+    size_t keybytes, keylength, prflength;
+
+    memset(pKey, 0, sizeof(*pKey));
+
+    GSSEAP_KRB_INIT(&context);
+
+    kd.contents = NULL;
+    prf.data = NULL;
+    KRB_KEYTYPE(&kd) = enctype;
+
+    code = krb5_c_keylengths(context, enctype, &keybytes, &keylength);
+    if (code != 0)
+        goto cleanup;
+
+    data.length = msk->length;
+    data.data = (char *)msk->value;
+
+    kd.contents = GSSEAP_MALLOC(keylength);
+    if (kd.contents == NULL) {
+        code = ENOMEM;
+        goto cleanup;
+    }
+    kd.length = keylength;
+
+    code = krb5_c_random_to_key(context, enctype, &data, &kd);
+    if (code != 0)
+        goto cleanup;
+
+    data.length = sizeof(keyDerivationConstant) - 1;
+    data.data = keyDerivationConstant;
+
+    code = krb5_c_prf_length(context, enctype, &prflength);
+    if (code != 0)
+        goto cleanup;
+
+    prf.length = prflength;
+    prf.data = GSSEAP_MALLOC(prflength);
+    if (data.data == NULL) {
+        code = ENOMEM;
+        goto cleanup;
+    }
+
+    code = krb5_c_prf(context, &kd, &data, &prf);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_c_random_to_key(context, enctype, &prf, &kd);
+    if (code != 0)
+        goto cleanup;
+
+    *pKey = kd;
+
+cleanup:
+    if (code != 0) {
+        GSSEAP_FREE(kd.contents);
+    }
+
+    GSSEAP_FREE(prf.data);
+
+    *minor = code;
+    return (*minor == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
+}
diff --git a/util_krb.c b/util_krb.c
new file mode 100644 (file)
index 0000000..2aa23ae
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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 GSSEAP_THREAD_ONCE krbContextKeyOnce = GSSEAP_ONCE_INITIALIZER;
+static GSSEAP_THREAD_KEY krbContextKey;
+
+static void
+destroyKrbContext(void *arg)
+{
+    krb5_context context = (krb5_context)arg;
+
+    if (context != NULL)
+        krb5_free_context(context);
+}
+
+static void
+createKrbContextKey(void)
+{
+    GSSEAP_KEY_CREATE(&krbContextKey, destroyKrbContext);
+}
+
+OM_uint32
+gssEapKerberosInit(OM_uint32 *minor, krb5_context *context)
+{
+    *minor = 0;
+
+    GSSEAP_ONCE(&krbContextKeyOnce, createKrbContextKey);
+
+    *context = GSSEAP_GETSPECIFIC(krbContextKey);
+    if (*context == NULL) {
+        *minor = krb5_init_context(context);
+        if (*minor == 0) {
+            if (GSSEAP_SETSPECIFIC(krbContextKey, *context) != 0) {
+                *minor = errno;
+                krb5_free_context(*context);
+                *context = NULL;
+            }
+        }
+    }
+
+    return *minor == 0 ? GSS_S_COMPLETE : GSS_S_FAILURE;
+}
index b5c7c72..97829d5 100644 (file)
@@ -86,16 +86,3 @@ gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName)
     return GSS_S_COMPLETE;
 }
 
-OM_uint32
-gssEapDuplicateName(krb5_context context,
-                    const gss_name_t src,
-                    gss_name_t *dst)
-{
-}
-
-krb5_boolean
-gssEapCompareName(krb5_context context,
-                  gss_name_t name1,
-                  gss_name_t name2)
-{
-}
index cd17e06..754eb0f 100644 (file)
@@ -75,10 +75,13 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
     size_t rrc = 0;
     unsigned int gssHeaderLen, gssTrailerLen;
     size_t dataLen, assocDataLen;
+    krb5_context krbContext;
 
     if (!CTX_IS_ESTABLISHED(ctx))
         return GSS_S_NO_CONTEXT;
 
+    GSSEAP_KRB_INIT(&krbContext);
+
     acceptorFlag = CTX_IS_INITIATOR(ctx) ? 0 : TOK_FLAG_SENDER_IS_ACCEPTOR;
     keyUsage = ((toktype == TOK_TYPE_WRAP)
                 ? (CTX_IS_INITIATOR(ctx)
@@ -107,12 +110,12 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
         size_t ec = 0;
         size_t confDataLen = dataLen - assocDataLen;
 
-        code = krb5_c_crypto_length(ctx->kerberosCtx, ctx->encryptionType,
+        code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
                                     KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen);
         if (code != 0)
             goto cleanup;
 
-        code = krb5_c_padding_length(ctx->kerberosCtx, ctx->encryptionType,
+        code = krb5_c_padding_length(krbContext, ctx->encryptionType,
                                      confDataLen + 16 /* E(Header) */,
                                      &krbPadLen);
         if (code != 0)
@@ -120,13 +123,13 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
 
         if (krbPadLen == 0 && (ctx->gssFlags & GSS_C_DCE_STYLE)) {
             /* Windows rejects AEAD tokens with non-zero EC */
-            code = krb5_c_block_size(ctx->kerberosCtx, ctx->encryptionType, &ec);
+            code = krb5_c_block_size(krbContext, ctx->encryptionType, &ec);
             if (code != 0)
                 goto cleanup;
         } else
             ec = krbPadLen;
 
-        code = krb5_c_crypto_length(ctx->kerberosCtx, ctx->encryptionType,
+        code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
                                     KRB5_CRYPTO_TYPE_TRAILER, &krbTrailerLen);
         if (code != 0)
             goto cleanup;
@@ -187,9 +190,9 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
         memset(tbuf, 0xFF, ec);
         memcpy(tbuf + ec, header->buffer.value, 16);
 
-        code = gssEapEncrypt(ctx->kerberosCtx,
+        code = gssEapEncrypt(krbContext,
                              ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0),
-                             ec, rrc, ctx->rfc3961Key,
+                             ec, rrc, &ctx->rfc3961Key,
                              keyUsage, 0, iov, iov_count);
         if (code != 0)
             goto cleanup;
@@ -203,7 +206,7 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
 
         gssHeaderLen = 16;
 
-        code = krb5_c_crypto_length(ctx->kerberosCtx, ctx->encryptionType,
+        code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
                                     KRB5_CRYPTO_TYPE_CHECKSUM,
                                     &gssTrailerLen);
         if (code != 0)
@@ -257,8 +260,8 @@ gssEapWrapOrGetMIC(OM_uint32 *minor,
         }
         store_64_be(ctx->sendSeq, outbuf + 8);
 
-        code = gssEapSign(ctx->kerberosCtx, ctx->checksumType,
-                          rrc, ctx->rfc3961Key, keyUsage,
+        code = gssEapSign(krbContext, ctx->checksumType,
+                          rrc, &ctx->rfc3961Key, keyUsage,
                           iov, iov_count);
         if (code != 0)
             goto cleanup;