From: Luke Howard Date: Wed, 8 Sep 2010 13:50:43 +0000 (+0200) Subject: Implement gssEapDeriveRFC3961Key X-Git-Tag: vm/20110310~387 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mech_eap.git;a=commitdiff_plain;h=200399d6cd2725e455c2079548ea99cb0e10eda5 Implement gssEapDeriveRFC3961Key --- diff --git a/Makefile.am b/Makefile.am index 37aa441..b24dd6b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/compare_name.c b/compare_name.c index 6c4af98..0c557f2 100644 --- a/compare_name.c +++ b/compare_name.c @@ -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); } diff --git a/gssapiP_eap.h b/gssapiP_eap.h index bc87bc5..6ab69df 100644 --- a/gssapiP_eap.h +++ b/gssapiP_eap.h @@ -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 diff --git a/pseudo_random.c b/pseudo_random.c index 723a27b..e054693 100644 --- a/pseudo_random.c +++ b/pseudo_random.c @@ -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; diff --git a/unwrap_iov.c b/unwrap_iov.c index 05ac8d9..4061bee 100644 --- a/unwrap_iov.c +++ b/unwrap_iov.c @@ -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 --- 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 #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) diff --git a/util_context.c b/util_context.c index fab4313..b6dec71 100644 --- a/util_context.c +++ b/util_context.c @@ -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); diff --git a/util_crypt.c b/util_crypt.c index e4d5d0d..00a3cb1 100644 --- a/util_crypt.c +++ b/util_crypt.c @@ -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 index 0000000..2aa23ae --- /dev/null +++ b/util_krb.c @@ -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; +} diff --git a/util_name.c b/util_name.c index b5c7c72..97829d5 100644 --- a/util_name.c +++ b/util_name.c @@ -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) -{ -} diff --git a/wrap_iov.c b/wrap_iov.c index cd17e06..754eb0f 100644 --- a/wrap_iov.c +++ b/wrap_iov.c @@ -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;