From 6fd9f5a521f9efa7cb9dd43f90ae8f33c83c66e1 Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Wed, 22 Sep 2010 22:09:55 +0200 Subject: [PATCH] some work on fast reauth --- Makefile.am | 1 + accept_sec_context.c | 14 +++ gssapiP_eap.h | 3 + init_sec_context.c | 18 ++++ set_cred_option.c | 7 +- util.h | 9 ++ util_cred.c | 8 +- util_reauth.c | 275 +++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 332 insertions(+), 3 deletions(-) create mode 100644 util_reauth.c diff --git a/Makefile.am b/Makefile.am index aa22204..5347b06 100644 --- a/Makefile.am +++ b/Makefile.am @@ -72,6 +72,7 @@ libmech_eap_la_SOURCES = \ util_oid.c \ util_ordering.c \ util_radius.cpp \ + util_reauth.c \ util_saml.cpp \ util_shib.cpp \ util_token.c \ diff --git a/accept_sec_context.c b/accept_sec_context.c index bc2f295..981a7d8 100644 --- a/accept_sec_context.c +++ b/accept_sec_context.c @@ -392,6 +392,19 @@ eapGssSmAcceptGssChannelBindings(OM_uint32 *minor, } static OM_uint32 +eapGssSmAcceptKrbCred(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t inputToken, + gss_channel_bindings_t chanBindings, + gss_buffer_t outputToken) +{ + /* Called with already established context */ + *minor = EINVAL; + return GSS_S_BAD_STATUS; +} + +static OM_uint32 eapGssSmAcceptEstablished(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred, @@ -417,6 +430,7 @@ static struct gss_eap_acceptor_sm { { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, eapGssSmAcceptIdentity }, { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, eapGssSmAcceptAuthenticate }, { TOK_TYPE_GSS_CB, TOK_TYPE_NONE, eapGssSmAcceptGssChannelBindings }, + { TOK_TYPE_NONE, TOK_TYPE_KRB_CRED, eapGssSmAcceptKrbCred }, { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmAcceptEstablished }, }; diff --git a/gssapiP_eap.h b/gssapiP_eap.h index f93c105..8fbbdb9 100644 --- a/gssapiP_eap.h +++ b/gssapiP_eap.h @@ -91,6 +91,8 @@ struct gss_cred_id_struct { gss_OID_set mechanisms; time_t expiryTime; char *radiusConfigFile; + krb5_ccache krbCredCache; + gss_cred_id_t krbCred; }; #define CTX_FLAG_INITIATOR 0x00000001 @@ -101,6 +103,7 @@ enum gss_eap_state { EAP_STATE_IDENTITY = 0, EAP_STATE_AUTHENTICATE, EAP_STATE_GSS_CHANNEL_BINDINGS, + EAP_STATE_FAST_REAUTH, EAP_STATE_ESTABLISHED }; diff --git a/init_sec_context.c b/init_sec_context.c index 5fcbec8..173f234 100644 --- a/init_sec_context.c +++ b/init_sec_context.c @@ -490,6 +490,23 @@ cleanup: } static OM_uint32 +eapGssSmInitKrbCred(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + /* Called with already established context */ + *minor = EINVAL; + return GSS_S_BAD_STATUS; +} + +static OM_uint32 eapGssSmInitEstablished(OM_uint32 *minor, gss_cred_id_t cred, gss_ctx_id_t ctx, @@ -523,6 +540,7 @@ static struct gss_eap_initiator_sm { { TOK_TYPE_NONE, TOK_TYPE_EAP_RESP, eapGssSmInitIdentity }, { TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP, eapGssSmInitAuthenticate }, { TOK_TYPE_NONE, TOK_TYPE_GSS_CB, eapGssSmInitGssChannelBindings }, + { TOK_TYPE_KRB_CRED,TOK_TYPE_NONE, eapGssSmInitKrbCred }, { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmInitEstablished }, }; diff --git a/set_cred_option.c b/set_cred_option.c index 9e9c8ee..b42dd80 100644 --- a/set_cred_option.c +++ b/set_cred_option.c @@ -105,16 +105,19 @@ gss_OID GSS_EAP_CRED_SET_CRED_FLAG = &setCredOps[1].oid; OM_uint32 gssspi_set_cred_option(OM_uint32 *minor, - gss_cred_id_t cred, + gss_cred_id_t *cred, const gss_OID desired_object, const gss_buffer_t value) { OM_uint32 major = GSS_S_UNAVAILABLE; int i; + if (*cred == GSS_C_NO_CREDENTIAL) + return GSS_S_UNAVAILABLE; + for (i = 0; i < sizeof(setCredOps) / sizeof(setCredOps[0]); i++) { if (oidEqual(&setCredOps[i].oid, desired_object)) { - major = (*setCredOps[i].setOption)(minor, cred, + major = (*setCredOps[i].setOption)(minor, *cred, desired_object, value); break; } diff --git a/util.h b/util.h index 74641c7..c23c997 100644 --- a/util.h +++ b/util.h @@ -89,6 +89,8 @@ enum gss_eap_token_type { TOK_TYPE_EAP_RESP = 0x0601, /* draft-howlett-eap-gss */ TOK_TYPE_EAP_REQ = 0x0602, /* draft-howlett-eap-gss */ TOK_TYPE_GSS_CB = 0x0603, /* draft-howlett-eap-gss */ + TOK_TYPE_KRB_CRED = 0x0604, /* to be specified */ + TOK_TYPE_GSS_REAUTH = 0x0605, /* to be specified */ }; #define EAP_EXPORT_CONTEXT_V1 1 @@ -547,6 +549,13 @@ krbDataToGssBuffer(krb5_data *data, gss_buffer_t buffer) buffer->length = data->length; } +static inline void +gssBufferToKrbData(gss_buffer_t buffer, krb5_data *data) +{ + data->data = (char *)buffer->value; + data->length = buffer->length; +} + #ifdef __cplusplus } #endif diff --git a/util_cred.c b/util_cred.c index ab24151..d6d8f51 100644 --- a/util_cred.c +++ b/util_cred.c @@ -63,11 +63,14 @@ gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred) { OM_uint32 tmpMinor; gss_cred_id_t cred = *pCred; + krb5_context krbContext = NULL; if (cred == GSS_C_NO_CREDENTIAL) { return GSS_S_COMPLETE; } + GSSEAP_KRB_INIT(&krbContext); + gssEapReleaseName(&tmpMinor, &cred->name); if (cred->password.value != NULL) { @@ -76,7 +79,10 @@ gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred) } if (cred->radiusConfigFile != NULL) - free(cred->radiusConfigFile); + GSSEAP_FREE(cred->radiusConfigFile); + + if (cred->krbCredCache != NULL) + krb5_cc_destroy(krbContext, cred->krbCredCache); GSSEAP_MUTEX_DESTROY(&cred->mutex); memset(cred, 0, sizeof(*cred)); diff --git a/util_reauth.c b/util_reauth.c new file mode 100644 index 0000000..4063321 --- /dev/null +++ b/util_reauth.c @@ -0,0 +1,275 @@ +/* + * 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" + +/* + * Fast reauthentication support for EAP GSS. + */ + +#define KRB5_AUTHDATA_RADIUS_AVP 513 + +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; + krb5_kt_cursor cursor = NULL; + + *princ = NULL; + memset(key, 0, sizeof(*key)); + memset(&ktent, 0, sizeof(ktent)); + + 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 { + 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 (ktent.key.enctype != ctx->encryptionType) { + krb5_free_keytab_entry_contents(krbContext, &ktent); + continue; + } + } + } + + code = krb5_copy_principal(krbContext, ktent.principal, princ); + if (code != 0) + goto cleanup; + + code = krb5_copy_keyblock_contents(krbContext, &ktent.key, key); + if (code != 0) + goto cleanup; + +cleanup: + if (cred == GSS_C_NO_CREDENTIAL || cred->name == GSS_C_NO_NAME) + krb5_kt_end_seq_get(krbContext, keytab, &cursor); + + krb5_free_keytab_entry_contents(krbContext, &ktent); + krb5_kt_end_seq_get(krbContext, keytab, &cursor); + krb5_kt_close(krbContext, keytab); + + if (code != 0) { + if (*princ != NULL) { + krb5_free_principal(krbContext, *princ); + *princ = NULL; + } + krb5_free_keyblock_contents(krbContext, key), + memset(key, 0, sizeof(key)); + } + + return code; +} + +static OM_uint32 +makeReauthCreds(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t credBuf) +{ + OM_uint32 major = GSS_S_COMPLETE, code; + krb5_context krbContext = NULL; + krb5_ticket ticket = { 0 }; + krb5_keyblock session, acceptorKey = { 0 }; + krb5_enc_tkt_part enc_part = { 0 }; + gss_buffer_desc attrBuf = GSS_C_EMPTY_BUFFER; + krb5_authdata *authData[2], authDatum = { 0 }; + krb5_data *ticketData = NULL, *credsData = NULL; + krb5_creds creds = { 0 }; + krb5_auth_context authContext = NULL; + + credBuf->length = 0; + credBuf->value = NULL; + + GSSEAP_KRB_INIT(&krbContext); + + code = getAcceptorKey(krbContext, ctx, cred, + &ticket.server, &acceptorKey); + if (code != 0) + goto cleanup; + + enc_part.flags = TKT_FLG_INITIAL; + + code = krb5_c_make_random_key(krbContext, ctx->encryptionType, + &session); + if (code != 0) + goto cleanup; + + 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 + ? ctx->expiryTime + : KRB5_INT32_MAX; + enc_part.times.renew_till = 0; + + major = gssEapExportAttrContext(minor, ctx->initiatorName, + &attrBuf); + if (GSS_ERROR(major)) + goto cleanup; + + authDatum.ad_type = KRB5_AUTHDATA_RADIUS_AVP; + authDatum.length = attrBuf.length; + authDatum.contents = attrBuf.value; + authData[0] = &authDatum; + authData[1] = NULL; + enc_part.authorization_data = authData; + + ticket.enc_part2 = &enc_part; + + code = encode_krb5_ticket(&ticket, &ticketData); + if (code != 0) + goto cleanup; + + creds.client = enc_part.client; + creds.server = ticket.server; + creds.keyblock = session; + creds.times = enc_part.times; + creds.ticket_flags = enc_part.flags; + creds.ticket = *ticketData; + creds.authdata = authData; + + code = krb5_auth_con_init(krbContext, &authContext); + if (code != 0) + goto cleanup; + + code = krb5_auth_con_setsendsubkey(krbContext, authContext, &ctx->rfc3961Key); + if (code != 0) + goto cleanup; + + code = krb5_mk_1cred(krbContext, authContext, &creds, &credsData, NULL); + if (code != 0) + goto cleanup; + + krbDataToGssBuffer(credsData, credBuf); + + code = krb5_encrypt_tkt_part(krbContext, acceptorKey, &ticket); + if (code != 0) + goto cleanup; + +cleanup: + *minor = code; + + if (ticket.enc_part.ciphertext.data != NULL) + GSSEAP_FREE(ticket.enc_part.ciphertext.data); + + krb5_free_keyblock_contents(krbContext, &session); + krb5_free_keyblock_contents(krbContext, &acceptorKey); + gss_release_buffer(&code, &attrBuf); + krb5_free_data(krbContext, ticketData); + krb5_auth_con_free(krbContext, authContext); + if (credsData != NULL) + GSSEAP_FREE(credsData); + + if (major == GSS_S_COMPLETE) + major = *minor ? GSS_S_FAILURE : GSS_S_COMPLETE; + + return major; +} + +static OM_uint32 +storeReauthCreds(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t credBuf) +{ + OM_uint32 major = GSS_S_COMPLETE, code; + krb5_context krbContext = NULL; + krb5_auth_context authContext = NULL; + krb5_data credData = { 0 }; + krb5_creds **creds = 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_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; + + + +/* +OM_uint32 KRB5_CALLCONV +gss_krb5_import_cred(OM_uint32 *minor_status, + krb5_ccache id, + krb5_principal keytab_principal, + krb5_keytab keytab, + gss_cred_id_t *cred); +*/ + + if (creds != NULL && creds[0] != NULL) { + } + +cleanup: + *minor = code; + + krb5_auth_con_free(krbContext, authContext); + if (creds != NULL) { + for (i = 0; creds[i] != NULL; i++) + krb5_free_creds(krbContext, creds[i]); + } + if (major == GSS_S_COMPLETE) + major = *minor ? GSS_S_FAILURE : GSS_S_COMPLETE; + + return major; +} -- 2.1.4