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 \
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
- 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
#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;
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))
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;
}
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
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
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;
if (GSS_ERROR(major))
return major;
+ initialContextToken = 1;
*context_handle = ctx;
}
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;
}
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)) {
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 */
--- /dev/null
+/* -*- 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 <krb5/krb5.h>
+
+/*
+ * 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 */
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
OM_uint32 *message_context,
gss_buffer_t status_string)
{
- OM_uint32 major, tmpMinor;
+ OM_uint32 major;
krb5_context krbContext;
const char *errMsg;
major = gssEapLocalAttrProviderInit(&minor);
assert(major == GSS_S_COMPLETE);
+
+#ifdef GSSEAP_ENABLE_REAUTH
+ major = gssEapReauthInitialize(&minor);
+ assert(major == GSS_S_COMPLETE);
+#endif
}
static void
* 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 */
/* GSS includes */
#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
#include <gssapi/gssapi_ext.h>
#include "gssapi_eap.h"
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)
#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;
};
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_ */
* 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;
#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)
{
}
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))
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;
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) {
}
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
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
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;
ctx->flags |= CTX_FLAG_INITIATOR;
+#ifdef GSSEAP_ENABLE_REAUTH
+ if (canReauthP(cred))
+ ctx->state = EAP_STATE_KRB_REAUTH_GSS;
+#endif
+
*context_handle = ctx;
}
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;
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 */
+
+
# 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
--- /dev/null
+authdata_client_0
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;
}
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;
}
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;
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;
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;
}
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;
*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;
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;
}
{
OM_uint32 major;
- if (!CTX_IS_ESTABLISHED(ctx))
- return GSS_S_NO_CONTEXT;
-
if (ctx->encryptionType == ENCTYPE_NULL)
return GSS_S_UNAVAILABLE;
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);
}
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
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
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)))
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_ */
--- /dev/null
+/*
+ * 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
+};
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 {
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;
}
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))
{
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) {
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);
gss_buffer_t output_name_buffer,
gss_OID *output_name_type)
{
- OM_uint32 major, tmpMinor;
+ OM_uint32 major;
krb5_context krbContext;
char *krbName;
--- /dev/null
+/*
+ * 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 <dlfcn.h>
+
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
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;
*minor = 0;
+ if (ret_tok_type != NULL)
+ *ret_tok_type = TOK_TYPE_NONE;
+
if ((toksize -= 1) < 0)
return GSS_S_DEFECTIVE_TOKEN;
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;
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;
#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,
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;
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);
/* 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 */
/* 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) {
if (code != 0)
goto cleanup;
- if (toktype != TOK_TYPE_GSS_CB)
- ctx->sendSeq++;
+ ctx->sendSeq++;
if (toktype == TOK_TYPE_WRAP) {
/* Fix up EC field */
/* 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) {
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);