From: Luke Howard Date: Thu, 23 Sep 2010 23:42:23 +0000 (+0200) Subject: Merge branch 'fastreauth' X-Git-Tag: vm/20110310~233 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mech_eap.orig;a=commitdiff_plain;h=c3ff83a02d37820b9c70cc3746c72a900dfd85ac;hp=47cb5eac0d36eda61ed5cb6fabfb4f13e5ed71d0 Merge branch 'fastreauth' Conflicts: mech_eap/util_cred.c --- diff --git a/Makefile.am b/Makefile.am index aa22204..9f2167a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,21 +1,21 @@ AUTOMAKE_OPTIONS = foreign gssdir = $(libdir)/gss +gss_LTLIBRARIES = mech_eap.la -gss_LTLIBRARIES = libmech_eap.la +mech_eap_la_CPPFLAGS = -DBUILD_GSSEAP_LIB -DSYSCONFDIR=\"${sysconfdir}\" +mech_eap_la_CFLAGS = -g -Wall -fno-strict-aliasing \ + @EAP_CFLAGS@ @KRB5_CFLAGS@ @TARGET_CFLAGS@ +mech_eap_la_CXXFLAGS = -g -Wall \ + @EAP_CFLAGS@ @KRB5_CFLAGS@ @SHIBSP_CXXFLAGS@ \ + @SHIBRESOLVER_CXXFLAGS@ @TARGET_CFLAGS@ +mech_eap_la_LDFLAGS = -avoid-version -module \ + -export-symbols mech_eap.exports -no-undefined \ + @EAP_LDFLAGS@ @KRB5_LDFLAGS@ @TARGET_LDFLAGS@ +mech_eap_la_LIBADD = @EAP_LIBS@ @KRB5_LIBS@ @SHIBSP_LIBS@ \ + @SHIBRESOLVER_LIBS@ -lfreeradius-client -libmech_eap_la_CPPFLAGS = -DBUILD_GSSEAP_LIB -DSYSCONFDIR=\"${sysconfdir}\" -libmech_eap_la_CFLAGS = -g -Wall -fno-strict-aliasing \ - @EAP_CFLAGS@ @KRB5_CFLAGS@ @TARGET_CFLAGS@ -libmech_eap_la_CXXFLAGS = -g -Wall \ - @EAP_CFLAGS@ @KRB5_CFLAGS@ @SHIBSP_CXXFLAGS@ \ - @SHIBRESOLVER_CXXFLAGS@ @TARGET_CFLAGS@ -libmech_eap_la_LDFLAGS = -export-symbols mech_eap.exports -version-info 0:0:0 \ - -no-undefined \ - @EAP_LDFLAGS@ @KRB5_LDFLAGS@ @TARGET_LDFLAGS@ -libmech_eap_la_LIBADD = @EAP_LIBS@ @KRB5_LIBS@ @SHIBSP_LIBS@ @SHIBRESOLVER_LIBS@ -lfreeradius-client - -libmech_eap_la_SOURCES = \ +mech_eap_la_SOURCES = \ accept_sec_context.c \ acquire_cred.c \ acquire_cred_with_password.c \ @@ -81,3 +81,16 @@ libmech_eap_la_SOURCES = \ wrap_iov_length.c \ wrap_size_limit.c +if GSSEAP_ENABLE_REAUTH +mech_eap_la_SOURCES += util_reauth.c + +krb5pluginsdir = $(libdir)/krb5/plugins/authdata +krb5plugins_LTLIBRARIES = radius_ad.la + +radius_ad_la_CFLAGS = -g -Wall -fno-strict-aliasing \ + @EAP_CFLAGS@ @KRB5_CFLAGS@ @TARGET_CFLAGS@ +radius_ad_la_LDFLAGS = -avoid-version -module \ + -export-symbols radius_ad.exports -no-undefined +radius_ad_la_LIBADD = @KRB5_LIBS@ +radius_ad_la_SOURCES = util_adshim.c +endif diff --git a/TODO b/TODO index bbfbc03..cdc5491 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,4 @@ - hook up libradius, AVP parsing logic - better handling of mechanism-specific error namespace - better interfaces for initiator EAP configuration/credential management -- libradius library handle is a global variable -- radius expiry time +- make CBT ASN.1 diff --git a/accept_sec_context.c b/accept_sec_context.c index bc2f295..ba0f021 100644 --- a/accept_sec_context.c +++ b/accept_sec_context.c @@ -32,11 +32,21 @@ #include "gssapiP_eap.h" +#ifdef GSSEAP_ENABLE_REAUTH +static OM_uint32 +eapGssSmAcceptGssReauth(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); +#endif + /* * Mark a context as ready for cryptographic operations */ static OM_uint32 -acceptReady(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred) +acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred) { OM_uint32 major, tmpMinor; VALUE_PAIR *vp; @@ -261,7 +271,7 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor, pos[0] == EAP_TYPE_IDENTITY) { /* * XXX TODO do we really need to set User-Name? FreeRADIUS does - * not appear to require it. + * not require it but some other RADIUS servers might. */ major = addAvpFromBuffer(minor, rh, &send, PW_USER_NAME, 0, &nameBuf); if (GSS_ERROR(major)) @@ -322,11 +332,11 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor, ctx->acceptorCtx.avps = received; received = NULL; - major = acceptReady(minor, ctx, cred); + major = acceptReadyEap(minor, ctx, cred); if (GSS_ERROR(major)) goto cleanup; - ctx->state = EAP_STATE_GSS_CHANNEL_BINDINGS; + ctx->state = EAP_STATE_EXTENSIONS_REQ; } major = GSS_S_CONTINUE_NEEDED; @@ -339,56 +349,99 @@ cleanup: } static OM_uint32 -eapGssSmAcceptGssChannelBindings(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) +acceptGssChannelBindings(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t inputToken, + gss_channel_bindings_t chanBindings) { - OM_uint32 major; + OM_uint32 major, tmpMinor; gss_iov_buffer_desc iov[2]; - outputToken->length = 0; - outputToken->value = NULL; + iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE; + iov[0].buffer.length = 0; + iov[0].buffer.value = NULL; - if (chanBindings == GSS_C_NO_CHANNEL_BINDINGS) { - ctx->state = EAP_STATE_ESTABLISHED; - return GSS_S_COMPLETE; - } + iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM; + iov[1].buffer = *inputToken; - if (inputToken->length < 14) { - return GSS_S_DEFECTIVE_TOKEN; + major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL, + iov, 2, TOK_TYPE_WRAP); + if (GSS_ERROR(major)) + return major; + + if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS && + !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) { + major = GSS_S_BAD_BINDINGS; + } else { + major = GSS_S_CONTINUE_NEEDED; } - iov[0].type = GSS_IOV_BUFFER_TYPE_DATA; - iov[0].buffer.length = 0; - iov[0].buffer.value = NULL; + gss_release_buffer(&tmpMinor, &iov[0].buffer); - if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS) - iov[0].buffer = chanBindings->application_data; + return major; +} - iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER; - iov[1].buffer.length = 16; - iov[1].buffer.value = (unsigned char *)inputToken->value - 2; +static OM_uint32 +eapGssSmAcceptExtensionsReq(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) +{ + OM_uint32 major; - assert(load_uint16_be(iov[1].buffer.value) == TOK_TYPE_GSS_CB); + outputToken->length = 0; + outputToken->value = NULL; - iov[2].type = GSS_IOV_BUFFER_TYPE_TRAILER; - iov[2].buffer.length = inputToken->length - 14; - iov[2].buffer.value = (unsigned char *)inputToken->value + 14; + major = acceptGssChannelBindings(minor, ctx, cred, inputToken, + chanBindings); + if (GSS_ERROR(major)) + return major; - major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL, - iov, 3, TOK_TYPE_GSS_CB); - if (major == GSS_S_COMPLETE) { - ctx->state = EAP_STATE_ESTABLISHED; + ctx->state = EAP_STATE_EXTENSIONS_RESP; + + return GSS_S_CONTINUE_NEEDED; +} + +static OM_uint32 +eapGssSmAcceptExtensionsResp(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) +{ + OM_uint32 major, tmpMinor; + gss_buffer_desc credsToken = GSS_C_EMPTY_BUFFER; + + /* + * If we're built with fast reauthentication enabled, then + * fabricate a ticket from the initiator to ourselves. + * Otherwise return an empty token. + */ +#ifdef GSSEAP_ENABLE_REAUTH + major = gssEapMakeReauthCreds(minor, ctx, cred, &credsToken); + if (GSS_ERROR(major)) + return major; +#else + credsToken.value = ""; +#endif /* GSSEAP_ENABLE_REAUTH */ + + major = duplicateBuffer(minor, &credsToken, outputToken); + if (GSS_ERROR(major)) { + gss_release_buffer(&tmpMinor, &credsToken); + return major; } -#if 0 - gss_release_buffer(&tmpMinor, &iov[0].buffer); +#ifdef GSSEAP_ENABLE_REAUTH + gss_release_buffer(&tmpMinor, &credsToken); #endif - return major; + ctx->state = EAP_STATE_ESTABLISHED; + + return GSS_S_COMPLETE; } static OM_uint32 @@ -414,10 +467,14 @@ static struct gss_eap_acceptor_sm { gss_channel_bindings_t, gss_buffer_t); } eapGssAcceptorSm[] = { - { 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_NONE, eapGssSmAcceptEstablished }, + { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, eapGssSmAcceptIdentity }, + { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, eapGssSmAcceptAuthenticate }, + { TOK_TYPE_EXT_REQ, TOK_TYPE_NONE, eapGssSmAcceptExtensionsReq }, + { TOK_TYPE_NONE, TOK_TYPE_EXT_RESP, eapGssSmAcceptExtensionsResp }, + { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmAcceptEstablished }, +#ifdef GSSEAP_ENABLE_REAUTH + { TOK_TYPE_GSS_REAUTH, TOK_TYPE_GSS_REAUTH, eapGssSmAcceptGssReauth }, +#endif }; OM_uint32 @@ -439,6 +496,8 @@ gss_accept_sec_context(OM_uint32 *minor, struct gss_eap_acceptor_sm *sm = NULL; gss_buffer_desc innerInputToken = GSS_C_EMPTY_BUFFER; gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER; + enum gss_eap_token_type tokType; + int initialContextToken = 0; *minor = 0; @@ -458,6 +517,7 @@ gss_accept_sec_context(OM_uint32 *minor, if (GSS_ERROR(major)) return major; + initialContextToken = 1; *context_handle = ctx; } @@ -466,12 +526,29 @@ gss_accept_sec_context(OM_uint32 *minor, sm = &eapGssAcceptorSm[ctx->state]; major = gssEapVerifyToken(minor, ctx, input_token, - sm->inputTokenType, &innerInputToken); + &tokType, &innerInputToken); if (GSS_ERROR(major)) goto cleanup; +#ifdef GSSEAP_ENABLE_REAUTH + /* + * If we're built with fast reauthentication support, it's valid + * for an initiator to send a GSS reauthentication token as its + * initial context token, causing us to short-circuit the state + * machine and process Kerberos GSS messages instead. + */ + if (tokType == TOK_TYPE_GSS_REAUTH && initialContextToken) { + ctx->state = EAP_STATE_KRB_REAUTH_GSS; + } else +#endif + if (tokType != sm->inputTokenType) { + major = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + /* If credentials were provided, check they're usable with this mech */ - if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) { + if (cred != GSS_C_NO_CREDENTIAL && + !gssEapCredAvailable(cred, ctx->mechanismUsed)) { major = GSS_S_BAD_MECH; goto cleanup; } @@ -493,7 +570,7 @@ gss_accept_sec_context(OM_uint32 *minor, if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type)) duplicateOid(&tmpMinor, ctx->mechanismUsed, mech_type); } - if (innerOutputToken.length != 0) { + if (innerOutputToken.value != NULL) { tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken, sm->outputTokenType, output_token); if (GSS_ERROR(tmpMajor)) { @@ -530,3 +607,75 @@ cleanup: return major; } +#ifdef GSSEAP_ENABLE_REAUTH +static OM_uint32 +acceptReadyKrb(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + const gss_name_t initiator, + const gss_OID mech, + OM_uint32 timeRec) +{ + OM_uint32 major; + + major = gssEapGlueToMechName(minor, initiator, &ctx->initiatorName); + if (GSS_ERROR(major)) + return major; + + if (cred != GSS_C_NO_CREDENTIAL && cred->name != GSS_C_NO_NAME) { + major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName); + if (GSS_ERROR(major)) + return major; + } + + major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec); + if (GSS_ERROR(major)) + return major; + + ctx->state = EAP_STATE_ESTABLISHED; + + return GSS_S_COMPLETE; +} + +static OM_uint32 +eapGssSmAcceptGssReauth(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) +{ + OM_uint32 major, tmpMinor; + gss_cred_id_t krbCred = GSS_C_NO_CREDENTIAL; + gss_name_t krbInitiator = GSS_C_NO_NAME; + gss_OID mech = GSS_C_NO_OID; + OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE; + + ctx->flags |= CTX_FLAG_KRB_REAUTH_GSS; + + if (cred != GSS_C_NO_CREDENTIAL) + krbCred = cred->krbCred; + + major = gssAcceptSecContext(minor, + &ctx->kerberosCtx, + krbCred, + inputToken, + chanBindings, + &krbInitiator, + &mech, + outputToken, + &gssFlags, + &timeRec, + NULL); + if (major == GSS_S_COMPLETE) { + major = acceptReadyKrb(minor, ctx, cred, + krbInitiator, mech, timeRec); + } + + ctx->gssFlags = gssFlags; + + gssReleaseName(&tmpMinor, &krbInitiator); + + return major; +} +#endif /* GSSEAP_ENABLE_REAUTH */ diff --git a/authdata_plugin.h b/authdata_plugin.h new file mode 100644 index 0000000..32bff2f --- /dev/null +++ b/authdata_plugin.h @@ -0,0 +1,331 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * krb5/authdata_plugin.h + * + * Copyright (C) 2007 Apple Inc. 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. + * + * AuthorizationData plugin definitions for Kerberos 5. + */ + +/* + * This is considered an INTERNAL interface at this time. + * + * Some work is needed before exporting it: + * + * + Documentation. + * + Sample code. + * + Test cases (preferably automated testing under "make check"). + * + Hook into TGS exchange too; will change API. + * + Examine memory management issues, especially for Windows; may + * change API. + * + * Other changes that would be nice to have, but not necessarily + * before making this interface public: + * + * + Library support for AD-IF-RELEVANT and similar wrappers. (We can + * make the plugin construct them if it wants them.) + * + KDC could combine/optimize wrapped AD elements provided by + * multiple plugins, e.g., two IF-RELEVANT sequences could be + * merged. (The preauth plugin API also has this bug, we're going + * to need a general fix.) + */ + +#ifndef KRB5_AUTHDATA_PLUGIN_H_INCLUDED +#define KRB5_AUTHDATA_PLUGIN_H_INCLUDED +#include + +/* + * While arguments of these types are passed-in, for the most part a + * authorization data module can treat them as opaque. If we need + * keying data, we can ask for it directly. + */ +struct _krb5_db_entry_new; + +/* + * The function table / structure which an authdata server module must export as + * "authdata_server_0". NOTE: replace "0" with "1" for the type and + * variable names if this gets picked up by upstream. If the interfaces work + * correctly, future versions of the table will add either more callbacks or + * more arguments to callbacks, and in both cases we'll be able to wrap the v0 + * functions. + */ +/* extern krb5plugin_authdata_ftable_v0 authdata_server_0; */ +typedef struct krb5plugin_authdata_server_ftable_v0 { + /* Not-usually-visible name. */ + char *name; + + /* + * Per-plugin initialization/cleanup. The init function is called + * by the KDC when the plugin is loaded, and the fini function is + * called before the plugin is unloaded. Both are optional. + */ + krb5_error_code (*init_proc)(krb5_context, void **); + void (*fini_proc)(krb5_context, void *); + /* + * Actual authorization data handling function. If this field + * holds a null pointer, this mechanism will be skipped, and the + * init/fini functions will not be run. + * + * This function should only modify the field + * enc_tkt_reply->authorization_data. All other values should be + * considered inputs only. And, it should *modify* the field, not + * overwrite it and assume that there are no other authdata + * plugins in use. + * + * Memory management: authorization_data is a malloc-allocated, + * null-terminated sequence of malloc-allocated pointers to + * authorization data structures. This plugin code currently + * assumes the libraries, KDC, and plugin all use the same malloc + * pool, which may be a problem if/when we get the KDC code + * running on Windows. + * + * If this function returns a non-zero error code, a message + * is logged, but no other action is taken. Other authdata + * plugins will be called, and a response will be sent to the + * client (barring other problems). + */ + krb5_error_code (*authdata_proc)(krb5_context, + struct _krb5_db_entry_new *client, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_enc_tkt_part *enc_tkt_reply); +} krb5plugin_server_authdata_ftable_v0; + +typedef krb5plugin_server_authdata_ftable_v0 krb5plugin_authdata_ftable_v0; + +typedef struct krb5plugin_authdata_server_ftable_v2 { + /* Not-usually-visible name. */ + char *name; + + /* + * Per-plugin initialization/cleanup. The init function is called + * by the KDC when the plugin is loaded, and the fini function is + * called before the plugin is unloaded. Both are optional. + */ + krb5_error_code (*init_proc)(krb5_context, void **); + void (*fini_proc)(krb5_context, void *); + /* + * Actual authorization data handling function. If this field + * holds a null pointer, this mechanism will be skipped, and the + * init/fini functions will not be run. + * + * This function should only modify the field + * enc_tkt_reply->authorization_data. All other values should be + * considered inputs only. And, it should *modify* the field, not + * overwrite it and assume that there are no other authdata + * plugins in use. + * + * Memory management: authorization_data is a malloc-allocated, + * null-terminated sequence of malloc-allocated pointers to + * authorization data structures. This plugin code currently + * assumes the libraries, KDC, and plugin all use the same malloc + * pool, which may be a problem if/when we get the KDC code + * running on Windows. + * + * If this function returns a non-zero error code, a message + * is logged, but no other action is taken. Other authdata + * plugins will be called, and a response will be sent to the + * client (barring other problems). + */ + krb5_error_code (*authdata_proc)(krb5_context, + unsigned int flags, + struct _krb5_db_entry_new *client, + struct _krb5_db_entry_new *server, + struct _krb5_db_entry_new *tgs, + krb5_keyblock *client_key, + krb5_keyblock *server_key, + krb5_keyblock *tgs_key, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_const_principal for_user_princ, + krb5_enc_tkt_part *enc_tkt_request, + krb5_enc_tkt_part *enc_tkt_reply); +} krb5plugin_authdata_server_ftable_v2; + +typedef krb5plugin_authdata_server_ftable_v2 krb5plugin_authdata_ftable_v2; + +typedef krb5_error_code +(*authdata_client_plugin_init_proc)(krb5_context context, + void **plugin_context); + +#define AD_USAGE_AS_REQ 0x01 +#define AD_USAGE_TGS_REQ 0x02 +#define AD_USAGE_AP_REQ 0x04 +#define AD_USAGE_KDC_ISSUED 0x08 +#define AD_USAGE_MASK 0x0F +#define AD_INFORMATIONAL 0x10 + +struct _krb5_authdata_context; + +typedef void +(*authdata_client_plugin_flags_proc)(krb5_context kcontext, + void *plugin_context, + krb5_authdatatype ad_type, + krb5_flags *flags); + +typedef void +(*authdata_client_plugin_fini_proc)(krb5_context kcontext, + void *plugin_context); + +typedef krb5_error_code +(*authdata_client_request_init_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void **request_context); + +typedef void +(*authdata_client_request_fini_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context); + +typedef krb5_error_code +(*authdata_client_import_authdata_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_authdata **authdata, + krb5_boolean kdc_issued_flag, + krb5_const_principal issuer); + +typedef krb5_error_code +(*authdata_client_export_authdata_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_flags usage, + krb5_authdata ***authdata); + +typedef krb5_error_code +(*authdata_client_get_attribute_types_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_data **attrs); + +typedef krb5_error_code +(*authdata_client_get_attribute_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + const krb5_data *attribute, + krb5_boolean *authenticated, + krb5_boolean *complete, + krb5_data *value, + krb5_data *display_value, + int *more); + +typedef krb5_error_code +(*authdata_client_set_attribute_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_boolean complete, + const krb5_data *attribute, + const krb5_data *value); + +typedef krb5_error_code +(*authdata_client_delete_attribute_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + const krb5_data *attribute); + +typedef krb5_error_code +(*authdata_client_export_internal_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_boolean restrict_authenticated, + void **ptr); + +typedef void +(*authdata_client_free_internal_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + void *ptr); + +typedef krb5_error_code +(*authdata_client_verify_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + const krb5_auth_context *auth_context, + const krb5_keyblock *key, + const krb5_ap_req *req); + +typedef krb5_error_code +(*authdata_client_size_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + size_t *sizep); + +typedef krb5_error_code +(*authdata_client_externalize_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_octet **buffer, + size_t *lenremain); + +typedef krb5_error_code +(*authdata_client_internalize_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_octet **buffer, + size_t *lenremain); + +typedef krb5_error_code +(*authdata_client_copy_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + void *dst_plugin_context, + void *dst_request_context); + +typedef struct krb5plugin_authdata_client_ftable_v0 { + char *name; + krb5_authdatatype *ad_type_list; + authdata_client_plugin_init_proc init; + authdata_client_plugin_fini_proc fini; + authdata_client_plugin_flags_proc flags; + authdata_client_request_init_proc request_init; + authdata_client_request_fini_proc request_fini; + authdata_client_get_attribute_types_proc get_attribute_types; + authdata_client_get_attribute_proc get_attribute; + authdata_client_set_attribute_proc set_attribute; + authdata_client_delete_attribute_proc delete_attribute; + authdata_client_export_authdata_proc export_authdata; + authdata_client_import_authdata_proc import_authdata; + authdata_client_export_internal_proc export_internal; + authdata_client_free_internal_proc free_internal; + authdata_client_verify_proc verify; + authdata_client_size_proc size; + authdata_client_externalize_proc externalize; + authdata_client_internalize_proc internalize; + authdata_client_copy_proc copy; /* optional */ +} krb5plugin_authdata_client_ftable_v0; + +#endif /* KRB5_AUTHDATA_PLUGIN_H_INCLUDED */ diff --git a/configure.ac b/configure.ac index 9f95e44..43ca2ec 100644 --- a/configure.ac +++ b/configure.ac @@ -26,6 +26,23 @@ dnl TARGET_CFLAGS="-Wall -pedantic -pthread" dnl TARGET_LDFLAGS="" dnl esac +reauth=no +AC_ARG_ENABLE(reauth, + [ --enable-reauth whether to enable fast reauthentication protocol: yes/no; default no ], + [ if test "x$enableval" = "xyes" -o "x$enableval" = "xno" ; then + reauth=$enableval + else + echo "--enable-reauth argument must be yes or no" + exit -1 + fi + ]) + +if test "x$reauth" = "xyes" ; then + echo "Fast reauthentication protocol enabled" + TARGET_CFLAGS="$TARGET_CFLAGS -DGSSEAP_ENABLE_REAUTH" +fi +AM_CONDITIONAL(GSSEAP_ENABLE_REAUTH, test "$reauth" = "yes") + AC_SUBST(TARGET_CFLAGS) AC_SUBST(TARGET_LDFLAGS) AX_CHECK_KRB5 diff --git a/display_status.c b/display_status.c index 3b872dc..f931be4 100644 --- a/display_status.c +++ b/display_status.c @@ -40,7 +40,7 @@ gss_display_status(OM_uint32 *minor, OM_uint32 *message_context, gss_buffer_t status_string) { - OM_uint32 major, tmpMinor; + OM_uint32 major; krb5_context krbContext; const char *errMsg; diff --git a/eap_mech.c b/eap_mech.c index ea43ad3..26ee5ec 100644 --- a/eap_mech.c +++ b/eap_mech.c @@ -284,6 +284,11 @@ gssEapInit(void) major = gssEapLocalAttrProviderInit(&minor); assert(major == GSS_S_COMPLETE); + +#ifdef GSSEAP_ENABLE_REAUTH + major = gssEapReauthInitialize(&minor); + assert(major == GSS_S_COMPLETE); +#endif } static void diff --git a/export_sec_context.c b/export_sec_context.c index 5fc98aa..021e366 100644 --- a/export_sec_context.c +++ b/export_sec_context.c @@ -81,12 +81,14 @@ gssEapExportSecContext(OM_uint32 *minor, * contexts. */ if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) { + assert((ctx->flags & CTX_FLAG_KRB_REAUTH_GSS) == 0); + major = gssEapExportPartialContext(minor, ctx, &partialCtx); if (GSS_ERROR(major)) goto cleanup; } - length = 16; /* version, state, flags, etc */ + length = 16; /* version, state, flags, */ length += 4 + ctx->mechanismUsed->length; /* mechanismUsed */ length += 12 + key.length; /* rfc3961Key.value */ length += 4 + initiatorName.length; /* initiatorName.value */ diff --git a/gssapiP_eap.h b/gssapiP_eap.h index f93c105..b46c2bc 100644 --- a/gssapiP_eap.h +++ b/gssapiP_eap.h @@ -42,6 +42,7 @@ /* GSS includes */ #include +#include #include #include "gssapi_eap.h" @@ -91,17 +92,26 @@ struct gss_cred_id_struct { gss_OID_set mechanisms; time_t expiryTime; char *radiusConfigFile; +#ifdef GSSEAP_ENABLE_REAUTH + krb5_ccache krbCredCache; + gss_cred_id_t krbCred; +#endif }; #define CTX_FLAG_INITIATOR 0x00000001 +#define CTX_FLAG_KRB_REAUTH_GSS 0x00000002 #define CTX_IS_INITIATOR(ctx) (((ctx)->flags & CTX_FLAG_INITIATOR) != 0) enum gss_eap_state { EAP_STATE_IDENTITY = 0, EAP_STATE_AUTHENTICATE, - EAP_STATE_GSS_CHANNEL_BINDINGS, - EAP_STATE_ESTABLISHED + EAP_STATE_EXTENSIONS_REQ, + EAP_STATE_EXTENSIONS_RESP, + EAP_STATE_ESTABLISHED, +#ifdef GSSEAP_ENABLE_REAUTH + EAP_STATE_KRB_REAUTH_GSS +#endif }; #define CTX_IS_ESTABLISHED(ctx) ((ctx)->state == EAP_STATE_ESTABLISHED) @@ -153,6 +163,10 @@ struct gss_ctx_id_struct { #define initiatorCtx ctxU.initiator struct gss_eap_acceptor_ctx acceptor; #define acceptorCtx ctxU.acceptor +#ifdef GSSEAP_ENABLE_REAUTH + gss_ctx_id_t kerberos; + #define kerberosCtx ctxU.kerberos +#endif } ctxU; }; @@ -193,5 +207,16 @@ gssEapWrapIovLength(OM_uint32 *minor, int *conf_state, gss_iov_buffer_desc *iov, int iov_count); +OM_uint32 +gssEapWrap(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_message_buffer, + int *conf_state, + gss_buffer_t output_message_buffer); + +unsigned char +rfc4121Flags(gss_ctx_id_t ctx, int receiving); #endif /* _GSSAPIP_EAP_H_ */ diff --git a/import_sec_context.c b/import_sec_context.c index 8aeb29c..219bfe0 100644 --- a/import_sec_context.c +++ b/import_sec_context.c @@ -269,6 +269,8 @@ gssEapImportContext(OM_uint32 *minor, * acceptor contexts. */ if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) { + assert((ctx->flags & CTX_FLAG_KRB_REAUTH_GSS) == 0); + major = gssEapImportPartialContext(minor, &p, &remain, ctx); if (GSS_ERROR(major)) return major; diff --git a/init_sec_context.c b/init_sec_context.c index 5fcbec8..e3b4d63 100644 --- a/init_sec_context.c +++ b/init_sec_context.c @@ -32,6 +32,23 @@ #include "gssapiP_eap.h" +#ifdef GSSEAP_ENABLE_REAUTH +static int +canReauthP(gss_cred_id_t cred); + +static OM_uint32 +eapGssSmInitGssReauth(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); +#endif + static OM_uint32 policyVariableToFlag(enum eapol_bool_var variable) { @@ -287,35 +304,31 @@ initReady(OM_uint32 *minor, gss_ctx_id_t ctx) } static OM_uint32 -eapGssSmInitIdentity(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) +initBegin(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) { - time_t now; OM_uint32 major; - int initialContextToken; - initialContextToken = (inputToken == GSS_C_NO_BUFFER || - inputToken->length == 0); - if (!initialContextToken) - return GSS_S_DEFECTIVE_TOKEN; - - time(&now); - if (timeReq == 0 || timeReq == GSS_C_INDEFINITE) + if (cred != GSS_C_NO_CREDENTIAL && cred->expiryTime) + ctx->expiryTime = cred->expiryTime; + else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE) ctx->expiryTime = 0; else - ctx->expiryTime = now + timeReq; + ctx->expiryTime = time(NULL) + timeReq; - major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName); - if (GSS_ERROR(major)) - return major; + if (cred != GSS_C_NO_CREDENTIAL) { + major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName); + if (GSS_ERROR(major)) + return major; + } major = gssEapDuplicateName(minor, target, &ctx->acceptorName); if (GSS_ERROR(major)) @@ -336,6 +349,34 @@ eapGssSmInitIdentity(OM_uint32 *minor, if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) return GSS_S_BAD_MECH; + return GSS_S_COMPLETE; +} + +static OM_uint32 +eapGssSmInitIdentity(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) +{ + OM_uint32 major; + int initialContextToken; + + initialContextToken = (inputToken->length == 0); + if (!initialContextToken) + return GSS_S_DEFECTIVE_TOKEN; + + major = initBegin(minor, cred, ctx, target, mech, + reqFlags, timeReq, chanBindings, + inputToken, outputToken); + if (GSS_ERROR(major)) + return major; + ctx->state = EAP_STATE_AUTHENTICATE; return GSS_S_CONTINUE_NEEDED; @@ -404,7 +445,7 @@ eapGssSmInitAuthenticate(OM_uint32 *minor, ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS); major = GSS_S_CONTINUE_NEEDED; - ctx->state = EAP_STATE_GSS_CHANNEL_BINDINGS; + ctx->state = EAP_STATE_EXTENSIONS_REQ; } else if (ctx->flags & CTX_FLAG_EAP_FAIL) { major = GSS_S_DEFECTIVE_CREDENTIAL; } else if (code == 0 && initialContextToken) { @@ -438,55 +479,81 @@ cleanup: } static OM_uint32 -eapGssSmInitGssChannelBindings(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) +initGssChannelBindings(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_channel_bindings_t chanBindings, + gss_buffer_t outputToken) { OM_uint32 major; - gss_iov_buffer_desc iov[2]; - gss_buffer_desc buf; - - iov[0].type = GSS_IOV_BUFFER_TYPE_DATA; - iov[0].buffer.length = 0; - iov[0].buffer.value = NULL; + gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER; - iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE; - iov[1].buffer.length = 0; - iov[1].buffer.value = NULL; if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS) - iov[0].buffer = chanBindings->application_data; + buffer = chanBindings->application_data; - major = gssEapWrapOrGetMIC(minor, ctx, FALSE, FALSE, iov, 2, - TOK_TYPE_GSS_CB); + major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT, + &buffer, NULL, outputToken); if (GSS_ERROR(major)) - goto cleanup; + return major; - /* Skip past token ID */ - assert(iov[1].buffer.length > 2); - assert(load_uint16_be(iov[1].buffer.value) == TOK_TYPE_GSS_CB); + return GSS_S_CONTINUE_NEEDED; +} - buf.length = iov[1].buffer.length - 2; - buf.value = (unsigned char *)iov[1].buffer.value + 2; +static OM_uint32 +eapGssSmInitExtensionsReq(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) +{ + OM_uint32 major, tmpMinor; + gss_buffer_desc cbToken = GSS_C_EMPTY_BUFFER; - major = duplicateBuffer(minor, &buf, outputToken); + major = initGssChannelBindings(minor, ctx, chanBindings, &cbToken); if (GSS_ERROR(major)) - goto cleanup; + return major; - major = GSS_S_COMPLETE; - ctx->state = EAP_STATE_ESTABLISHED; + ctx->state = EAP_STATE_EXTENSIONS_RESP; -cleanup: - gssEapReleaseIov(iov, 2); + major = duplicateBuffer(minor, &cbToken, outputToken); + if (GSS_ERROR(major)) { + gss_release_buffer(&tmpMinor, &cbToken); + return major; + } - return major; + gss_release_buffer(&tmpMinor, &cbToken); + + return GSS_S_CONTINUE_NEEDED; +} + +static OM_uint32 +eapGssSmInitExtensionsResp(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) +{ +#ifdef GSSEAP_ENABLE_REAUTH + OM_uint32 major; + + major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken); + if (GSS_ERROR(major)) + return major; +#endif + + ctx->state = EAP_STATE_ESTABLISHED; + + return GSS_S_COMPLETE; } static OM_uint32 @@ -520,10 +587,14 @@ static struct gss_eap_initiator_sm { gss_buffer_t, gss_buffer_t); } eapGssInitiatorSm[] = { - { 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_NONE, TOK_TYPE_NONE, eapGssSmInitEstablished }, + { TOK_TYPE_NONE, TOK_TYPE_EAP_RESP, eapGssSmInitIdentity }, + { TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP, eapGssSmInitAuthenticate }, + { TOK_TYPE_NONE, TOK_TYPE_EXT_REQ, eapGssSmInitExtensionsReq }, + { TOK_TYPE_EXT_RESP,TOK_TYPE_NONE, eapGssSmInitExtensionsResp }, + { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmInitEstablished }, +#ifdef GSSEAP_ENABLE_REAUTH + { TOK_TYPE_GSS_REAUTH, TOK_TYPE_GSS_REAUTH, eapGssSmInitGssReauth }, +#endif }; OM_uint32 @@ -547,6 +618,7 @@ gss_init_sec_context(OM_uint32 *minor, struct gss_eap_initiator_sm *sm = NULL; gss_buffer_desc innerInputToken; gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER; + enum gss_eap_token_type tokType; *minor = 0; @@ -568,6 +640,11 @@ gss_init_sec_context(OM_uint32 *minor, ctx->flags |= CTX_FLAG_INITIATOR; +#ifdef GSSEAP_ENABLE_REAUTH + if (canReauthP(cred)) + ctx->state = EAP_STATE_KRB_REAUTH_GSS; +#endif + *context_handle = ctx; } @@ -577,9 +654,14 @@ gss_init_sec_context(OM_uint32 *minor, if (input_token != GSS_C_NO_BUFFER) { major = gssEapVerifyToken(minor, ctx, input_token, - sm->inputTokenType, &innerInputToken); + &tokType, &innerInputToken); if (GSS_ERROR(major)) goto cleanup; + + if (tokType != sm->inputTokenType) { + major = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } } else { innerInputToken.length = 0; innerInputToken.value = NULL; @@ -636,3 +718,79 @@ cleanup: return major; } + +#ifdef GSSEAP_ENABLE_REAUTH +static int +canReauthP(gss_cred_id_t cred) +{ + return (cred != GSS_C_NO_CREDENTIAL && + cred->krbCred != GSS_C_NO_CREDENTIAL && + cred->expiryTime > time(NULL)); +} + +static OM_uint32 +eapGssSmInitGssReauth(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) +{ + OM_uint32 major, tmpMinor; + gss_name_t mechTarget = GSS_C_NO_NAME; + gss_OID actualMech = GSS_C_NO_OID; + OM_uint32 gssFlags, timeRec; + + assert(cred != GSS_C_NO_CREDENTIAL); + + ctx->flags |= CTX_FLAG_KRB_REAUTH_GSS; + + if (inputToken->length == 0) { + major = initBegin(minor, cred, ctx, target, mech, + reqFlags, timeReq, chanBindings, + inputToken, outputToken); + if (GSS_ERROR(major)) + goto cleanup; + } + + major = gssEapMechToGlueName(minor, target, &mechTarget); + if (GSS_ERROR(major)) + goto cleanup; + + major = gssInitSecContext(minor, + cred->krbCred, + &ctx->kerberosCtx, + mechTarget, + (gss_OID)gss_mech_krb5, + reqFlags, /* | GSS_C_DCE_STYLE, */ + timeReq, + chanBindings, + inputToken, + &actualMech, + outputToken, + &gssFlags, + &timeRec); + if (GSS_ERROR(major)) + goto cleanup; + + ctx->gssFlags = gssFlags; + + if (major == GSS_S_COMPLETE) { + major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec); + if (GSS_ERROR(major)) + goto cleanup; + ctx->state = EAP_STATE_ESTABLISHED; + } + +cleanup: + gssReleaseName(&tmpMinor, &mechTarget); + + return major; +} +#endif /* GSSEAP_ENABLE_REAUTH */ + + diff --git a/mech b/mech index 89c5512..0b0fe7f 100644 --- a/mech +++ b/mech @@ -3,8 +3,8 @@ # Any encryption type supported by Kerberos can be defined as the # last element of the OID arc. # -eap 1.3.6.1.4.1.5322.21.1 libmech_eap.dylib -eap-des3-cbc-sha1 1.3.6.1.4.1.5322.21.1.16 libmech_eap.dylib -eap-aes128 1.3.6.1.4.1.5322.21.1.17 libmech_eap.dylib -eap-aes256 1.3.6.1.4.1.5322.21.1.18 libmech_eap.dylib -eap-rc4-hmac 1.3.6.1.4.1.5322.21.1.23 libmech_eap.dylib +eap 1.3.6.1.4.1.5322.21.1 mech_eap.so +eap-des3-cbc-sha1 1.3.6.1.4.1.5322.21.1.16 mech_eap.so +eap-aes128 1.3.6.1.4.1.5322.21.1.17 mech_eap.so +eap-aes256 1.3.6.1.4.1.5322.21.1.18 mech_eap.so +eap-rc4-hmac 1.3.6.1.4.1.5322.21.1.23 mech_eap.so diff --git a/radius_ad.exports b/radius_ad.exports new file mode 100644 index 0000000..8d5d5c4 --- /dev/null +++ b/radius_ad.exports @@ -0,0 +1 @@ +authdata_client_0 diff --git a/set_cred_option.c b/set_cred_option.c index 48d2ceb..44ebebc 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/store_cred.c b/store_cred.c index 4ddac48..b1e5e54 100644 --- a/store_cred.c +++ b/store_cred.c @@ -42,6 +42,27 @@ gss_store_cred(OM_uint32 *minor, gss_OID_set *elements_stored, gss_cred_usage_t *cred_usage_stored) { + if (elements_stored != NULL) + *elements_stored = GSS_C_NO_OID_SET; + if (cred_usage_stored != NULL) + *cred_usage_stored = input_usage; + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED; + +#ifdef GSSEAP_ENABLE_REAUTH + if (input_cred_handle->krbCred != GSS_C_NO_CREDENTIAL) { + return gssStoreCred(minor, + input_cred_handle->krbCred, + input_usage, + gss_mech_krb5, + overwrite_cred, + default_cred, + elements_stored, + cred_usage_stored); + } +#endif + *minor = 0; return GSS_S_UNAVAILABLE; } diff --git a/unwrap.c b/unwrap.c index ca10b32..844c762 100644 --- a/unwrap.c +++ b/unwrap.c @@ -43,6 +43,9 @@ gss_unwrap(OM_uint32 *minor, OM_uint32 major, tmpMinor; gss_iov_buffer_desc iov[2]; + if (!CTX_IS_ESTABLISHED(ctx)) + return GSS_S_NO_CONTEXT; + iov[0].type = GSS_IOV_BUFFER_TYPE_STREAM; iov[0].buffer = *input_message_buffer; diff --git a/unwrap_iov.c b/unwrap_iov.c index 1624308..7e6eb87 100644 --- a/unwrap_iov.c +++ b/unwrap_iov.c @@ -73,7 +73,7 @@ unwrapToken(OM_uint32 *minor, gss_iov_buffer_t header; gss_iov_buffer_t padding; gss_iov_buffer_t trailer; - unsigned char acceptorFlag; + unsigned char flags; unsigned char *ptr = NULL; int keyUsage; size_t rrc, ec; @@ -99,40 +99,27 @@ unwrapToken(OM_uint32 *minor, trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); - acceptorFlag = CTX_IS_INITIATOR(ctx) ? TOK_FLAG_SENDER_IS_ACCEPTOR : 0; - switch (toktype) { - case TOK_TYPE_WRAP: + flags = rfc4121Flags(ctx, TRUE); + + if (toktype == TOK_TYPE_WRAP) { keyUsage = !CTX_IS_INITIATOR(ctx) ? KEY_USAGE_INITIATOR_SEAL : KEY_USAGE_ACCEPTOR_SEAL; - break; - case TOK_TYPE_GSS_CB: - keyUsage = KEY_USAGE_CHANNEL_BINDINGS; - break; - case TOK_TYPE_MIC: - default: + } else { keyUsage = !CTX_IS_INITIATOR(ctx) ? KEY_USAGE_INITIATOR_SIGN : KEY_USAGE_ACCEPTOR_SIGN; - break; } gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen); ptr = (unsigned char *)header->buffer.value; - if (header->buffer.length < 16) { - *minor = 0; + if (header->buffer.length < 16) return GSS_S_DEFECTIVE_TOKEN; - } - if ((ptr[2] & TOK_FLAG_SENDER_IS_ACCEPTOR) != acceptorFlag) { + if ((ptr[2] & flags) != flags) return GSS_S_BAD_SIG; - } - - if (ptr[2] & TOK_FLAG_ACCEPTOR_SUBKEY) { - return GSS_S_BAD_SIG; - } if (toktype == TOK_TYPE_WRAP) { unsigned int krbTrailerLen; @@ -219,7 +206,7 @@ unwrapToken(OM_uint32 *minor, } code = sequenceCheck(minor, &ctx->seqState, seqnum); - } else if (toktype == TOK_TYPE_MIC || toktype == TOK_TYPE_GSS_CB) { + } else if (toktype == TOK_TYPE_MIC) { if (load_uint16_be(ptr) != toktype) goto defective; @@ -235,8 +222,7 @@ unwrapToken(OM_uint32 *minor, *minor = code; return GSS_S_BAD_SIG; } - if (toktype != TOK_TYPE_GSS_CB) - code = sequenceCheck(minor, &ctx->seqState, seqnum); + code = sequenceCheck(minor, &ctx->seqState, seqnum); } else if (toktype == TOK_TYPE_DELETE_CONTEXT) { if (load_uint16_be(ptr) != TOK_TYPE_DELETE_CONTEXT) goto defective; @@ -307,7 +293,7 @@ unwrapStream(OM_uint32 *minor, assert(toktype == TOK_TYPE_WRAP); - if (toktype != TOK_TYPE_WRAP || (ctx->gssFlags & GSS_C_DCE_STYLE)) { + if (toktype != TOK_TYPE_WRAP) { code = EINVAL; goto cleanup; } @@ -477,9 +463,6 @@ gssEapUnwrapOrVerifyMIC(OM_uint32 *minor, { OM_uint32 major; - if (!CTX_IS_ESTABLISHED(ctx)) - return GSS_S_NO_CONTEXT; - if (ctx->encryptionType == ENCTYPE_NULL) return GSS_S_UNAVAILABLE; @@ -502,6 +485,9 @@ gss_unwrap_iov(OM_uint32 *minor, gss_iov_buffer_desc *iov, int iov_count) { + if (!CTX_IS_ESTABLISHED(ctx)) + return GSS_S_NO_CONTEXT; + return gssEapUnwrapOrVerifyMIC(minor, ctx, conf_state, qop_state, iov, iov_count, TOK_TYPE_WRAP); } diff --git a/util.h b/util.h index 74641c7..6b47283 100644 --- a/util.h +++ b/util.h @@ -88,7 +88,9 @@ enum gss_eap_token_type { TOK_TYPE_DELETE_CONTEXT = 0x0405, /* RFC 2743 delete context */ 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_EXT_REQ = 0x0603, /* draft-howlett-eap-gss */ + TOK_TYPE_EXT_RESP = 0x0604, /* to be specified */ + TOK_TYPE_GSS_REAUTH = 0x0605, /* to be specified */ }; #define EAP_EXPORT_CONTEXT_V1 1 @@ -169,7 +171,7 @@ OM_uint32 gssEapVerifyToken(OM_uint32 *minor, gss_ctx_id_t ctx, const gss_buffer_t inputToken, - enum gss_eap_token_type tokenType, + enum gss_eap_token_type *tokenType, gss_buffer_t innerInputToken); OM_uint32 @@ -396,7 +398,7 @@ verifyTokenHeader(OM_uint32 *minor, size_t *body_size, unsigned char **buf_in, size_t toksize_in, - enum gss_eap_token_type tok_type); + enum gss_eap_token_type *ret_tok_type); /* Helper macros */ #define GSSEAP_CALLOC(count, size) (calloc((count), (size))) @@ -547,10 +549,20 @@ 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 #include "util_attr.h" +#ifdef GSSEAP_ENABLE_REAUTH +#include "util_reauth.h" +#endif #endif /* _UTIL_H_ */ diff --git a/util_adshim.c b/util_adshim.c new file mode 100644 index 0000000..c0b6837 --- /dev/null +++ b/util_adshim.c @@ -0,0 +1,237 @@ +/* + * 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" +#include "authdata_plugin.h" + +/* + * This rubbish is necessary because MIT doesn't provide another way + * to access verified AD-KDCIssued elements. We can't verify them + * ourselves because they're signed in the ticket session key, which + * is destroyed immediately after the AP-REQ is processed. + */ + +struct radius_ad_context { + krb5_data avpdata; + krb5_boolean verified; +}; + +static krb5_data radius_ad_attr = { + KV5M_DATA, sizeof("urn:authdata-radius-avp") - 1, "urn:authdata-radius-avp" }; + +static krb5_error_code +radius_ad_init(krb5_context kcontext, void **plugin_context) +{ + *plugin_context = 0; + return 0; +} + +static void +radius_ad_flags(krb5_context kcontext, + void *plugin_context, + krb5_authdatatype ad_type, + krb5_flags *flags) +{ + *flags = AD_USAGE_KDC_ISSUED | AD_INFORMATIONAL; +} + +static void +radius_ad_fini(krb5_context kcontext, void *plugin_context) +{ + return; +} + +static krb5_error_code +radius_ad_request_init(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void **request_context) +{ + struct radius_ad_context *ctx; + + ctx = GSSEAP_CALLOC(1, sizeof(*ctx)); + if (ctx == NULL) + return ENOMEM; + + *request_context = ctx; + + return 0; +} + +static krb5_error_code +radius_ad_export_authdata(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_flags usage, + krb5_authdata ***out_authdata) +{ + struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context; + krb5_authdata *data[2]; + krb5_authdata datum; + + datum.ad_type = KRB5_AUTHDATA_RADIUS_AVP; + datum.length = radius_ad->avpdata.length; + datum.contents = (krb5_octet *)radius_ad->avpdata.data; + + data[0] = &datum; + data[1] = NULL; + + return krb5_copy_authdata(kcontext, data, out_authdata); +} + +static krb5_error_code +radius_ad_import_authdata(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_authdata **authdata, + krb5_boolean kdc_issued_flag, + krb5_const_principal issuer) +{ + struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context; + + krb5_free_data_contents(kcontext, &radius_ad->avpdata); + radius_ad->verified = FALSE; + + assert(authdata[0] != NULL); + + radius_ad->avpdata.data = GSSEAP_MALLOC(authdata[0]->length); + if (radius_ad->avpdata.data == NULL) + return ENOMEM; + + memcpy(radius_ad->avpdata.data, authdata[0]->contents, + authdata[0]->length); + radius_ad->avpdata.length = authdata[0]->length; + + radius_ad->verified = kdc_issued_flag; + + return 0; +} + +static void +radius_ad_request_fini(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context) +{ + struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context; + + if (radius_ad != NULL) { + krb5_free_data_contents(kcontext, &radius_ad->avpdata); + GSSEAP_FREE(radius_ad); + } +} + +static krb5_error_code +radius_ad_get_attribute(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + const krb5_data *attribute, + krb5_boolean *authenticated, + krb5_boolean *complete, + krb5_data *value, + krb5_data *display_value, + int *more) +{ + struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context; + + if (attribute->length != radius_ad_attr.length || + memcmp(attribute->data, radius_ad_attr.data, + radius_ad_attr.length) != 0) + return ENOENT; + + *authenticated = radius_ad->verified; + *complete = TRUE; + *more = 0; + + value->data = GSSEAP_MALLOC(radius_ad->avpdata.length); + if (value->data == NULL) + return ENOMEM; + + memcpy(value->data, radius_ad->avpdata.data, radius_ad->avpdata.length); + value->length = radius_ad->avpdata.length; + + return 0; +} + +static krb5_error_code +radius_ad_copy(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + void *dst_plugin_context, + void *dst_request_context) +{ + struct radius_ad_context *radius_ad_src = + (struct radius_ad_context *)request_context; + struct radius_ad_context *radius_ad_dst = + (struct radius_ad_context *)dst_request_context; + + radius_ad_dst->avpdata.data = GSSEAP_MALLOC(radius_ad_src->avpdata.length); + if (radius_ad_dst->avpdata.data == NULL) + return ENOMEM; + + memcpy(radius_ad_dst->avpdata.data, radius_ad_src->avpdata.data, + radius_ad_src->avpdata.length); + radius_ad_dst->avpdata.length = radius_ad_src->avpdata.length; + radius_ad_dst->verified = radius_ad_src->verified; + + return 0; +} + +static krb5_authdatatype radius_ad_ad_types[] = + { KRB5_AUTHDATA_RADIUS_AVP, 0 }; + +krb5plugin_authdata_client_ftable_v0 authdata_client_0 = { + "radius_ad", + radius_ad_ad_types, + radius_ad_init, + radius_ad_fini, + radius_ad_flags, + radius_ad_request_init, + radius_ad_request_fini, + NULL, + radius_ad_get_attribute, + NULL, + NULL, + radius_ad_export_authdata, + radius_ad_import_authdata, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + radius_ad_copy +}; diff --git a/util_context.c b/util_context.c index a96b452..37edb63 100644 --- a/util_context.c +++ b/util_context.c @@ -106,6 +106,11 @@ gssEapReleaseContext(OM_uint32 *minor, gssEapKerberosInit(&tmpMinor, &krbContext); +#ifdef GSSEAP_ENABLE_REAUTH + if (ctx->flags & CTX_FLAG_KRB_REAUTH_GSS) { + gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER); + } else +#endif if (CTX_IS_INITIATOR(ctx)) { releaseInitiatorContext(&ctx->initiatorCtx); } else { @@ -156,7 +161,7 @@ OM_uint32 gssEapVerifyToken(OM_uint32 *minor, gss_ctx_id_t ctx, const gss_buffer_t inputToken, - enum gss_eap_token_type tokenType, + enum gss_eap_token_type *actualToken, gss_buffer_t innerInputToken) { OM_uint32 major; @@ -174,9 +179,9 @@ gssEapVerifyToken(OM_uint32 *minor, } major = verifyTokenHeader(minor, oid, &bodySize, &p, - inputToken->length, tokenType); + inputToken->length, actualToken); if (GSS_ERROR(major)) - return GSS_S_DEFECTIVE_TOKEN; + return major; if (ctx->mechanismUsed == GSS_C_NO_OID) { if (!gssEapIsConcreteMechanismOid(oid)) diff --git a/util_cred.c b/util_cred.c index 176bb1e..3893ff6 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) { @@ -78,6 +81,13 @@ gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred) if (cred->radiusConfigFile != NULL) GSSEAP_FREE(cred->radiusConfigFile); +#ifdef GSSEAP_ENABLE_REAUTH + if (cred->krbCredCache != NULL) + krb5_cc_destroy(krbContext, cred->krbCredCache); + if (cred->krbCred != GSS_C_NO_CREDENTIAL) + gssReleaseCred(&tmpMinor, &cred->krbCred); +#endif + GSSEAP_MUTEX_DESTROY(&cred->mutex); memset(cred, 0, sizeof(*cred)); GSSEAP_FREE(cred); diff --git a/util_name.c b/util_name.c index f4a6338..6063e7e 100644 --- a/util_name.c +++ b/util_name.c @@ -514,7 +514,7 @@ gssEapDisplayName(OM_uint32 *minor, gss_buffer_t output_name_buffer, gss_OID *output_name_type) { - OM_uint32 major, tmpMinor; + OM_uint32 major; krb5_context krbContext; char *krbName; diff --git a/util_reauth.c b/util_reauth.c new file mode 100644 index 0000000..a552fcb --- /dev/null +++ b/util_reauth.c @@ -0,0 +1,837 @@ +/* + * 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" + +#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 = NULL; + + *princ = NULL; + memset(key, 0, sizeof(*key)); + + 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) + break; + else + krb5_free_keytab_entry_contents(krbContext, &ktent); + } + } + + if (code == 0) { + *princ = ktent.principal; + *key = ktent.key; + } + +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) + krb5_free_keytab_entry_contents(krbContext, &ktent); + + return code; +} + +static OM_uint32 +freezeAttrContext(OM_uint32 *minor, + gss_name_t initiatorName, + krb5_const_principal acceptorPrinc, + krb5_keyblock *session, + krb5_authdata ***authdata) +{ + OM_uint32 major, tmpMinor; + krb5_error_code code; + gss_buffer_desc attrBuf = GSS_C_EMPTY_BUFFER; + krb5_authdata *authData[2], authDatum = { 0 }; + krb5_context krbContext; + + GSSEAP_KRB_INIT(&krbContext); + + major = gssEapExportAttrContext(minor, initiatorName, &attrBuf); + if (GSS_ERROR(major)) + return major; + + authDatum.ad_type = KRB5_AUTHDATA_RADIUS_AVP; + authDatum.length = attrBuf.length; + authDatum.contents = attrBuf.value; + authData[0] = &authDatum; + authData[1] = NULL; + + code = krb5_make_authdata_kdc_issued(krbContext, session, acceptorPrinc, + authData, authdata); + 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_ticket ticket = { 0 }; + krb5_keyblock session = { 0 }, acceptorKey = { 0 }; + krb5_enc_tkt_part enc_part = { 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 == KRB5_KT_NOTFOUND) { + gss_buffer_desc emptyToken = { 0, "" }; + + /* + * If we can't produce the KRB-CRED message, we need to + * return an empty (not NULL) token to the caller so we + * don't change the number of authentication legs. + */ + return duplicateBuffer(minor, &emptyToken, credBuf); + } else if (code != 0) + goto cleanup; + + enc_part.flags = TKT_FLG_INITIAL; + + /* + * 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); + 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 = freezeAttrContext(minor, ctx->initiatorName, ticket.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; + + 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 = enc_part.authorization_data; + + 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 = krb5_mk_1cred(krbContext, authContext, &creds, &credsData, NULL); + if (code != 0) + goto cleanup; + + krbDataToGssBuffer(credsData, credBuf); + +cleanup: + 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); + krb5_free_data(krbContext, ticketData); + krb5_auth_con_free(krbContext, authContext); + krb5_free_authdata(krbContext, enc_part.authorization_data); + if (credsData != NULL) + GSSEAP_FREE(credsData); + + 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 (krb5_princ_size(krbContext, principal) == 2 && + krb5_princ_component(krbContext, principal, 0)->length == 6 && + memcmp(krb5_princ_component(krbContext, + principal, 0)->data, "krbtgt", 6) == 0) + return TRUE; + + return FALSE; +} + +/* + * Store re-authentication (Kerberos) credentials in a credential handle. + */ +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, code; + krb5_context krbContext = NULL; + krb5_auth_context authContext = NULL; + krb5_data credData = { 0 }; + krb5_creds **creds = NULL; + krb5_principal canonPrinc; + 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; + + cred->expiryTime = creds[0]->times.endtime; + + code = krb5_cc_new_unique(krbContext, "MEMORY", NULL, &cred->krbCredCache); + if (code != 0) + goto cleanup; + + 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; + } + + /* + * To turn a credentials cache into a GSS credentials handle, we + * require the gss_krb5_import_cred() API (present in Heimdal, but + * not shipped in MIT yet). + */ + major = gss_krb5_import_cred(minor, cred->krbCredCache, NULL, NULL, + &cred->krbCred); + if (GSS_ERROR(major)) + goto cleanup; + +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; +} + +static gss_buffer_desc radiusAvpKrbAttr = { + sizeof("urn:authdata-radius-avp") - 1, "urn:authdata-radius-avp" +}; + +/* + * 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_name_t glueName, + gss_name_t mechName) +{ + OM_uint32 major, tmpMinor; + gss_buffer_desc authData = GSS_C_EMPTY_BUFFER; + gss_buffer_desc authDataDisplay = GSS_C_EMPTY_BUFFER; + int more = -1; + int authenticated, complete; + + 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); + + 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_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, 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; + + if (!oidEqual(mech, gss_mech_krb5)) { + major = GSS_S_BAD_MECH; + goto cleanup; + } + + /* Get the raw subsession key and encryptino type*/ + major = gssInquireSecContextByOid(minor, ctx->kerberosCtx, + GSS_C_INQ_SSPI_SESSION_KEY, &keyData); + if (GSS_ERROR(major)) + goto cleanup; + + { + 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_context krbContext = NULL; + krb5_keyblock key; + + GSSEAP_KRB_INIT(&krbContext); + + 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; + } + } + + 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: + 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) ((local) = dlsym(RTLD_NEXT, (global))) + +OM_uint32 +gssEapReauthInitialize(OM_uint32 *minor) +{ + 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"); + NEXT_SYMBOL(gssGetNameAttributeNext, "gss_get_name_attribute"); + + return GSS_S_COMPLETE; +} + +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) + 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) + 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) + return GSS_S_UNAVAILABLE; + + return gssReleaseCredNext(minor, cred_handle); +} + +OM_uint32 +gssReleaseName(OM_uint32 *minor, + gss_name_t *name) +{ + if (gssReleaseName == NULL) + 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) + 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) + 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) + 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) + 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) + 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) + return GSS_S_UNAVAILABLE; + + return gssGetNameAttributeNext(minor, name, attr, authenticated, complete, + value, display_value, more); +} diff --git a/util_reauth.h b/util_reauth.h new file mode 100644 index 0000000..dca9bf0 --- /dev/null +++ b/util_reauth.h @@ -0,0 +1,140 @@ +/* + * 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" + +#ifndef _UTIL_REAUTH_H_ +#define _UTIL_REAUTH_H_ 1 + +#define KRB5_AUTHDATA_RADIUS_AVP 513 + +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); + +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); + +OM_uint32 +gssReleaseCred(OM_uint32 *minor, + gss_cred_id_t *cred_handle); + +OM_uint32 +gssReleaseName(OM_uint32 *minor, + gss_name_t *name); + +OM_uint32 +gssDeleteSecContext(OM_uint32 *minor, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token); + +OM_uint32 +gssInquireSecContextByOid(OM_uint32 *minor, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *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); + +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); + +OM_uint32 +gssEapMakeReauthCreds(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t credBuf); + +OM_uint32 +gssEapStoreReauthCreds(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t credBuf); + + +OM_uint32 +gssEapGlueToMechName(OM_uint32 *minor, + gss_name_t glueName, + gss_name_t *pMechName); + +OM_uint32 +gssEapMechToGlueName(OM_uint32 *minor, + gss_name_t mechName, + gss_name_t *pGlueName); + +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 +gssEapReauthInitialize(OM_uint32 *minor); + +#endif /* _UTIL_REAUTH_H_ */ diff --git a/util_token.c b/util_token.c index 7f3d3c2..02d6557 100644 --- a/util_token.c +++ b/util_token.c @@ -202,7 +202,7 @@ verifyTokenHeader(OM_uint32 *minor, size_t *body_size, unsigned char **buf_in, size_t toksize_in, - enum gss_eap_token_type tok_type) + enum gss_eap_token_type *ret_tok_type) { unsigned char *buf = *buf_in; ssize_t seqsize; @@ -211,6 +211,9 @@ verifyTokenHeader(OM_uint32 *minor, *minor = 0; + if (ret_tok_type != NULL) + *ret_tok_type = TOK_TYPE_NONE; + if ((toksize -= 1) < 0) return GSS_S_DEFECTIVE_TOKEN; @@ -249,13 +252,12 @@ verifyTokenHeader(OM_uint32 *minor, return GSS_S_BAD_MECH; } - if (tok_type != TOK_TYPE_NONE) { + if (ret_tok_type != NULL) { if ((toksize -= 2) < 0) return GSS_S_DEFECTIVE_TOKEN; - if ((*buf++ != ((tok_type >> 8) & 0xff)) || - (*buf++ != (tok_type & 0xff))) - return GSS_S_DEFECTIVE_TOKEN; + *ret_tok_type = load_uint16_be(buf); + buf += 2; } *buf_in = buf; *body_size = toksize; diff --git a/wrap.c b/wrap.c index 4208e91..f6542b7 100644 --- a/wrap.c +++ b/wrap.c @@ -41,13 +41,28 @@ gss_wrap(OM_uint32 *minor, int *conf_state, gss_buffer_t output_message_buffer) { + if (!CTX_IS_ESTABLISHED(ctx)) + return GSS_S_NO_CONTEXT; + + return gssEapWrap(minor, ctx, conf_req_flag, qop_req, + input_message_buffer, + conf_state, output_message_buffer); +} + +OM_uint32 +gssEapWrap(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_message_buffer, + int *conf_state, + gss_buffer_t output_message_buffer) +{ OM_uint32 major, tmpMinor; gss_iov_buffer_desc iov[4]; unsigned char *p; int i; - if (!CTX_IS_ESTABLISHED(ctx)) - return GSS_S_NO_CONTEXT; iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; iov[0].buffer.value = NULL; diff --git a/wrap_iov.c b/wrap_iov.c index 9a10fe3..ae43ef9 100644 --- a/wrap_iov.c +++ b/wrap_iov.c @@ -55,6 +55,27 @@ #include "gssapiP_eap.h" +unsigned char +rfc4121Flags(gss_ctx_id_t ctx, int receiving) +{ + unsigned char flags; + int isAcceptor; + + isAcceptor = !CTX_IS_INITIATOR(ctx); + if (receiving) + isAcceptor = !isAcceptor; + + flags = 0; + if (isAcceptor) + flags |= TOK_FLAG_SENDER_IS_ACCEPTOR; + + if ((ctx->flags & CTX_FLAG_KRB_REAUTH_GSS) && + (ctx->gssFlags & GSS_C_MUTUAL_FLAG)) + flags |= TOK_FLAG_ACCEPTOR_SUBKEY; + + return flags; +} + OM_uint32 gssEapWrapOrGetMIC(OM_uint32 *minor, gss_ctx_id_t ctx, @@ -68,7 +89,7 @@ gssEapWrapOrGetMIC(OM_uint32 *minor, gss_iov_buffer_t header; gss_iov_buffer_t padding; gss_iov_buffer_t trailer; - unsigned char acceptorFlag; + unsigned char flags; unsigned char *outbuf = NULL; unsigned char *tbuf = NULL; int keyUsage; @@ -82,23 +103,16 @@ gssEapWrapOrGetMIC(OM_uint32 *minor, GSSEAP_KRB_INIT(&krbContext); - acceptorFlag = CTX_IS_INITIATOR(ctx) ? 0 : TOK_FLAG_SENDER_IS_ACCEPTOR; + flags = rfc4121Flags(ctx, FALSE); - switch (toktype) { - case TOK_TYPE_WRAP: + if (toktype == TOK_TYPE_WRAP) { keyUsage = CTX_IS_INITIATOR(ctx) ? KEY_USAGE_INITIATOR_SEAL : KEY_USAGE_ACCEPTOR_SEAL; - break; - case TOK_TYPE_GSS_CB: - keyUsage = KEY_USAGE_CHANNEL_BINDINGS; - break; - case TOK_TYPE_MIC: - default: + } else { keyUsage = CTX_IS_INITIATOR(ctx) ? KEY_USAGE_INITIATOR_SIGN : KEY_USAGE_ACCEPTOR_SIGN; - break; } gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen); @@ -177,9 +191,8 @@ gssEapWrapOrGetMIC(OM_uint32 *minor, /* TOK_ID */ store_uint16_be((uint16_t)toktype, outbuf); /* flags */ - outbuf[2] = (acceptorFlag - | (conf_req_flag ? TOK_FLAG_WRAP_CONFIDENTIAL : 0) - | (0 ? TOK_FLAG_ACCEPTOR_SUBKEY : 0)); + outbuf[2] = flags + | (conf_req_flag ? TOK_FLAG_WRAP_CONFIDENTIAL : 0); /* filler */ outbuf[3] = 0xFF; /* EC */ @@ -251,8 +264,7 @@ gssEapWrapOrGetMIC(OM_uint32 *minor, /* TOK_ID */ store_uint16_be((uint16_t)toktype, outbuf); /* flags */ - outbuf[2] = (acceptorFlag - | (0 ? TOK_FLAG_ACCEPTOR_SUBKEY : 0)); + outbuf[2] = flags; /* filler */ outbuf[3] = 0xFF; if (toktype == TOK_TYPE_WRAP) { @@ -276,8 +288,7 @@ gssEapWrapOrGetMIC(OM_uint32 *minor, if (code != 0) goto cleanup; - if (toktype != TOK_TYPE_GSS_CB) - ctx->sendSeq++; + ctx->sendSeq++; if (toktype == TOK_TYPE_WRAP) { /* Fix up EC field */ @@ -285,7 +296,7 @@ gssEapWrapOrGetMIC(OM_uint32 *minor, /* Fix up RRC field */ store_uint16_be(rrc, outbuf + 6); } - } else if (toktype == TOK_TYPE_MIC || toktype == TOK_TYPE_GSS_CB) { + } else if (toktype == TOK_TYPE_MIC) { trailer = NULL; goto wrap_with_checksum; } else if (toktype == TOK_TYPE_DELETE_CONTEXT) { diff --git a/wrap_iov_length.c b/wrap_iov_length.c index c451cb2..33a15ef 100644 --- a/wrap_iov_length.c +++ b/wrap_iov_length.c @@ -80,8 +80,8 @@ gssEapWrapIovLength(OM_uint32 *minor, if (qop_req != GSS_C_QOP_DEFAULT) return GSS_S_FAILURE; - if (!CTX_IS_ESTABLISHED(ctx)) - return GSS_S_NO_CONTEXT; + if (ctx->encryptionType == ENCTYPE_NULL) + return GSS_S_UNAVAILABLE; GSSEAP_KRB_INIT(&krbContext);