From: Luke Howard Date: Sun, 2 Jan 2011 08:57:19 +0000 (+1100) Subject: Merge branch 'master' of ssh://moonshot.suchdamage.org:822/srv/git/moonshot X-Git-Tag: vm/20110310~72 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=moonshot.git;a=commitdiff_plain;h=a4a91192e39832457581c00a484ae567b2f7e30f Merge branch 'master' of ssh://moonshot.suchdamage.org:822/srv/git/moonshot Reauth fixes Conflicts: shibboleth/opensaml2 shibboleth/sp --- a4a91192e39832457581c00a484ae567b2f7e30f diff --cc cyrus-sasl index 2d5a000,2d5a000..755d9d2 --- a/cyrus-sasl +++ b/cyrus-sasl @@@ -1,1 -1,1 +1,1 @@@ --Subproject commit 2d5a0004454cee27bd0e4842404fd5d0f3e23ef2 ++Subproject commit 755d9d20aadc1a140d01583b586f5a6d29c3a670 diff --cc libradsec index 88adb3a,88adb3a..b07f9ad --- a/libradsec +++ b/libradsec @@@ -1,1 -1,1 +1,1 @@@ --Subproject commit 88adb3a99af7ac04c5f07174acdd54fbf13fee01 ++Subproject commit b07f9ade9bad2ec38d4dc9f6592f64dff2e1b120 diff --cc mech_eap/release_oid.c index 947baa2,0000000..184dbab mode 100644,000000..100644 --- a/mech_eap/release_oid.c +++ b/mech_eap/release_oid.c @@@ -1,55 -1,0 +1,44 @@@ +/* + * 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. + */ + +/* + * Mark an internalized OID as not required to be released. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_internal_release_oid(OM_uint32 *minor, + gss_OID *oid) +{ - gss_OID internalizedOid = GSS_C_NO_OID; - - *minor = 0; - - if (gssEapInternalizeOid(*oid, &internalizedOid)) { - /* OID was internalized, so we can mark it as "freed" */ - *oid = GSS_C_NO_OID; - return GSS_S_COMPLETE; - } - - /* we don't know about this OID */ - return GSS_S_CONTINUE_NEEDED; ++ return gssEapReleaseOid(minor, oid); +} diff --cc mech_eap/util.h index 7f2e7fe,0000000..49c7c91 mode 100644,000000..100644 --- a/mech_eap/util.h +++ b/mech_eap/util.h @@@ -1,772 -1,0 +1,775 @@@ +/* + * 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. + */ +/* + * Portions Copyright 2003-2010 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +/* + * Utility functions. + */ + +#ifndef _UTIL_H_ +#define _UTIL_H_ 1 + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MIN +#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#endif + +/* util_buffer.c */ +OM_uint32 +makeStringBuffer(OM_uint32 *minor, + const char *string, + gss_buffer_t buffer); + +OM_uint32 +bufferToString(OM_uint32 *minor, + const gss_buffer_t buffer, + char **pString); + +OM_uint32 +duplicateBuffer(OM_uint32 *minor, + const gss_buffer_t src, + gss_buffer_t dst); + +static inline int +bufferEqual(const gss_buffer_t b1, const gss_buffer_t b2) +{ + return (b1->length == b2->length && + memcmp(b1->value, b2->value, b2->length) == 0); +} + +static inline int +bufferEqualString(const gss_buffer_t b1, const char *s) +{ + gss_buffer_desc b2; + + b2.length = strlen(s); + b2.value = (char *)s; + + return bufferEqual(b1, &b2); +} + +/* util_cksum.c */ +int +gssEapSign(krb5_context context, + krb5_cksumtype type, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *key, +#endif + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count); + +int +gssEapVerify(krb5_context context, + krb5_cksumtype type, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *key, +#endif + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int *valid); + +#if 0 +OM_uint32 +gssEapEncodeGssChannelBindings(OM_uint32 *minor, + gss_channel_bindings_t chanBindings, + gss_buffer_t encodedBindings); +#endif + +/* util_context.c */ +#define EAP_EXPORT_CONTEXT_V1 1 + +enum gss_eap_token_type { + TOK_TYPE_NONE = 0x0000, /* no token */ + TOK_TYPE_MIC = 0x0404, /* RFC 4121 MIC token */ + TOK_TYPE_WRAP = 0x0504, /* RFC 4121 wrap token */ + TOK_TYPE_EXPORT_NAME = 0x0401, /* RFC 2743 exported name */ + TOK_TYPE_EXPORT_NAME_COMPOSITE = 0x0402, /* exported composite name */ + TOK_TYPE_DELETE_CONTEXT = 0x0405, /* RFC 2743 delete context */ + TOK_TYPE_EAP_RESP = 0x0601, /* EAP response */ + TOK_TYPE_EAP_REQ = 0x0602, /* EAP request */ + TOK_TYPE_EXT_REQ = 0x0603, /* GSS EAP extensions request */ + TOK_TYPE_EXT_RESP = 0x0604, /* GSS EAP extensions response */ + TOK_TYPE_GSS_REAUTH = 0x0605, /* GSS EAP fast reauthentication token */ + TOK_TYPE_CONTEXT_ERR = 0x0606, /* context error */ +}; + +OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx); +OM_uint32 gssEapReleaseContext(OM_uint32 *minor, gss_ctx_id_t *pCtx); + +OM_uint32 +gssEapMakeToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t innerToken, + enum gss_eap_token_type tokenType, + gss_buffer_t outputToken); + +OM_uint32 +gssEapVerifyToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t inputToken, + enum gss_eap_token_type *tokenType, + gss_buffer_t innerInputToken); + +OM_uint32 +gssEapContextTime(OM_uint32 *minor, + gss_ctx_id_t context_handle, + OM_uint32 *time_rec); + +OM_uint32 +gssEapDisplayName(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t output_name_buffer, + gss_OID *output_name_type); + +/* util_cred.c */ +OM_uint32 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred); +OM_uint32 gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred); + +OM_uint32 +gssEapAcquireCred(OM_uint32 *minor, + const gss_name_t desiredName, + const gss_buffer_t password, + OM_uint32 timeReq, + const gss_OID_set desiredMechs, + int cred_usage, + gss_cred_id_t *pCred, + gss_OID_set *pActualMechs, + OM_uint32 *timeRec); + +int gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech); + +/* util_crypt.c */ +int +gssEapEncrypt(krb5_context context, int dce_style, size_t ec, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *key, +#endif + int usage, + gss_iov_buffer_desc *iov, int iov_count); + +int +gssEapDecrypt(krb5_context context, int dce_style, size_t ec, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *key, +#endif + int usage, + gss_iov_buffer_desc *iov, int iov_count); + +int +gssEapMapCryptoFlag(OM_uint32 type); + +gss_iov_buffer_t +gssEapLocateIov(gss_iov_buffer_desc *iov, + int iov_count, + OM_uint32 type); + +void +gssEapIovMessageLength(gss_iov_buffer_desc *iov, + int iov_count, + size_t *data_length, + size_t *assoc_data_length); + +void +gssEapReleaseIov(gss_iov_buffer_desc *iov, int iov_count); + +int +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, + const unsigned char *key, + size_t keyLength, + krb5_enctype enctype, + krb5_keyblock *pKey); + +/* util_exts.c */ +#define EXT_FLAG_CRITICAL 0x80000000 /* critical, wire flag */ +#define EXT_FLAG_VERIFIED 0x40000000 /* verified, API flag */ + +#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; /* client */ + int required; /* server */ + OM_uint32 (*make)(OM_uint32 *, + gss_cred_id_t, + gss_ctx_id_t, + gss_channel_bindings_t, + gss_buffer_t); + OM_uint32 (*verify)(OM_uint32 *, + gss_cred_id_t, + gss_ctx_id_t, + gss_channel_bindings_t, + const gss_buffer_t); +}; + +OM_uint32 +gssEapMakeExtensions(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + 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, + gss_channel_bindings_t chanBindings, + const gss_buffer_t buffer); + +/* util_krb.c */ +#ifdef HAVE_HEIMDAL_VERSION + +#define KRB_TIME_FOREVER ((time_t)~0L) + +#define KRB_KEY_TYPE(key) ((key)->keytype) +#define KRB_KEY_DATA(key) ((key)->keyvalue.data) +#define KRB_KEY_LENGTH(key) ((key)->keyvalue.length) + +#define KRB_PRINC_LENGTH(princ) ((princ)->name.name_string.len) +#define KRB_PRINC_TYPE(princ) ((princ)->name.name_type) +#define KRB_PRINC_NAME(princ) ((princ)->name.name_string.val) +#define KRB_PRINC_REALM(princ) ((princ)->realm) + +#define KRB_KT_ENT_KEYBLOCK(e) (&(e)->keyblock) +#define KRB_KT_ENT_FREE(c, e) krb5_kt_free_entry((c), (e)) + +#define KRB_CRYPTO_CONTEXT(ctx) (krbCrypto) + +#else + +#define KRB_TIME_FOREVER KRB5_INT32_MAX + +#define KRB_KEY_TYPE(key) ((key)->enctype) +#define KRB_KEY_DATA(key) ((key)->contents) +#define KRB_KEY_LENGTH(key) ((key)->length) + +#define KRB_PRINC_LENGTH(princ) (krb5_princ_size(NULL, (princ))) +#define KRB_PRINC_TYPE(princ) (krb5_princ_type(NULL, (princ))) +#define KRB_PRINC_NAME(princ) (krb5_princ_name(NULL, (princ))) +#define KRB_PRINC_REALM(princ) (krb5_princ_realm(NULL, (princ))) + +#define KRB_KT_ENT_KEYBLOCK(e) (&(e)->key) +#define KRB_KT_ENT_FREE(c, e) krb5_free_keytab_entry_contents((c), (e)) + +#define KRB_CRYPTO_CONTEXT(ctx) (&(ctx)->rfc3961Key) + +#endif /* HAVE_HEIMDAL_VERSION */ + +#define KRB_KEY_INIT(key) do { \ + KRB_KEY_TYPE(key) = ENCTYPE_NULL; \ + KRB_KEY_DATA(key) = NULL; \ + KRB_KEY_LENGTH(key) = 0; \ + } while (0) + +#ifdef HAVE_HEIMDAL_VERSION +#define GSS_IOV_BUFFER_FLAG_ALLOCATE GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE +#define GSS_IOV_BUFFER_FLAG_ALLOCATED GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATED + +#define GSS_S_CRED_UNAVAIL GSS_S_FAILURE +#endif + +#define GSSEAP_KRB_INIT(ctx) do { \ + OM_uint32 tmpMajor; \ + \ + tmpMajor = gssEapKerberosInit(minor, ctx); \ + if (GSS_ERROR(tmpMajor)) { \ + return tmpMajor; \ + } \ + } while (0) + +OM_uint32 +gssEapKerberosInit(OM_uint32 *minor, krb5_context *context); + +OM_uint32 +rfc3961ChecksumTypeForKey(OM_uint32 *minor, + krb5_keyblock *key, + krb5_cksumtype *cksumtype); + +krb5_const_principal +krbAnonymousPrincipal(void); + +krb5_error_code +krbCryptoLength(krb5_context krbContext, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *key, +#endif + int type, + size_t *length); + +krb5_error_code +krbPaddingLength(krb5_context krbContext, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *key, +#endif + size_t dataLength, + size_t *padLength); + +krb5_error_code +krbBlockSize(krb5_context krbContext, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *key, +#endif + size_t *blockSize); + +krb5_error_code +krbEnctypeToString(krb5_context krbContext, + krb5_enctype enctype, + const char *prefix, + gss_buffer_t string); + +krb5_error_code +krbMakeAuthDataKdcIssued(krb5_context context, + const krb5_keyblock *key, + krb5_const_principal issuer, +#ifdef HAVE_HEIMDAL_VERSION + const AuthorizationData *authdata, + AuthorizationData *adKdcIssued +#else + krb5_authdata *const *authdata, + krb5_authdata ***adKdcIssued +#endif + ); + +krb5_error_code +krbMakeCred(krb5_context context, + krb5_auth_context authcontext, + krb5_creds *creds, + krb5_data *data); + +/* util_lucid.c */ +OM_uint32 +gssEapExportLucidSecContext(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_OID desiredObject, + gss_buffer_set_t *data_set); + +/* util_mech.c */ +extern gss_OID GSS_EAP_MECHANISM; + +int +gssEapInternalizeOid(const gss_OID oid, + gss_OID *const pInternalizedOid); + +OM_uint32 ++gssEapReleaseOid(OM_uint32 *minor, gss_OID *oid); ++ ++OM_uint32 +gssEapDefaultMech(OM_uint32 *minor, + gss_OID *oid); + +OM_uint32 +gssEapIndicateMechs(OM_uint32 *minor, + gss_OID_set *mechs); + +OM_uint32 +gssEapEnctypeToOid(OM_uint32 *minor, + krb5_enctype enctype, + gss_OID *pOid); + +OM_uint32 +gssEapOidToEnctype(OM_uint32 *minor, + const gss_OID oid, + krb5_enctype *enctype); + +int +gssEapIsMechanismOid(const gss_OID oid); + +int +gssEapIsConcreteMechanismOid(const gss_OID oid); + +OM_uint32 +gssEapValidateMechs(OM_uint32 *minor, + const gss_OID_set mechs); + +gss_buffer_t +gssEapOidToSaslName(const gss_OID oid); + +gss_OID +gssEapSaslNameToOid(const gss_buffer_t name); + +/* util_name.c */ +#define EXPORT_NAME_FLAG_OID 0x1 +#define EXPORT_NAME_FLAG_COMPOSITE 0x2 + +OM_uint32 gssEapAllocName(OM_uint32 *minor, gss_name_t *pName); +OM_uint32 gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName); +OM_uint32 gssEapExportName(OM_uint32 *minor, + const gss_name_t name, + gss_buffer_t exportedName); +OM_uint32 gssEapExportNameInternal(OM_uint32 *minor, + const gss_name_t name, + gss_buffer_t exportedName, + unsigned int flags); +OM_uint32 gssEapImportName(OM_uint32 *minor, + const gss_buffer_t input_name_buffer, + gss_OID input_name_type, + gss_name_t *output_name); +OM_uint32 gssEapImportNameInternal(OM_uint32 *minor, + const gss_buffer_t input_name_buffer, + gss_name_t *output_name, + unsigned int flags); +OM_uint32 +gssEapDuplicateName(OM_uint32 *minor, + const gss_name_t input_name, + gss_name_t *dest_name); + +/* util_oid.c */ +OM_uint32 +composeOid(OM_uint32 *minor_status, + const char *prefix, + size_t prefix_len, + int suffix, + gss_OID_desc *oid); + +OM_uint32 +decomposeOid(OM_uint32 *minor_status, + const char *prefix, + size_t prefix_len, + gss_OID_desc *oid, + int *suffix) ; + +OM_uint32 +duplicateOid(OM_uint32 *minor_status, + const gss_OID_desc * const oid, + gss_OID *new_oid); + +OM_uint32 +duplicateOidSet(OM_uint32 *minor, + const gss_OID_set src, + gss_OID_set *dst); + +static inline int +oidEqual(const gss_OID_desc *o1, const gss_OID_desc *o2) +{ + if (o1 == GSS_C_NO_OID) + return (o2 == GSS_C_NO_OID); + else if (o2 == GSS_C_NO_OID) + return (o1 == GSS_C_NO_OID); + else + return (o1->length == o2->length && + memcmp(o1->elements, o2->elements, o1->length) == 0); +} + +/* util_ordering.c */ +OM_uint32 +sequenceInternalize(OM_uint32 *minor, + void **vqueue, + unsigned char **buf, + size_t *lenremain); + +OM_uint32 +sequenceExternalize(OM_uint32 *minor, + void *vqueue, + unsigned char **buf, + size_t *lenremain); + +size_t +sequenceSize(void *vqueue); + +OM_uint32 +sequenceFree(OM_uint32 *minor, void **vqueue); + +OM_uint32 +sequenceCheck(OM_uint32 *minor, void **vqueue, uint64_t seqnum); + +OM_uint32 +sequenceInit(OM_uint32 *minor, void **vqueue, uint64_t seqnum, + int do_replay, int do_sequence, int wide_nums); + +/* util_token.c */ +size_t +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); + +OM_uint32 +verifyTokenHeader(OM_uint32 *minor, + gss_OID mech, + size_t *body_size, + unsigned char **buf_in, + size_t toksize_in, + enum gss_eap_token_type *ret_tok_type); + +/* Helper macros */ + +#define GSSEAP_CALLOC calloc +#define GSSEAP_MALLOC malloc +#define GSSEAP_FREE free +#define GSSEAP_REALLOC realloc + +#define GSSEAP_NOT_IMPLEMENTED do { \ + assert(0 && "not implemented"); \ + *minor = ENOSYS; \ + return GSS_S_FAILURE; \ + } while (0) + +#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) +{ + unsigned char *p = (unsigned char *)vp; + + p[0] = (val >> 8) & 0xff; + p[1] = (val ) & 0xff; +} + +static inline uint16_t +load_uint16_be(const void *cvp) +{ + const unsigned char *p = (const unsigned char *)cvp; + + return (p[1] | (p[0] << 8)); +} + +static inline void +store_uint32_be(uint32_t val, void *vp) +{ + unsigned char *p = (unsigned char *)vp; + + p[0] = (val >> 24) & 0xff; + p[1] = (val >> 16) & 0xff; + p[2] = (val >> 8) & 0xff; + p[3] = (val ) & 0xff; +} + +static inline uint32_t +load_uint32_be(const void *cvp) +{ + const unsigned char *p = (const unsigned char *)cvp; + + return (p[3] | (p[2] << 8) + | ((uint32_t) p[1] << 16) + | ((uint32_t) p[0] << 24)); +} + +static inline void +store_uint64_be(uint64_t val, void *vp) +{ + unsigned char *p = (unsigned char *)vp; + + p[0] = (unsigned char)((val >> 56) & 0xff); + p[1] = (unsigned char)((val >> 48) & 0xff); + p[2] = (unsigned char)((val >> 40) & 0xff); + p[3] = (unsigned char)((val >> 32) & 0xff); + p[4] = (unsigned char)((val >> 24) & 0xff); + p[5] = (unsigned char)((val >> 16) & 0xff); + p[6] = (unsigned char)((val >> 8) & 0xff); + p[7] = (unsigned char)((val ) & 0xff); +} + +static inline uint64_t +load_uint64_be(const void *cvp) +{ + const unsigned char *p = (const unsigned char *)cvp; + + return ((uint64_t)load_uint32_be(p) << 32) | load_uint32_be(p + 4); +} + +static inline unsigned char * +store_buffer(gss_buffer_t buffer, void *vp, int wide_nums) +{ + unsigned char *p = (unsigned char *)vp; + + if (wide_nums) { + store_uint64_be(buffer->length, p); + p += 8; + } else { + store_uint32_be(buffer->length, p); + p += 4; + } + + if (buffer->value != NULL) { + memcpy(p, buffer->value, buffer->length); + p += buffer->length; + } + + return p; +} + +static inline unsigned char * +load_buffer(const void *cvp, size_t length, gss_buffer_t buffer) +{ + buffer->length = 0; + buffer->value = GSSEAP_MALLOC(length); + if (buffer->value == NULL) + return NULL; + buffer->length = length; + memcpy(buffer->value, cvp, length); + return (unsigned char *)cvp + length; +} + +static inline unsigned char * +store_oid(gss_OID oid, void *vp) +{ + gss_buffer_desc buf; + + if (oid != GSS_C_NO_OID) { + buf.length = oid->length; + buf.value = oid->elements; + } else { + buf.length = 0; + buf.value = NULL; + } + + return store_buffer(&buf, vp, FALSE); +} + +static inline void +krbDataToGssBuffer(krb5_data *data, gss_buffer_t buffer) +{ + buffer->value = (void *)data->data; + buffer->length = data->length; +} + +static inline void +krbPrincComponentToGssBuffer(krb5_principal krbPrinc, + int index, gss_buffer_t buffer) +{ +#ifdef HAVE_HEIMDAL_VERSION + buffer->value = (void *)KRB_PRINC_NAME(krbPrinc)[index]; + buffer->length = strlen((char *)buffer->value); +#else + buffer->value = (void *)krb5_princ_component(NULL, krbPrinc, index)->data; + buffer->length = krb5_princ_component(NULL, krbPrinc, index)->length; +#endif /* HAVE_HEIMDAL_VERSION */ +} + +static inline void +krbPrincRealmToGssBuffer(krb5_principal krbPrinc, gss_buffer_t buffer) +{ +#ifdef HAVE_HEIMDAL_VERSION + buffer->value = (void *)KRB_PRINC_REALM(krbPrinc); + buffer->length = strlen((char *)buffer->value); +#else + krbDataToGssBuffer(KRB_PRINC_REALM(krbPrinc), buffer); +#endif +} + +static inline void +gssBufferToKrbData(gss_buffer_t buffer, krb5_data *data) +{ + data->data = (char *)buffer->value; + data->length = buffer->length; +} + +#ifdef __cplusplus +} +#endif + +#include "util_attr.h" +#ifdef GSSEAP_ENABLE_REAUTH +#include "util_reauth.h" +#endif + +#endif /* _UTIL_H_ */ diff --cc mech_eap/util_context.c index e54203f,0000000..0020ef6 mode 100644,000000..100644 --- a/mech_eap/util_context.c +++ b/mech_eap/util_context.c @@@ -1,234 -1,0 +1,234 @@@ +/* + * 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. + */ + +/* + * Utility routines for context handles. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gssEapAllocContext(OM_uint32 *minor, + gss_ctx_id_t *pCtx) +{ + OM_uint32 tmpMinor; + gss_ctx_id_t ctx; + + assert(*pCtx == GSS_C_NO_CONTEXT); + + ctx = (gss_ctx_id_t)GSSEAP_CALLOC(1, sizeof(*ctx)); + if (ctx == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + if (GSSEAP_MUTEX_INIT(&ctx->mutex) != 0) { + *minor = errno; + gssEapReleaseContext(&tmpMinor, &ctx); + return GSS_S_FAILURE; + } + + ctx->state = GSSEAP_STATE_IDENTITY; + + /* + * Integrity, confidentiality, sequencing and replay detection are + * always available. Regardless of what flags are requested in + * GSS_Init_sec_context, implementations MUST set the flag corresponding + * to these services in the output of GSS_Init_sec_context and + * GSS_Accept_sec_context. + */ + ctx->gssFlags = GSS_C_TRANS_FLAG | /* exporting contexts */ + GSS_C_INTEG_FLAG | /* integrity */ + GSS_C_CONF_FLAG | /* confidentiality */ + GSS_C_SEQUENCE_FLAG | /* sequencing */ + GSS_C_REPLAY_FLAG; /* replay detection */ + + *pCtx = ctx; + + return GSS_S_COMPLETE; +} + +static void +releaseInitiatorContext(struct gss_eap_initiator_ctx *ctx) +{ + eap_peer_sm_deinit(ctx->eap); +} + +static void +releaseAcceptorContext(struct gss_eap_acceptor_ctx *ctx) +{ + OM_uint32 tmpMinor; + + if (ctx->radConn != NULL) + rs_conn_destroy(ctx->radConn); + if (ctx->radContext != NULL) + rs_context_destroy(ctx->radContext); + if (ctx->radServer != NULL) + GSSEAP_FREE(ctx->radServer); + gss_release_buffer(&tmpMinor, &ctx->state); + if (ctx->vps != NULL) + gssEapRadiusFreeAvps(&tmpMinor, &ctx->vps); +} + +OM_uint32 +gssEapReleaseContext(OM_uint32 *minor, + gss_ctx_id_t *pCtx) +{ + OM_uint32 tmpMinor; + gss_ctx_id_t ctx = *pCtx; + krb5_context krbContext = NULL; + + if (ctx == GSS_C_NO_CONTEXT) { + return GSS_S_COMPLETE; + } + + gssEapKerberosInit(&tmpMinor, &krbContext); + +#ifdef GSSEAP_ENABLE_REAUTH + if (ctx->flags & CTX_FLAG_KRB_REAUTH) { + gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER); + } else +#endif + if (CTX_IS_INITIATOR(ctx)) { + releaseInitiatorContext(&ctx->initiatorCtx); + } else { + releaseAcceptorContext(&ctx->acceptorCtx); + } + + krb5_free_keyblock_contents(krbContext, &ctx->rfc3961Key); + gssEapReleaseName(&tmpMinor, &ctx->initiatorName); + gssEapReleaseName(&tmpMinor, &ctx->acceptorName); - gss_release_oid(&tmpMinor, &ctx->mechanismUsed); ++ gssEapReleaseOid(&tmpMinor, &ctx->mechanismUsed); + sequenceFree(&tmpMinor, &ctx->seqState); + gssEapReleaseCred(&tmpMinor, &ctx->defaultCred); + + GSSEAP_MUTEX_DESTROY(&ctx->mutex); + + memset(ctx, 0, sizeof(*ctx)); + GSSEAP_FREE(ctx); + *pCtx = GSS_C_NO_CONTEXT; + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapMakeToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t innerToken, + enum gss_eap_token_type tokenType, + gss_buffer_t outputToken) +{ + unsigned char *p; + + outputToken->length = tokenSize(ctx->mechanismUsed, innerToken->length); + outputToken->value = GSSEAP_MALLOC(outputToken->length); + if (outputToken->value == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + p = (unsigned char *)outputToken->value; + makeTokenHeader(ctx->mechanismUsed, innerToken->length, &p, tokenType); + memcpy(p, innerToken->value, innerToken->length); + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapVerifyToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t inputToken, + enum gss_eap_token_type *actualToken, + gss_buffer_t innerInputToken) +{ + OM_uint32 major; + size_t bodySize; + unsigned char *p = (unsigned char *)inputToken->value; + gss_OID_desc oidBuf; + gss_OID oid; + + 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, actualToken); + if (GSS_ERROR(major)) + return major; + + if (ctx->mechanismUsed == GSS_C_NO_OID) { + if (!gssEapIsConcreteMechanismOid(oid)) { + *minor = GSSEAP_WRONG_MECH; + return GSS_S_BAD_MECH; + } + + if (!gssEapInternalizeOid(oid, &ctx->mechanismUsed)) { + major = duplicateOid(minor, oid, &ctx->mechanismUsed); + if (GSS_ERROR(major)) + return major; + } + } + + innerInputToken->length = bodySize; + innerInputToken->value = p; + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapContextTime(OM_uint32 *minor, + gss_ctx_id_t context_handle, + OM_uint32 *time_rec) +{ + if (context_handle->expiryTime == 0) { + *time_rec = GSS_C_INDEFINITE; + } else { + time_t now, lifetime; + + time(&now); + lifetime = context_handle->expiryTime - now; + if (lifetime <= 0) { + *time_rec = 0; + return GSS_S_CONTEXT_EXPIRED; + } + *time_rec = lifetime; + } + + return GSS_S_COMPLETE; +} diff --cc mech_eap/util_krb.c index e96f5e6,0000000..7143685 mode 100644,000000..100644 --- a/mech_eap/util_krb.c +++ b/mech_eap/util_krb.c @@@ -1,585 -1,0 +1,583 @@@ +/* + * 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. + */ + +/* + * Kerberos 5 helpers. + */ + +#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; +} + +/* + * Derive a key K for RFC 4121 use by using the following + * derivation function (based on RFC 4402); + * + * KMSK = random-to-key(MSK) + * Tn = pseudo-random(KMSK, n || "rfc4121-gss-eap") + * L = output key size + * K = truncate(L, T1 || T2 || .. || Tn) + */ +OM_uint32 +gssEapDeriveRfc3961Key(OM_uint32 *minor, + const unsigned char *inputKey, + size_t inputKeyLength, + krb5_enctype encryptionType, + krb5_keyblock *pKey) +{ + krb5_context krbContext; +#ifndef HAVE_HEIMDAL_VERSION + krb5_data data; +#endif + krb5_data ns, t, prfOut; + krb5_keyblock kd; + krb5_error_code code; + size_t randomLength, keyLength, prfLength; + unsigned char constant[4 + sizeof("rfc4121-gss-eap") - 1], *p; + ssize_t i, remain; + + assert(encryptionType != ENCTYPE_NULL); + + memset(pKey, 0, sizeof(*pKey)); + + GSSEAP_KRB_INIT(&krbContext); + + KRB_KEY_INIT(&kd); + KRB_KEY_TYPE(&kd) = encryptionType; + + t.data = NULL; + t.length = 0; + + prfOut.data = NULL; + prfOut.length = 0; + + code = krb5_c_keylengths(krbContext, encryptionType, + &randomLength, &keyLength); + if (code != 0) + goto cleanup; + + KRB_KEY_DATA(&kd) = GSSEAP_MALLOC(keyLength); + if (KRB_KEY_DATA(&kd) == NULL) { + code = ENOMEM; + goto cleanup; + } + KRB_KEY_LENGTH(&kd) = keyLength; + + /* Convert MSK into a Kerberos key */ +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_random_to_key(krbContext, encryptionType, inputKey, + MIN(inputKeyLength, randomLength), &kd); +#else + data.length = MIN(inputKeyLength, randomLength); + data.data = (char *)inputKey; + + code = krb5_c_random_to_key(krbContext, encryptionType, &data, &kd); +#endif + if (code != 0) + goto cleanup; + + memset(&constant[0], 0, 4); + memcpy(&constant[4], "rfc4121-gss-eap", sizeof("rfc4121-gss-eap") - 1); + + ns.length = sizeof(constant); + ns.data = (char *)constant; + + /* Plug derivation constant and key into PRF */ + code = krb5_c_prf_length(krbContext, encryptionType, &prfLength); + if (code != 0) + goto cleanup; + + t.length = prfLength; + t.data = GSSEAP_MALLOC(t.length); + if (t.data == NULL) { + code = ENOMEM; + goto cleanup; + } + + prfOut.length = randomLength; + prfOut.data = GSSEAP_MALLOC(prfOut.length); + if (prfOut.data == NULL) { + code = ENOMEM; + goto cleanup; + } + + for (i = 0, p = (unsigned char *)prfOut.data, remain = randomLength; + remain > 0; + p += t.length, remain -= t.length, i++) + { + store_uint32_be(i, ns.data); + + code = krb5_c_prf(krbContext, &kd, &ns, &t); + if (code != 0) + goto cleanup; + + memcpy(p, t.data, MIN(t.length, remain)); + } + + /* Finally, convert PRF output into a new key which we will return */ +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_random_to_key(krbContext, encryptionType, + prfOut.data, prfOut.length, &kd); +#else + code = krb5_c_random_to_key(krbContext, encryptionType, &prfOut, &kd); +#endif + if (code != 0) + goto cleanup; + + *pKey = kd; + KRB_KEY_DATA(&kd) = NULL; + +cleanup: + if (KRB_KEY_DATA(&kd) != NULL) { + memset(KRB_KEY_DATA(&kd), 0, KRB_KEY_LENGTH(&kd)); + GSSEAP_FREE(KRB_KEY_DATA(&kd)); + } + if (t.data != NULL) { + memset(t.data, 0, t.length); + GSSEAP_FREE(t.data); + } + if (prfOut.data != NULL) { + memset(prfOut.data, 0, prfOut.length); + GSSEAP_FREE(prfOut.data); + } + *minor = code; + return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + +#ifdef HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE +extern krb5_error_code +krb5int_c_mandatory_cksumtype(krb5_context, krb5_enctype, krb5_cksumtype *); +#endif + +OM_uint32 +rfc3961ChecksumTypeForKey(OM_uint32 *minor, + krb5_keyblock *key, + krb5_cksumtype *cksumtype) +{ + krb5_context krbContext; +#ifndef HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE + krb5_data data; + krb5_checksum cksum; +#endif + + GSSEAP_KRB_INIT(&krbContext); + +#ifdef HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE + *minor = krb5int_c_mandatory_cksumtype(krbContext, KRB_KEY_TYPE(key), + cksumtype); + if (*minor != 0) + return GSS_S_FAILURE; +#else + data.length = 0; + data.data = NULL; + + memset(&cksum, 0, sizeof(cksum)); + + /* + * This is a complete hack but it's the only way to work with + * MIT Kerberos pre-1.9 without using private API, as it does + * not support passing in zero as the checksum type. + */ + *minor = krb5_c_make_checksum(krbContext, 0, key, 0, &data, &cksum); + if (*minor != 0) + return GSS_S_FAILURE; + +#ifdef HAVE_HEIMDAL_VERSION + *cksumtype = cksum.cksumtype; +#else + *cksumtype = cksum.checksum_type; +#endif + + krb5_free_checksum_contents(krbContext, &cksum); +#endif /* HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE */ + + if (!krb5_c_is_keyed_cksum(*cksumtype)) { + *minor = KRB5KRB_AP_ERR_INAPP_CKSUM; + return GSS_S_FAILURE; + } + + return GSS_S_COMPLETE; +} + +#ifdef HAVE_HEIMDAL_VERSION +static heim_general_string krbAnonymousPrincipalComponents[] = + { KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME }; + +static const Principal krbAnonymousPrincipalData = { + { KRB5_NT_WELLKNOWN, { 2, krbAnonymousPrincipalComponents } }, + "WELLKNOWN:ANONYMOUS" +}; +#endif + +krb5_const_principal +krbAnonymousPrincipal(void) +{ +#ifdef HAVE_HEIMDAL_VERSION + return &krbAnonymousPrincipalData; +#else + return krb5_anonymous_principal(); +#endif +} + +krb5_error_code +krbCryptoLength(krb5_context krbContext, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *key, +#endif + int type, + size_t *length) +{ +#ifdef HAVE_HEIMDAL_VERSION + return krb5_crypto_length(krbContext, krbCrypto, type, length); +#else + unsigned int len; + krb5_error_code code; + + code = krb5_c_crypto_length(krbContext, KRB_KEY_TYPE(key), type, &len); + if (code == 0) + *length = (size_t)len; + + return code; +#endif +} + +krb5_error_code +krbPaddingLength(krb5_context krbContext, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *key, +#endif + size_t dataLength, + size_t *padLength) +{ + krb5_error_code code; +#ifdef HAVE_HEIMDAL_VERSION + size_t headerLength, paddingLength; + + code = krbCryptoLength(krbContext, krbCrypto, + KRB5_CRYPTO_TYPE_HEADER, &headerLength); + if (code != 0) + return code; + + dataLength += headerLength; + + code = krb5_crypto_length(krbContext, krbCrypto, + KRB5_CRYPTO_TYPE_PADDING, &paddingLength); + if (code != 0) + return code; + + if (paddingLength != 0 && (dataLength % paddingLength) != 0) + *padLength = paddingLength - (dataLength % paddingLength); + else + *padLength = 0; + + return 0; +#else + unsigned int pad; + + code = krb5_c_padding_length(krbContext, KRB_KEY_TYPE(key), dataLength, &pad); + if (code == 0) + *padLength = (size_t)pad; + + return code; +#endif /* HAVE_HEIMDAL_VERSION */ +} + +krb5_error_code +krbBlockSize(krb5_context krbContext, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *key, +#endif + size_t *blockSize) +{ +#ifdef HAVE_HEIMDAL_VERSION + return krb5_crypto_getblocksize(krbContext, krbCrypto, blockSize); +#else + return krb5_c_block_size(krbContext, KRB_KEY_TYPE(key), blockSize); +#endif +} + +krb5_error_code +krbEnctypeToString(krb5_context krbContext, + krb5_enctype enctype, + const char *prefix, + gss_buffer_t string) +{ + krb5_error_code code; +#ifdef HAVE_HEIMDAL_VERSION + char *enctypeBuf = NULL; +#else + char enctypeBuf[128]; +#endif + size_t prefixLength, enctypeLength; + +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_enctype_to_string(krbContext, enctype, &enctypeBuf); +#else + code = krb5_enctype_to_name(enctype, 0, enctypeBuf, sizeof(enctypeBuf)); +#endif + if (code != 0) + return code; + + prefixLength = (prefix != NULL) ? strlen(prefix) : 0; + enctypeLength = strlen(enctypeBuf); + + string->value = GSSEAP_MALLOC(prefixLength + enctypeLength + 1); + if (string->value == NULL) { +#ifdef HAVE_HEIMDAL_VERSION + krb5_xfree(enctypeBuf); +#endif + return ENOMEM; + } + + if (prefixLength != 0) + memcpy(string->value, prefix, prefixLength); + memcpy((char *)string->value + prefixLength, enctypeBuf, enctypeLength); + + string->length = prefixLength + enctypeLength; + ((char *)string->value)[string->length] = '\0'; + +#ifdef HAVE_HEIMDAL_VERSION + krb5_xfree(enctypeBuf); +#endif + + return 0; +} + +krb5_error_code +krbMakeAuthDataKdcIssued(krb5_context context, + const krb5_keyblock *key, + krb5_const_principal issuer, +#ifdef HAVE_HEIMDAL_VERSION + const AuthorizationData *authdata, + AuthorizationData *adKdcIssued +#else + krb5_authdata *const *authdata, + krb5_authdata ***adKdcIssued +#endif + ) +{ +#ifdef HAVE_HEIMDAL_VERSION + krb5_error_code code; + AD_KDCIssued kdcIssued; + AuthorizationDataElement adDatum; + unsigned char *buf; + size_t buf_size, len; + krb5_crypto crypto = NULL; + + memset(&kdcIssued, 0, sizeof(kdcIssued)); + memset(adKdcIssued, 0, sizeof(*adKdcIssued)); + + kdcIssued.i_realm = issuer->realm != NULL ? (Realm *)&issuer->realm : NULL; + kdcIssued.i_sname = (PrincipalName *)&issuer->name; + kdcIssued.elements = *authdata; + + ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata, &len, code); + if (code != 0) + goto cleanup; + + code = krb5_crypto_init(context, key, 0, &crypto); + if (code != 0) + goto cleanup; + + code = krb5_create_checksum(context, crypto, KRB5_KU_AD_KDC_ISSUED, + 0, buf, buf_size, &kdcIssued.ad_checksum); + if (code != 0) + goto cleanup; + + GSSEAP_FREE(buf); + buf = NULL; + + ASN1_MALLOC_ENCODE(AD_KDCIssued, buf, buf_size, &kdcIssued, &len, code); + if (code != 0) + goto cleanup; + + adDatum.ad_type = KRB5_AUTHDATA_KDC_ISSUED; + adDatum.ad_data.length = buf_size; + adDatum.ad_data.data = buf; + + code = add_AuthorizationData(adKdcIssued, &adDatum); + if (code != 0) + goto cleanup; + +cleanup: + if (buf != NULL) + GSSEAP_FREE(buf); + if (crypto != NULL) + krb5_crypto_destroy(context, crypto); + free_Checksum(&kdcIssued.ad_checksum); + + return code; +#else + return krb5_make_authdata_kdc_issued(context, key, issuer, authdata, + adKdcIssued); +#endif /* HAVE_HEIMDAL_VERSION */ +} + +krb5_error_code +krbMakeCred(krb5_context krbContext, + krb5_auth_context authContext, + krb5_creds *creds, + krb5_data *data) +{ + krb5_error_code code; +#ifdef HAVE_HEIMDAL_VERSION + KRB_CRED krbCred; + KrbCredInfo krbCredInfo; + krb5_keyblock *key; + krb5_crypto krbCrypto = NULL; - unsigned char *buf = NULL; - size_t buf_size, len; ++ krb5_data credInfoData = { 0 }; ++ size_t len; +#else + krb5_data *d = NULL; +#endif + + memset(data, 0, sizeof(*data)); +#ifdef HAVE_HEIMDAL_VERSION + memset(&krbCred, 0, sizeof(krbCred)); + memset(&krbCredInfo, 0, sizeof(krbCredInfo)); + + key = (authContext->local_subkey != NULL) + ? authContext->local_subkey + : authContext->keyblock; + + krbCred.pvno = 5; + krbCred.msg_type = krb_cred; + krbCred.tickets.val = (Ticket *)GSSEAP_CALLOC(1, sizeof(Ticket)); + if (krbCred.tickets.val == NULL) { + code = ENOMEM; + goto cleanup; + } + krbCred.tickets.len = 1; + + code = decode_Ticket(creds->ticket.data, + creds->ticket.length, + krbCred.tickets.val, &len); + if (code != 0) + goto cleanup; + + krbCredInfo.key = creds->session; + krbCredInfo.prealm = &creds->client->realm; + krbCredInfo.pname = &creds->client->name; + krbCredInfo.flags = &creds->flags.b; + krbCredInfo.authtime = &creds->times.authtime; + krbCredInfo.starttime = &creds->times.starttime; + krbCredInfo.endtime = &creds->times.endtime; + krbCredInfo.renew_till = &creds->times.renew_till; + krbCredInfo.srealm = &creds->server->realm; + krbCredInfo.sname = &creds->server->name; + krbCredInfo.caddr = creds->addresses.len ? &creds->addresses : NULL; + - ASN1_MALLOC_ENCODE(KrbCredInfo, buf, buf_size, &krbCredInfo, &len, code); ++ ASN1_MALLOC_ENCODE(KrbCredInfo, credInfoData.data, credInfoData.length, ++ &krbCredInfo, &len, code); + if (code != 0) + goto cleanup; + + code = krb5_crypto_init(krbContext, key, 0, &krbCrypto); + if (code != 0) + goto cleanup; + + code = krb5_encrypt_EncryptedData(krbContext, + krbCrypto, + KRB5_KU_KRB_CRED, - buf, - len, ++ credInfoData.data, ++ credInfoData.length, + 0, + &krbCred.enc_part); + if (code != 0) + goto cleanup; + - GSSEAP_FREE(buf); - buf = NULL; - - ASN1_MALLOC_ENCODE(KRB_CRED, buf, buf_size, &krbCred, &len, code); ++ ASN1_MALLOC_ENCODE(KRB_CRED, data->data, data->length, ++ &krbCred, &len, code); + if (code != 0) + goto cleanup; + +cleanup: - if (buf != NULL) - GSSEAP_FREE(buf); + if (krbCrypto != NULL) + krb5_crypto_destroy(krbContext, krbCrypto); + free_KRB_CRED(&krbCred); ++ krb5_data_free(&credInfoData); + + return code; +#else + code = krb5_mk_1cred(krbContext, authContext, creds, &d, NULL); + if (code == 0) { + *data = *d; + GSSEAP_FREE(d); + } + + return code; +#endif /* HAVE_HEIMDAL_VERSION */ +} diff --cc mech_eap/util_mech.c index 81a6163,0000000..7343b31 mode 100644,000000..100644 --- a/mech_eap/util_mech.c +++ b/mech_eap/util_mech.c @@@ -1,315 -1,0 +1,332 @@@ +/* + * 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. + */ + +/* + * General mechanism utility routines. + */ + +#include "gssapiP_eap.h" + +/* + * 1.3.6.1.4.1.5322(padl) + * gssEap(22) + * mechanisms(1) + * eap-aes128-cts-hmac-sha1-96(17) + * eap-aes256-cts-hmac-sha1-96(18) + * nameTypes(2) + * apiExtensions(3) + * inquireSecContextByOid(1) + * inquireCredByOid(2) + * setSecContextOption(3) + * setCredOption(4) + * mechInvoke(5) + */ + +/* + * Note: the enctype-less OID is used as the mechanism OID in exported + * names. There is no public symbol for it. This is consistent with + * the krb5 mechanism which, whilst known by many OIDs, always uses a + * canonical OID for exported names. (This OID is also returned by + * gss_inquire_name.) + */ +static gss_OID_desc gssEapMechOids[] = { + /* 1.3.6.1.4.1.5322.22.1 */ + { 9, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01" }, + /* 1.3.6.1.4.1.5322.22.1.17 */ + { 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01\x11" }, + /* 1.3.6.1.4.1.5322.22.1.18 */ + { 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01\x12" } +}; + +gss_OID GSS_EAP_MECHANISM = &gssEapMechOids[0]; +gss_OID GSS_EAP_AES128_CTS_HMAC_SHA1_96_MECHANISM = &gssEapMechOids[1]; +gss_OID GSS_EAP_AES256_CTS_HMAC_SHA1_96_MECHANISM = &gssEapMechOids[2]; + +/* + * Returns TRUE is the OID is a concrete mechanism OID, that is, one + * with a Kerberos enctype as the last element. + */ +int +gssEapIsConcreteMechanismOid(const gss_OID oid) +{ + return oid->length > GSS_EAP_MECHANISM->length && + memcmp(oid->elements, GSS_EAP_MECHANISM->elements, + GSS_EAP_MECHANISM->length) == 0; +} + +int +gssEapIsMechanismOid(const gss_OID oid) +{ + return oid == GSS_C_NO_OID || + oidEqual(oid, GSS_EAP_MECHANISM) || + gssEapIsConcreteMechanismOid(oid); +} + +/* + * Validate that all elements are concrete mechanism OIDs. + */ +OM_uint32 +gssEapValidateMechs(OM_uint32 *minor, + const gss_OID_set mechs) +{ + int i; + + *minor = 0; + + if (mechs == GSS_C_NO_OID_SET) { + return GSS_S_COMPLETE; + } + + for (i = 0; i < mechs->count; i++) { + gss_OID oid = &mechs->elements[i]; + + if (!gssEapIsConcreteMechanismOid(oid)) { + *minor = GSSEAP_WRONG_MECH; + return GSS_S_BAD_MECH; + } + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapOidToEnctype(OM_uint32 *minor, + const gss_OID oid, + krb5_enctype *enctype) +{ + OM_uint32 major; + int suffix; + + major = decomposeOid(minor, + GSS_EAP_MECHANISM->elements, + GSS_EAP_MECHANISM->length, + oid, + &suffix); + if (major == GSS_S_COMPLETE) + *enctype = suffix; + + return major; +} + +OM_uint32 +gssEapEnctypeToOid(OM_uint32 *minor, + krb5_enctype enctype, + gss_OID *pOid) +{ + OM_uint32 major; + gss_OID oid; + + *pOid = NULL; + + oid = (gss_OID)GSSEAP_MALLOC(sizeof(*oid)); + if (oid == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + oid->length = GSS_EAP_MECHANISM->length + 1; + oid->elements = GSSEAP_MALLOC(oid->length); + if (oid->elements == NULL) { + *minor = ENOMEM; + GSSEAP_FREE(oid); + return GSS_S_FAILURE; + } + + major = composeOid(minor, + GSS_EAP_MECHANISM->elements, + GSS_EAP_MECHANISM->length, + enctype, + oid); + if (major == GSS_S_COMPLETE) { + gssEapInternalizeOid(oid, pOid); + *pOid = oid; + } else { + GSSEAP_FREE(oid->elements); + GSSEAP_FREE(oid); + } + + return major; +} + +OM_uint32 +gssEapIndicateMechs(OM_uint32 *minor, + gss_OID_set *mechs) +{ + krb5_context krbContext; + OM_uint32 major, tmpMinor; + krb5_enctype *etypes; + int i; + + GSSEAP_KRB_INIT(&krbContext); + + *minor = krb5_get_permitted_enctypes(krbContext, &etypes); + if (*minor != 0) { + return GSS_S_FAILURE; + } + + major = gss_create_empty_oid_set(minor, mechs); + if (GSS_ERROR(major)) { + GSSEAP_FREE(etypes); + return major; + } + + for (i = 0; etypes[i] != ENCTYPE_NULL; i++) { + gss_OID mechOid; + + /* XXX currently we aren't equipped to encode these enctypes */ + if (etypes[i] < 0 || etypes[i] > 127) + continue; + + major = gssEapEnctypeToOid(minor, etypes[i], &mechOid); + if (GSS_ERROR(major)) + break; + + major = gss_add_oid_set_member(minor, mechOid, mechs); + if (GSS_ERROR(major)) + break; + + gss_release_oid(&tmpMinor, &mechOid); + } + + GSSEAP_FREE(etypes); + + *minor = 0; + return major; +} + +OM_uint32 +gssEapDefaultMech(OM_uint32 *minor, + gss_OID *oid) +{ + gss_OID_set mechs; + OM_uint32 major, tmpMinor; + + major = gssEapIndicateMechs(minor, &mechs); + if (GSS_ERROR(major)) { + return major; + } + + if (mechs->count == 0) { + gss_release_oid_set(&tmpMinor, &mechs); + return GSS_S_BAD_MECH; + } + + if (!gssEapInternalizeOid(&mechs->elements[0], oid)) { + /* don't double-free if we didn't internalize it */ + mechs->elements[0].length = 0; + mechs->elements[0].elements = NULL; + } + + gss_release_oid_set(&tmpMinor, &mechs); + + *minor = 0; + return GSS_S_COMPLETE; +} + +int +gssEapInternalizeOid(const gss_OID oid, + gss_OID *const pInternalizedOid) +{ + int i; + + *pInternalizedOid = GSS_C_NO_OID; + + for (i = 0; + i < sizeof(gssEapMechOids) / sizeof(gssEapMechOids[0]); + i++) { + if (oidEqual(oid, &gssEapMechOids[i])) { + *pInternalizedOid = (const gss_OID)&gssEapMechOids[i]; + break; + } + } + + if (*pInternalizedOid == GSS_C_NO_OID) { + if (oidEqual(oid, GSS_EAP_NT_PRINCIPAL_NAME)) + *pInternalizedOid = (const gss_OID)GSS_EAP_NT_PRINCIPAL_NAME; + } + + if (*pInternalizedOid == GSS_C_NO_OID) { + *pInternalizedOid = oid; + return 0; + } + + return 1; +} + ++OM_uint32 ++gssEapReleaseOid(OM_uint32 *minor, gss_OID *oid) ++{ ++ gss_OID internalizedOid = GSS_C_NO_OID; ++ ++ *minor = 0; ++ ++ if (gssEapInternalizeOid(*oid, &internalizedOid)) { ++ /* OID was internalized, so we can mark it as "freed" */ ++ *oid = GSS_C_NO_OID; ++ return GSS_S_COMPLETE; ++ } ++ ++ /* we don't know about this OID */ ++ return GSS_S_CONTINUE_NEEDED; ++} ++ +static gss_buffer_desc gssEapSaslMechs[] = { + { sizeof("EAP") - 1, "EAP", }, /* not used */ + { sizeof("EAP-AES128") - 1, "EAP-AES128" }, + { sizeof("EAP-AES256") - 1, "EAP-AES256" }, +}; + +gss_buffer_t +gssEapOidToSaslName(const gss_OID oid) +{ + size_t i; + + for (i = 1; i < sizeof(gssEapMechOids)/sizeof(gssEapMechOids[0]); i++) { + if (oidEqual(&gssEapMechOids[i], oid)) + return &gssEapSaslMechs[i]; + } + + return GSS_C_NO_BUFFER; +} + +gss_OID +gssEapSaslNameToOid(const gss_buffer_t name) +{ + size_t i; + + for (i = 1; i < sizeof(gssEapSaslMechs)/sizeof(gssEapSaslMechs[0]); i++) { + if (bufferEqual(&gssEapSaslMechs[i], name)) + return &gssEapMechOids[i]; + } + + return GSS_C_NO_OID; +} diff --cc mech_eap/util_reauth.c index 28dd133,0000000..8b853cb mode 100644,000000..100644 --- a/mech_eap/util_reauth.c +++ b/mech_eap/util_reauth.c @@@ -1,1176 -1,0 +1,1184 @@@ +/* + * 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. + */ + +/* + * Fast reauthentication support. + */ + +#include "gssapiP_eap.h" + +#include + +/* + * Fast reauthentication support for EAP GSS. + */ + +krb5_error_code +krb5_encrypt_tkt_part(krb5_context, const krb5_keyblock *, krb5_ticket *); + +krb5_error_code +encode_krb5_ticket(const krb5_ticket *rep, krb5_data **code); + +static OM_uint32 +gssDisplayName(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t buffer, + gss_OID *name_type); + +static OM_uint32 +gssImportName(OM_uint32 *minor, + gss_buffer_t buffer, + gss_OID name_type, + gss_name_t *name); + +static krb5_error_code +getAcceptorKey(krb5_context krbContext, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + krb5_principal *princ, + krb5_keyblock *key) +{ + krb5_error_code code; + krb5_keytab keytab = NULL; + krb5_keytab_entry ktent = { 0 }; + krb5_kt_cursor cursor; + + *princ = NULL; + memset(key, 0, sizeof(*key)); + memset(&cursor, 0, sizeof(cursor)); + + code = krb5_kt_default(krbContext, &keytab); + if (code != 0) + goto cleanup; + + if (cred != GSS_C_NO_CREDENTIAL && cred->name != GSS_C_NO_NAME) { + code = krb5_kt_get_entry(krbContext, keytab, + cred->name->krbPrincipal, 0, + ctx->encryptionType, &ktent); + if (code != 0) + goto cleanup; + } else { + /* + * It's not clear that looking encrypting the ticket in the + * requested EAP enctype provides any value. + */ + code = krb5_kt_start_seq_get(krbContext, keytab, &cursor); + if (code != 0) + goto cleanup; + + while ((code = krb5_kt_next_entry(krbContext, keytab, + &ktent, &cursor)) == 0) { + if (KRB_KEY_TYPE(KRB_KT_ENT_KEYBLOCK(&ktent)) == ctx->encryptionType) + break; + else + KRB_KT_ENT_FREE(krbContext, &ktent); + } + } + + if (code == 0) { + *princ = ktent.principal; + *key = *KRB_KT_ENT_KEYBLOCK(&ktent); + } + +cleanup: + if (cred == GSS_C_NO_CREDENTIAL || cred->name == GSS_C_NO_NAME) + krb5_kt_end_seq_get(krbContext, keytab, &cursor); + krb5_kt_close(krbContext, keytab); + if (code != 0) + KRB_KT_ENT_FREE(krbContext, &ktent); + + return code; +} + +static OM_uint32 +freezeAttrContext(OM_uint32 *minor, + gss_name_t initiatorName, + krb5_const_principal acceptorPrinc, + krb5_keyblock *session, +#ifdef HAVE_HEIMDAL_VERSION + krb5_authdata *kdcIssuedAuthData +#else + krb5_authdata ***kdcIssuedAuthData +#endif + ) +{ + OM_uint32 major, tmpMinor; + krb5_error_code code; + krb5_context krbContext; + gss_buffer_desc attrBuf = GSS_C_EMPTY_BUFFER; +#ifdef HAVE_HEIMDAL_VERSION + krb5_authdata authDataBuf, *authData = &authDataBuf; + AuthorizationDataElement authDatum = { 0 }; +#else + krb5_authdata *authData[2], authDatum = { 0 }; +#endif + + memset(kdcIssuedAuthData, 0, sizeof(*kdcIssuedAuthData)); + + GSSEAP_KRB_INIT(&krbContext); + + major = gssEapExportAttrContext(minor, initiatorName, &attrBuf); + if (GSS_ERROR(major)) + return major; + + authDatum.ad_type = KRB5_AUTHDATA_RADIUS_AVP; +#ifdef HAVE_HEIMDAL_VERSION + authDatum.ad_data.length = attrBuf.length; + authDatum.ad_data.data = attrBuf.value; + authData->len = 1; + authData->val = &authDatum; +#else + authDatum.length = attrBuf.length; + authDatum.contents = attrBuf.value; + authData[0] = &authDatum; + authData[1] = NULL; +#endif + + code = krbMakeAuthDataKdcIssued(krbContext, session, acceptorPrinc, + authData, kdcIssuedAuthData); + if (code != 0) { + major = GSS_S_FAILURE; + *minor = code; + } else { + major = GSS_S_COMPLETE; + } + + gss_release_buffer(&tmpMinor, &attrBuf); + + return major; +} + +/* + * Fabricate a ticket to ourselves given a GSS EAP context. + */ +OM_uint32 +gssEapMakeReauthCreds(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t credBuf) +{ + OM_uint32 major = GSS_S_COMPLETE; + krb5_error_code code; + krb5_context krbContext = NULL; + krb5_keyblock session = { 0 }, acceptorKey = { 0 }; + krb5_principal server = NULL; +#ifdef HAVE_HEIMDAL_VERSION + Ticket ticket; + EncTicketPart enc_part; + AuthorizationData authData = { 0 }; + krb5_crypto krbCrypto = NULL; - unsigned char *buf = NULL; - size_t buf_size, len; ++ krb5_data ticketData = { 0 }; ++ krb5_data encPartData = { 0 }; ++ size_t len; +#else + krb5_ticket ticket; + krb5_enc_tkt_part enc_part; ++ krb5_data *ticketData = NULL; +#endif - krb5_data *ticketData = NULL, credsData = { 0 }; ++ krb5_data credsData = { 0 }; + krb5_creds creds = { 0 }; + krb5_auth_context authContext = NULL; + + memset(&ticket, 0, sizeof(ticket)); + memset(&enc_part, 0, sizeof(enc_part)); + + credBuf->length = 0; + credBuf->value = NULL; + + GSSEAP_KRB_INIT(&krbContext); + + code = getAcceptorKey(krbContext, ctx, cred, &server, &acceptorKey); + if (code == KRB5_KT_NOTFOUND) { + *minor = code; + return GSS_S_UNAVAILABLE; + } else if (code != 0) + goto cleanup; + +#ifdef HAVE_HEIMDAL_VERSION + ticket.realm = server->realm; + ticket.sname = server->name; +#else + ticket.server = server; +#endif + + /* + * Generate a random session key to place in the ticket and + * sign the "KDC-Issued" authorization data element. + */ - code = krb5_c_make_random_key(krbContext, ctx->encryptionType, - &session); ++#ifdef HAVE_HEIMDAL_VERSION ++ code = krb5_generate_random_keyblock(krbContext, ctx->encryptionType, ++ &session); + if (code != 0) + goto cleanup; + - #ifdef HAVE_HEIMDAL_VERSION + enc_part.flags.initial = 1; + enc_part.key = session; + enc_part.crealm = ctx->initiatorName->krbPrincipal->realm; + enc_part.cname = ctx->initiatorName->krbPrincipal->name; + enc_part.authtime = time(NULL); + enc_part.starttime = &enc_part.authtime; + enc_part.endtime = (ctx->expiryTime != 0) + ? ctx->expiryTime : KRB_TIME_FOREVER; + enc_part.renew_till = NULL; + enc_part.authorization_data = &authData; + + major = freezeAttrContext(minor, ctx->initiatorName, server, + &session, &authData); + if (GSS_ERROR(major)) + goto cleanup; + - ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size, &enc_part, &len, code); ++ ASN1_MALLOC_ENCODE(EncTicketPart, encPartData.data, encPartData.length, ++ &enc_part, &len, code); + if (code != 0) + goto cleanup; + + code = krb5_crypto_init(krbContext, &acceptorKey, 0, &krbCrypto); + if (code != 0) + goto cleanup; + + code = krb5_encrypt_EncryptedData(krbContext, + krbCrypto, + KRB5_KU_TICKET, - buf, - len, ++ encPartData.data, ++ encPartData.length, + 0, + &ticket.enc_part); + if (code != 0) + goto cleanup; + - GSSEAP_FREE(buf); - buf = NULL; - - ASN1_MALLOC_ENCODE(Ticket, buf, buf_size, &ticket, &len, code); ++ ASN1_MALLOC_ENCODE(Ticket, ticketData.data, ticketData.length, ++ &ticket, &len, code); + if (code != 0) + goto cleanup; +#else ++ code = krb5_c_make_random_key(krbContext, ctx->encryptionType, ++ &session); ++ if (code != 0) ++ goto cleanup; ++ + enc_part.flags = TKT_FLG_INITIAL; + enc_part.session = &session; + enc_part.client = ctx->initiatorName->krbPrincipal; + enc_part.times.authtime = time(NULL); + enc_part.times.starttime = enc_part.times.authtime; + enc_part.times.endtime = (ctx->expiryTime != 0) + ? ctx->expiryTime + : KRB_TIME_FOREVER; + enc_part.times.renew_till = 0; + + major = freezeAttrContext(minor, ctx->initiatorName, server, + &session, &enc_part.authorization_data); + if (GSS_ERROR(major)) + goto cleanup; + + ticket.enc_part2 = &enc_part; + + code = krb5_encrypt_tkt_part(krbContext, &acceptorKey, &ticket); + if (code != 0) + goto cleanup; + + code = encode_krb5_ticket(&ticket, &ticketData); + if (code != 0) + goto cleanup; +#endif /* HAVE_HEIMDAL_VERSION */ + + creds.client = ctx->initiatorName->krbPrincipal; + creds.server = server; +#ifdef HAVE_HEIMDAL_VERSION + creds.session = session; + creds.times.authtime = enc_part.authtime; + creds.times.starttime = *enc_part.starttime; + creds.times.endtime = enc_part.endtime; + creds.times.renew_till = 0; + creds.flags.b = enc_part.flags; - creds.ticket = *ticketData; ++ creds.ticket = ticketData; + creds.authdata = authData; +#else + creds.keyblock = session; + creds.times = enc_part.times; + creds.ticket_flags = enc_part.flags; + creds.ticket = *ticketData; + creds.authdata = enc_part.authorization_data; +#endif + + code = krb5_auth_con_init(krbContext, &authContext); + if (code != 0) + goto cleanup; + + code = krb5_auth_con_setflags(krbContext, authContext, 0); + if (code != 0) + goto cleanup; + + code = krb5_auth_con_setsendsubkey(krbContext, authContext, + &ctx->rfc3961Key); + if (code != 0) + goto cleanup; + + code = krbMakeCred(krbContext, authContext, &creds, &credsData); + if (code != 0) + goto cleanup; + + krbDataToGssBuffer(&credsData, credBuf); + +cleanup: +#ifdef HAVE_HEIMDAL_VERSION + if (krbCrypto != NULL) + krb5_crypto_destroy(krbContext, krbCrypto); - if (buf != NULL) - GSSEAP_FREE(buf); + free_AuthorizationData(&authData); + free_EncryptedData(&ticket.enc_part); ++ krb5_data_free(&ticketData); ++ krb5_data_free(&encPartData); +#else + krb5_free_authdata(krbContext, enc_part.authorization_data); + if (ticket.enc_part.ciphertext.data != NULL) + GSSEAP_FREE(ticket.enc_part.ciphertext.data); ++ krb5_free_data(krbContext, ticketData); +#endif + krb5_free_keyblock_contents(krbContext, &session); + krb5_free_principal(krbContext, server); + krb5_free_keyblock_contents(krbContext, &acceptorKey); - krb5_free_data(krbContext, ticketData); + krb5_auth_con_free(krbContext, authContext); + + if (major == GSS_S_COMPLETE) { + *minor = code; + major = (code != 0) ? GSS_S_FAILURE : GSS_S_COMPLETE; + } + + return major; +} + +static int +isTicketGrantingServiceP(krb5_context krbContext, + krb5_const_principal principal) +{ + if (KRB_PRINC_LENGTH(principal) == 2 && +#ifdef HAVE_HEIMDAL_VERSION + strcmp(KRB_PRINC_NAME(principal)[0], "krbtgt") == 0 +#else + krb5_princ_component(krbContext, principal, 0)->length == 6 && + memcmp(krb5_princ_component(krbContext, + principal, 0)->data, "krbtgt", 6) == 0 +#endif + ) + return TRUE; + + return FALSE; +} + +/* + * Returns TRUE if the configuration variable reauth_use_ccache is + * set in krb5.conf for the eap_gss application and the client realm. + */ +static int +reauthUseCredsCache(krb5_context krbContext, + krb5_principal principal) +{ + int reauthUseCCache; + + /* if reauth_use_ccache, use default credentials cache if ticket is for us */ + krb5_appdefault_boolean(krbContext, "eap_gss", + KRB_PRINC_REALM(principal), + "reauth_use_ccache", 0, &reauthUseCCache); + + return reauthUseCCache; +} + +/* + * Look in default credentials cache for reauthentication credentials, + * if policy allows. + */ +static OM_uint32 +getDefaultReauthCredentials(OM_uint32 *minor, + gss_cred_id_t cred, + gss_name_t target, + time_t now, + OM_uint32 timeReq) +{ + OM_uint32 major = GSS_S_CRED_UNAVAIL; + krb5_context krbContext = NULL; + krb5_error_code code = 0; + krb5_ccache ccache = NULL; + krb5_creds match = { 0 }; + krb5_creds creds = { 0 }; + + GSSEAP_KRB_INIT(&krbContext); + + assert(cred != GSS_C_NO_CREDENTIAL); + assert(target != GSS_C_NO_NAME); + + if (cred->name == GSS_C_NO_NAME || + !reauthUseCredsCache(krbContext, cred->name->krbPrincipal)) + goto cleanup; + + match.client = cred->name->krbPrincipal; + match.server = target->krbPrincipal; + if (timeReq != 0 && timeReq != GSS_C_INDEFINITE) + match.times.endtime = now + timeReq; + + code = krb5_cc_default(krbContext, &ccache); + if (code != 0) + goto cleanup; + + code = krb5_cc_retrieve_cred(krbContext, ccache, 0, &match, &creds); + if (code != 0) + goto cleanup; + + cred->flags |= CRED_FLAG_DEFAULT_CCACHE; + cred->krbCredCache = ccache; + ccache = NULL; + + major = gss_krb5_import_cred(minor, cred->krbCredCache, NULL, NULL, + &cred->krbCred); + +cleanup: + if (major == GSS_S_CRED_UNAVAIL) + *minor = code; + + if (ccache != NULL) + krb5_cc_close(krbContext, ccache); + krb5_free_cred_contents(krbContext, &creds); + + return major; +} + +/* + * Returns TRUE if the credential handle's reauth credentials are + * valid or if we can use the default credentials cache. Credentials + * handle must be locked. + */ +int +gssEapCanReauthP(gss_cred_id_t cred, + gss_name_t target, + OM_uint32 timeReq) +{ + time_t now, expiryReq; + OM_uint32 minor; + + assert(cred != GSS_C_NO_CREDENTIAL); + + now = time(NULL); + expiryReq = now; + if (timeReq != GSS_C_INDEFINITE) + expiryReq += timeReq; + + if (cred->krbCredCache != NULL && cred->expiryTime > expiryReq) + return TRUE; + + if (getDefaultReauthCredentials(&minor, cred, target, + now, timeReq) == GSS_S_COMPLETE) + return TRUE; + + return FALSE; +} + +/* + * Store re-authentication (Kerberos) credentials in a credential handle. + * Credentials handle must be locked. + */ +OM_uint32 +gssEapStoreReauthCreds(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t credBuf) +{ + OM_uint32 major = GSS_S_COMPLETE; + krb5_error_code code; + krb5_context krbContext = NULL; + krb5_auth_context authContext = NULL; + krb5_data credData = { 0 }; + krb5_creds **creds = NULL; + krb5_principal canonPrinc; + krb5_principal ccPrinc = NULL; + int i; + + if (credBuf->length == 0 || cred == GSS_C_NO_CREDENTIAL) + return GSS_S_COMPLETE; + + GSSEAP_KRB_INIT(&krbContext); + + code = krb5_auth_con_init(krbContext, &authContext); + if (code != 0) + goto cleanup; + + code = krb5_auth_con_setflags(krbContext, authContext, 0); + if (code != 0) + goto cleanup; + + code = krb5_auth_con_setrecvsubkey(krbContext, authContext, + &ctx->rfc3961Key); + if (code != 0) + goto cleanup; + + gssBufferToKrbData(credBuf, &credData); + + code = krb5_rd_cred(krbContext, authContext, &credData, &creds, NULL); + if (code != 0) + goto cleanup; + + if (creds == NULL || creds[0] == NULL) + goto cleanup; + + code = krb5_copy_principal(krbContext, creds[0]->client, &canonPrinc); + if (code != 0) + goto cleanup; + + krb5_free_principal(krbContext, cred->name->krbPrincipal); + cred->name->krbPrincipal = canonPrinc; + + if (creds[0]->times.endtime == KRB_TIME_FOREVER) + cred->expiryTime = 0; + else + cred->expiryTime = creds[0]->times.endtime; + + if (cred->krbCredCache == NULL) { + if (reauthUseCredsCache(krbContext, creds[0]->client) && + krb5_cc_default(krbContext, &cred->krbCredCache) == 0) + cred->flags |= CRED_FLAG_DEFAULT_CCACHE; + } else { + /* + * If we already have an associated credentials cache, possibly from + * the last time we stored a reauthentication credential, then we + * need to clear it out and release the associated GSS credential. + */ + if (cred->flags & CRED_FLAG_DEFAULT_CCACHE) { + krb5_cc_remove_cred(krbContext, cred->krbCredCache, 0, creds[0]); + } else { + krb5_cc_destroy(krbContext, cred->krbCredCache); + cred->krbCredCache = NULL; + } + gssReleaseCred(minor, &cred->krbCred); + } + + if (cred->krbCredCache == NULL) { + code = krb5_cc_new_unique(krbContext, "MEMORY", NULL, &cred->krbCredCache); + if (code != 0) + goto cleanup; + } + + if ((cred->flags & CRED_FLAG_DEFAULT_CCACHE) == 0 || + krb5_cc_get_principal(krbContext, cred->krbCredCache, &ccPrinc) != 0) { + code = krb5_cc_initialize(krbContext, cred->krbCredCache, + creds[0]->client); + if (code != 0) + goto cleanup; + } + + for (i = 0; creds[i] != NULL; i++) { + krb5_creds kcred = *(creds[i]); + + /* + * Swap in the acceptor name the client asked for so + * get_credentials() works. We're making the assumption that + * any service tickets returned are for us. We'll need to + * reflect some more on whether that is a safe assumption. + */ + if (!isTicketGrantingServiceP(krbContext, kcred.server)) + kcred.server = ctx->acceptorName->krbPrincipal; + + code = krb5_cc_store_cred(krbContext, cred->krbCredCache, &kcred); + if (code != 0) + goto cleanup; + } + + major = gss_krb5_import_cred(minor, cred->krbCredCache, NULL, NULL, + &cred->krbCred); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + *minor = code; + + krb5_free_principal(krbContext, ccPrinc); + krb5_auth_con_free(krbContext, authContext); + if (creds != NULL) { + for (i = 0; creds[i] != NULL; i++) + krb5_free_creds(krbContext, creds[i]); + GSSEAP_FREE(creds); + } + if (major == GSS_S_COMPLETE) + major = *minor ? GSS_S_FAILURE : GSS_S_COMPLETE; + + return major; +} + +#ifndef HAVE_HEIMDAL_VERSION +static gss_buffer_desc radiusAvpKrbAttr = { + sizeof("urn:authdata-radius-avp") - 1, "urn:authdata-radius-avp" +}; +#endif + +/* + * Unfortunately extracting an AD-KDCIssued authorization data element + * is pretty implementation-dependent. It's not possible to verify the + * signature ourselves because the ticket session key is not exposed + * outside GSS. In an ideal world, all AD-KDCIssued elements would be + * verified by the Kerberos library and authentication would fail if + * verification failed. We're not quite there yet and as a result have + * to go through some hoops to get this to work. The alternative would + * be to sign the authorization data with our long-term key, but it + * seems a pity to compromise the design because of current implementation + * limitations. + * + * (Specifically, the hoops involve a libkrb5 authorisation data plugin + * that exposes the verified and serialised attribute context through + * the Kerberos GSS mechanism's naming extensions API.) + */ +static OM_uint32 +defrostAttrContext(OM_uint32 *minor, + gss_ctx_id_t glueContext, + gss_name_t glueName, + gss_name_t mechName) +{ + OM_uint32 major, tmpMinor; +#ifdef HAVE_HEIMDAL_VERSION + gss_OID_desc oid = { 0 }; + gss_buffer_set_t authData = GSS_C_NO_BUFFER_SET; +#else + gss_buffer_desc authData = GSS_C_EMPTY_BUFFER; + gss_buffer_desc authDataDisplay = GSS_C_EMPTY_BUFFER; + int more = -1; + int authenticated, complete; +#endif + +#ifdef HAVE_HEIMDAL_VERSION + major = composeOid(minor, + GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements, + GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length, + KRB5_AUTHDATA_RADIUS_AVP, &oid); + if (GSS_ERROR(major)) + return major; + + /* XXX we are assuming that this verifies AD-KDCIssued signature */ + major = gssInquireSecContextByOid(minor, glueContext, + &oid, &authData); + if (major == GSS_S_COMPLETE) { + if (authData == GSS_C_NO_BUFFER_SET || authData->count != 1) + major = GSS_S_FAILURE; + else + major = gssEapImportAttrContext(minor, authData->elements, mechName); + } else if (major == GSS_S_FAILURE && *minor == ENOENT) { + /* This is the equivalent of GSS_S_UNAVAILABLE for MIT attr APIs */ + *minor = 0; + major = GSS_S_COMPLETE; + } + + gss_release_buffer_set(&tmpMinor, &authData); + GSSEAP_FREE(oid.elements); +#else + major = gssGetNameAttribute(minor, glueName, &radiusAvpKrbAttr, + &authenticated, &complete, + &authData, &authDataDisplay, &more); + if (major == GSS_S_COMPLETE) { + if (authenticated == 0) + major = GSS_S_BAD_NAME; + else + major = gssEapImportAttrContext(minor, &authData, mechName); + } else if (major == GSS_S_UNAVAILABLE) { + major = GSS_S_COMPLETE; + } + + gss_release_buffer(&tmpMinor, &authData); + gss_release_buffer(&tmpMinor, &authDataDisplay); +#endif /* HAVE_HEIMDAL_VERSION */ + + return major; +} + +/* + * Convert a mechanism glue to an EAP mechanism name by displaying and + * importing it. This also handles the RADIUS attributes. + */ +OM_uint32 +gssEapGlueToMechName(OM_uint32 *minor, + gss_ctx_id_t glueContext, + gss_name_t glueName, + gss_name_t *pMechName) +{ + OM_uint32 major, tmpMinor; + gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER; + + *pMechName = GSS_C_NO_NAME; + + major = gssDisplayName(minor, glueName, &nameBuf, NULL); + if (GSS_ERROR(major)) + goto cleanup; + + major = gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME, + pMechName); + if (GSS_ERROR(major)) + goto cleanup; + + major = defrostAttrContext(minor, glueContext, glueName, *pMechName); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + if (GSS_ERROR(major)) { + gssReleaseName(&tmpMinor, pMechName); + *pMechName = GSS_C_NO_NAME; + } + + gss_release_buffer(&tmpMinor, &nameBuf); + + return major; +} + +/* + * Convert an EAP mechanism name to a mechanism glue name by displaying + * and importing it. + */ +OM_uint32 +gssEapMechToGlueName(OM_uint32 *minor, + gss_name_t mechName, + gss_name_t *pGlueName) +{ + OM_uint32 major, tmpMinor; + gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER; + + *pGlueName = GSS_C_NO_NAME; + + major = gssEapDisplayName(minor, mechName, &nameBuf, NULL); + if (GSS_ERROR(major)) + goto cleanup; + + major = gssImportName(minor, &nameBuf, GSS_C_NT_USER_NAME, + pGlueName); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + gss_release_buffer(&tmpMinor, &nameBuf); + + return major; +} + +/* + * Suck out the analgous elements of a Kerberos GSS context into an EAP + * one so that the application doesn't know the difference. + */ +OM_uint32 +gssEapReauthComplete(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + const gss_OID mech, + OM_uint32 timeRec) +{ + OM_uint32 major, tmpMinor; + gss_buffer_set_t keyData = GSS_C_NO_BUFFER_SET; + krb5_context krbContext = NULL; +#ifdef HAVE_HEIMDAL_VERSION + krb5_storage *sp = NULL; +#endif + + GSSEAP_KRB_INIT(&krbContext); + + if (!oidEqual(mech, gss_mech_krb5)) { + major = GSS_S_BAD_MECH; + goto cleanup; + } + + /* Get the raw subsession key and encryption type */ +#ifdef HAVE_HEIMDAL_VERSION +#define KRB_GSS_SUBKEY_COUNT 1 /* encoded session key */ + major = gssInquireSecContextByOid(minor, ctx->kerberosCtx, + GSS_KRB5_GET_SUBKEY_X, &keyData); +#else +#define KRB_GSS_SUBKEY_COUNT 2 /* raw session key, enctype OID */ + major = gssInquireSecContextByOid(minor, ctx->kerberosCtx, + GSS_C_INQ_SSPI_SESSION_KEY, &keyData); +#endif + if (GSS_ERROR(major)) + goto cleanup; + + if (keyData == GSS_C_NO_BUFFER_SET || keyData->count < KRB_GSS_SUBKEY_COUNT) { + *minor = GSSEAP_KEY_UNAVAILABLE; + major = GSS_S_FAILURE; + goto cleanup; + } + +#ifdef HAVE_HEIMDAL_VERSION + sp = krb5_storage_from_mem(keyData->elements[0].value, + keyData->elements[0].length); + if (sp == NULL) { + *minor = ENOMEM; + major = GSS_S_FAILURE; + goto cleanup; + } + + *minor = krb5_ret_keyblock(sp, &ctx->rfc3961Key); + if (*minor != 0) { + major = GSS_S_FAILURE; + goto cleanup; + } +#else + { + gss_OID_desc oid; + int suffix; + + oid.length = keyData->elements[1].length; + oid.elements = keyData->elements[1].value; + + /* GSS_KRB5_SESSION_KEY_ENCTYPE_OID */ + major = decomposeOid(minor, + "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04", + 10, &oid, &suffix); + if (GSS_ERROR(major)) + goto cleanup; + + ctx->encryptionType = suffix; + } + + { + krb5_keyblock key; + + KRB_KEY_LENGTH(&key) = keyData->elements[0].length; + KRB_KEY_DATA(&key) = keyData->elements[0].value; + KRB_KEY_TYPE(&key) = ctx->encryptionType; + + *minor = krb5_copy_keyblock_contents(krbContext, + &key, &ctx->rfc3961Key); + if (*minor != 0) { + major = GSS_S_FAILURE; + goto cleanup; + } + } +#endif /* HAVE_HEIMDAL_VERSION */ + + major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key, + &ctx->checksumType); + if (GSS_ERROR(major)) + goto cleanup; + + if (timeRec != GSS_C_INDEFINITE) + ctx->expiryTime = time(NULL) + timeRec; + + /* Initialize our sequence state */ + 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)) + goto cleanup; + + major = GSS_S_COMPLETE; + +cleanup: +#ifdef HAVE_HEIMDAL_VERSION + if (sp != NULL) + krb5_storage_free(sp); +#endif + gss_release_buffer_set(&tmpMinor, &keyData); + + return major; +} + +/* + * The remainder of this file consists of wrappers so we can call into the + * mechanism glue without calling ourselves. + */ +static OM_uint32 +(*gssInitSecContextNext)(OM_uint32 *, + gss_cred_id_t, + gss_ctx_id_t *, + gss_name_t, + gss_OID, + OM_uint32, + OM_uint32, + gss_channel_bindings_t, + gss_buffer_t, + gss_OID *, + gss_buffer_t, + OM_uint32 *, + OM_uint32 *); + +static OM_uint32 +(*gssAcceptSecContextNext)(OM_uint32 *, + gss_ctx_id_t *, + gss_cred_id_t, + gss_buffer_t, + gss_channel_bindings_t, + gss_name_t *, + gss_OID *, + gss_buffer_t, + OM_uint32 *, + OM_uint32 *, + gss_cred_id_t *); + +static OM_uint32 +(*gssReleaseCredNext)(OM_uint32 *, gss_cred_id_t *); + +static OM_uint32 +(*gssReleaseNameNext)(OM_uint32 *, gss_name_t *); + +static OM_uint32 +(*gssInquireSecContextByOidNext)(OM_uint32 *, + const gss_ctx_id_t, + const gss_OID, + gss_buffer_set_t *); + +static OM_uint32 +(*gssDeleteSecContextNext)(OM_uint32 *, + gss_ctx_id_t *, + gss_buffer_t); + +static OM_uint32 +(*gssDisplayNameNext)(OM_uint32 *, + gss_name_t, + gss_buffer_t, + gss_OID *); + +static OM_uint32 +(*gssImportNameNext)(OM_uint32 *, + gss_buffer_t, + gss_OID, + gss_name_t *); + +static OM_uint32 +(*gssStoreCredNext)(OM_uint32 *, + const gss_cred_id_t, + gss_cred_usage_t, + const gss_OID, + OM_uint32, + OM_uint32, + gss_OID_set *, + gss_cred_usage_t *); + +static OM_uint32 +(*gssGetNameAttributeNext)(OM_uint32 *, + gss_name_t, + gss_buffer_t, + int *, + int *, + gss_buffer_t, + gss_buffer_t, + int *); + +#define NEXT_SYMBOL(local, global) do { \ + ((local) = dlsym(RTLD_NEXT, (global))); \ + if ((local) == NULL) { \ + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; \ + major = GSS_S_UNAVAILABLE; \ + /* but continue */ \ + } \ + } while (0) + +OM_uint32 +gssEapReauthInitialize(OM_uint32 *minor) +{ + OM_uint32 major = GSS_S_COMPLETE; + + NEXT_SYMBOL(gssInitSecContextNext, "gss_init_sec_context"); + NEXT_SYMBOL(gssAcceptSecContextNext, "gss_accept_sec_context"); + NEXT_SYMBOL(gssReleaseCredNext, "gss_release_cred"); + NEXT_SYMBOL(gssReleaseNameNext, "gss_release_name"); + NEXT_SYMBOL(gssInquireSecContextByOidNext, "gss_inquire_sec_context_by_oid"); + NEXT_SYMBOL(gssDeleteSecContextNext, "gss_delete_sec_context"); + NEXT_SYMBOL(gssDisplayNameNext, "gss_display_name"); + NEXT_SYMBOL(gssImportNameNext, "gss_import_name"); + NEXT_SYMBOL(gssStoreCredNext, "gss_store_cred"); ++#ifndef HAVE_HEIMDAL_VERSION + NEXT_SYMBOL(gssGetNameAttributeNext, "gss_get_name_attribute"); ++#endif + + return major; +} + +OM_uint32 +gssInitSecContext(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec) +{ + if (gssInitSecContextNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssInitSecContextNext(minor, cred, context_handle, + target_name, mech_type, req_flags, + time_req, input_chan_bindings, + input_token, actual_mech_type, + output_token, ret_flags, time_rec); +} + +OM_uint32 +gssAcceptSecContext(OM_uint32 *minor, + gss_ctx_id_t *context_handle, + gss_cred_id_t cred, + gss_buffer_t input_token, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle) +{ + if (gssAcceptSecContextNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssAcceptSecContextNext(minor, context_handle, cred, + input_token, input_chan_bindings, + src_name, mech_type, output_token, + ret_flags, time_rec, delegated_cred_handle); +} + +OM_uint32 +gssReleaseCred(OM_uint32 *minor, + gss_cred_id_t *cred_handle) +{ + if (gssReleaseCredNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssReleaseCredNext(minor, cred_handle); +} + +OM_uint32 +gssReleaseName(OM_uint32 *minor, + gss_name_t *name) +{ + if (gssReleaseName == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssReleaseNameNext(minor, name); +} + +OM_uint32 +gssDeleteSecContext(OM_uint32 *minor, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) +{ + if (gssDeleteSecContextNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssDeleteSecContextNext(minor, context_handle, output_token); +} + +static OM_uint32 +gssDisplayName(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t buffer, + gss_OID *name_type) +{ + if (gssDisplayNameNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssDisplayNameNext(minor, name, buffer, name_type); +} + +static OM_uint32 +gssImportName(OM_uint32 *minor, + gss_buffer_t buffer, + gss_OID name_type, + gss_name_t *name) +{ + if (gssImportNameNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssImportNameNext(minor, buffer, name_type, name); +} + +OM_uint32 +gssInquireSecContextByOid(OM_uint32 *minor, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + if (gssInquireSecContextByOidNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssInquireSecContextByOidNext(minor, context_handle, + desired_object, data_set); +} + +OM_uint32 +gssStoreCred(OM_uint32 *minor, + const gss_cred_id_t input_cred_handle, + gss_cred_usage_t input_usage, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored) +{ + if (gssStoreCredNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssStoreCredNext(minor, input_cred_handle, input_usage, + desired_mech, overwrite_cred, default_cred, + elements_stored, cred_usage_stored); +} + +OM_uint32 +gssGetNameAttribute(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) +{ + if (gssGetNameAttributeNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssGetNameAttributeNext(minor, name, attr, authenticated, complete, + value, display_value, more); +}