gss_LTLIBRARIES = mech_eap.la
mech_eap_la_CPPFLAGS = -DBUILD_GSSEAP_LIB -DSYSCONFDIR=\"${sysconfdir}\" -DDATAROOTDIR=\"${datarootdir}\"
-mech_eap_la_CFLAGS = -g -Wall -fno-strict-aliasing \
+mech_eap_la_CFLAGS = -g -Wunused-parameter -Wall -fno-strict-aliasing \
@KRB5_CFLAGS@ @EAP_CFLAGS@ @RADSEC_CFLAGS@ @TARGET_CFLAGS@
-mech_eap_la_CXXFLAGS = -g -Wall \
+mech_eap_la_CXXFLAGS = -g -Wunused-parameter -Wall \
@KRB5_CFLAGS@ @EAP_CFLAGS@ @RADSEC_CFLAGS@ \
@SHIBRESOLVER_CXXFLAGS@ @SHIBSP_CXXFLAGS@ @TARGET_CFLAGS@
mech_eap_la_LDFLAGS = -avoid-version -module \
util_cksum.c \
util_cred.c \
util_crypt.c \
- util_exts.c \
util_krb.c \
util_lucid.c \
util_mech.c \
util_radius.cpp \
util_saml.cpp \
util_shib.cpp \
+ util_sm.c \
util_token.c \
verify_mic.c \
wrap.c \
gsseap_err.h gsseap_err.c: gsseap_err.et
$(COMPILE_ET) $<
-
-
radsec_err.h radsec_err.c: radsec_err.et
$(COMPILE_ET) $<
- gss_xxx routines acquire lock, gssXxx don't
+- git
+
+If you do want to update with a rebase, deletethe branch from the
+server first then push the rebased branch
+
+to delete a branch from a server git push origin :branch_to_del
+
========
This is an implementation of the GSS EAP mechanism, as described in
-draft-ietf-abfab-gss-eap-00.txt.
+draft-ietf-abfab-gss-eap-01.txt.
Building
========
Installing
==========
+GSS mechglue
+------------
+
When installing, be sure to edit $prefix/etc/gss/mech to register
the EAP mechanisms. A sample configuration file is in this directory.
+You may need to specify an absolute path.
+
+RADIUS client library
+---------------------
Make sure your RADIUS library is configured to talk to the server of
-your choice: see the example radsec.conf in this directory.
+your choice: see the example radsec.conf in this directory. If you
+want to use TCP or TLS, you'll need to run radsecproxy in front of
+your RADIUS server.
-On the RADIUS server side, you need to install dictionary.ukerna and
-include it from the main dictionary file.
+RADIUS server
+-------------
-If you want the acceptor be able to identify the user, the RADIUS
-server needs to echo back the EAP username from the inner tunnel;
-for privacy, mech_eap only sends the realm in the EAP Identity
-response. To configure this with FreeRADIUS, add:
+These instructions apply to FreeRADIUS only, which is downloadable
+from http://freeradius.org/. After configure, make, install, do the
+following:
- update outer.reply {
- User-Name = "%{request:User-Name}"
- }
+On the RADIUS server side, you need to install dictionary.ukerna to
+$prefix/etc/raddb and include it from the main dictionary file, by
+adding:
+
+ $INCLUDE dictionary.ukerna
+
+to $prefix/etc/raddb/dictionary. Make sure these files are world-
+readable; they weren't in my installation.
-to $prefix/etc/raddb/sites-enabled/inner-tunnel, and ensure that
+Edit $prefix/etc/raddb/users to add your test user and password:
- virtual_server = "inner-tunnel"
+ bob@PROJECT-MOONSHOT.ORG Cleartext-Password := secret
-is set in eap.conf for the desired EAP types.
+Add an entry for your acceptor to $prefix/etc/raddb/clients.conf:
-To test the SAML assertion code path, you can place a fixed SAML
-assertion in the update reply block of the default configuration.
+ client somehost {
+ ipaddr = 127.0.0.1
+ secret = testing123
+ require_message_authenticator = yes
+ }
- update reply {
- SAML-AAA-Assertion = '<saml:Assertion ...'
- SAML-AAA-Assertion += '...'
+Edit $prefix/etc/raddb/eap.conf and set:
+
+ eap {
+...
+ default_eap_type = ttls
+...
+ tls {
+ certdir = ...
+ cadir = ...
+ private_key_file = ...
+ certificate_file = ...
+ }
+ ttls {
+ default_eap_type = mschapv2
+ copy_request_to_tunnel = no
+ use_tunneled_reply = no
+ virtual_server = "inner-tunnel"
}
+...
+ }
+
+to enable EAP-TTLS.
+
+If you want the acceptor be able to identify the user, the RADIUS
+server needs to echo back the EAP username from the inner tunnel;
+for privacy, mech_eap only sends the realm in the EAP Identity
+response. To configure this with FreeRADIUS, add:
+
+ update outer.reply {
+ User-Name = "%{request:User-Name}"
+ }
+
+If you want to add a SAML assertion, do this with "update reply"
+in $prefix/etc/raddb/sites-available/default:
+
+ update reply {
+ SAML-AAA-Assertion = '<saml:Assertion ...'
+ SAML-AAA-Assertion += '...'
+ }
You'll need to split it into multiple lines because of the RADIUS
attribute size limit.
% client -C -p 5556 -s host -m EAP-AES128 <host>
% server -c -p 5556 -s host -h <host>
+To test fast reauthentication support, add the following to
+/etc/krb5.conf:
+
+[appdefaults]
+ eap_gss = {
+ reauth_use_ccache = TRUE
+ }
+
+This will store a Kerberos ticket for a GSS-EAP authenticated user
+in a credentials cache, which can then be used for re-authentication
+to the same acceptor. You must have a valid keytab configured.
#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_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 outputToken);
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags);
#endif
/*
return GSS_S_COMPLETE;
}
+static OM_uint32
+eapGssSmAcceptAcceptorName(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 *smFlags)
+{
+ OM_uint32 major;
+
+ /* XXX TODO import and validate name from inputToken */
+
+ if (ctx->acceptorName != GSS_C_NO_NAME) {
+ /* Send desired target name to acceptor */
+ major = gssEapDisplayName(minor, ctx->acceptorName,
+ outputToken, NULL);
+ if (GSS_ERROR(major))
+ return major;
+ }
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+
+#ifdef GSSEAP_DEBUG
+static OM_uint32
+eapGssSmAcceptVendorInfo(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 *smFlags)
+{
+ fprintf(stderr, "GSS-EAP: vendor: %.*s\n",
+ (int)inputToken->length, (char *)inputToken->value);
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+#endif
+
+
/*
* Emit a identity EAP request to force the initiator (peer) to identify
* itself.
*/
static OM_uint32
eapGssSmAcceptIdentity(OM_uint32 *minor,
- gss_ctx_id_t ctx,
gss_cred_id_t cred,
- gss_buffer_t inputToken,
+ 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 outputToken)
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags)
{
OM_uint32 major;
struct wpabuf *reqData;
gss_buffer_desc pktBuffer;
+ if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
+ *minor = GSSEAP_CRED_MECH_MISMATCH;
+ return GSS_S_BAD_MECH;
+ }
+
if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
*minor = GSSEAP_WRONG_SIZE;
return GSS_S_DEFECTIVE_TOKEN;
}
- assert(ctx->acceptorName == GSS_C_NO_NAME);
-
- if (cred->name != GSS_C_NO_NAME) {
- major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
- if (GSS_ERROR(major))
- return major;
- }
-
reqData = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, 0,
EAP_CODE_REQUEST, 0);
if (reqData == NULL) {
if (GSS_ERROR(major))
return major;
- ctx->state = GSSEAP_STATE_AUTHENTICATE;
-
wpabuf_free(reqData);
+ GSSEAP_SM_TRANSITION_NEXT(ctx);
+
*minor = 0;
+ *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
+
return GSS_S_CONTINUE_NEEDED;
}
*/
static OM_uint32
eapGssSmAcceptAuthenticate(OM_uint32 *minor,
- gss_ctx_id_t ctx,
gss_cred_id_t cred,
- gss_buffer_t inputToken,
+ 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 outputToken)
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags)
{
OM_uint32 major, tmpMinor;
struct rs_connection *rconn;
frresp = rs_packet_frpkt(resp);
switch (frresp->code) {
- case PW_AUTHENTICATION_ACK:
case PW_ACCESS_CHALLENGE:
- major = GSS_S_CONTINUE_NEEDED;
+ case PW_AUTHENTICATION_ACK:
break;
case PW_AUTHENTICATION_REJECT:
*minor = GSSEAP_RADIUS_AUTH_FAILURE;
if (GSS_ERROR(major))
goto cleanup;
- ctx->state = GSSEAP_STATE_EXTENSIONS_REQ;
+ GSSEAP_SM_TRANSITION_NEXT(ctx);
}
- *minor = 0;
major = GSS_S_CONTINUE_NEEDED;
+ *minor = 0;
+ *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
cleanup:
if (request != NULL)
}
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)
+eapGssSmAcceptGssChannelBindings(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 *smFlags)
{
- OM_uint32 major;
+ OM_uint32 major, tmpMinor;
+ gss_iov_buffer_desc iov[2];
+
+ iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+ iov[0].buffer.length = 0;
+ iov[0].buffer.value = NULL;
- major = gssEapVerifyExtensions(minor, cred, ctx, chanBindings, inputToken);
+ iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM;
+ iov[1].buffer = *inputToken;
+
+ major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
+ iov, 2, TOK_TYPE_WRAP);
if (GSS_ERROR(major))
- return major;
+ return GSS_S_BAD_BINDINGS;
- outputToken->length = 0;
- outputToken->value = NULL;
+ if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS &&
+ !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
+ major = GSS_S_BAD_BINDINGS;
+ *minor = GSSEAP_BINDINGS_MISMATCH;
+ } else {
+ major = GSS_S_CONTINUE_NEEDED;
+ *minor = 0;
+ }
- ctx->state = GSSEAP_STATE_EXTENSIONS_RESP;
+ gss_release_buffer(&tmpMinor, &iov[0].buffer);
- *minor = 0;
- return GSS_S_CONTINUE_NEEDED;
+ return major;
}
+#ifdef GSSEAP_ENABLE_REAUTH
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)
+eapGssSmAcceptReauthCreds(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 *smFlags)
{
OM_uint32 major;
- major = gssEapMakeExtensions(minor, cred, ctx, chanBindings, outputToken);
- if (GSS_ERROR(major))
- return major;
-
- ctx->state = GSSEAP_STATE_ESTABLISHED;
+ /*
+ * If we're built with fast reauthentication enabled, then
+ * fabricate a ticket from the initiator to ourselves.
+ */
+ major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken);
+ if (major == GSS_S_UNAVAILABLE)
+ major = GSS_S_COMPLETE;
+ if (major == GSS_S_COMPLETE)
+ major = GSS_S_CONTINUE_NEEDED;
- *minor = 0;
- return GSS_S_COMPLETE;
+ return major;
}
+#endif
static OM_uint32
-eapGssSmAcceptEstablished(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)
+eapGssSmAcceptCompleteInitiatorExts(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 *smFlags)
{
- /* Called with already established context */
- *minor = GSSEAP_CONTEXT_ESTABLISHED;
- return GSS_S_BAD_STATUS;
+ GSSEAP_SM_TRANSITION_NEXT(ctx);
+
+ *minor = 0;
+
+ return GSS_S_CONTINUE_NEEDED;
}
static OM_uint32
-makeErrorToken(OM_uint32 *minor,
- OM_uint32 majorStatus,
- OM_uint32 minorStatus,
- gss_buffer_t outputToken)
+eapGssSmAcceptCompleteAcceptorExts(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 *smFlags)
{
- unsigned char errorData[8];
- gss_buffer_desc errorBuffer;
+ GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
- assert(GSS_ERROR(majorStatus));
-
- /*
- * Only return error codes that the initiator could have caused,
- * to avoid information leakage.
- */
- if (IS_RADIUS_ERROR(minorStatus)) {
- /* Squash RADIUS error codes */
- minorStatus = GSSEAP_RADIUS_PROT_FAILURE;
- } else if (!IS_WIRE_ERROR(minorStatus)) {
- /* Don't return non-wire error codes */
- return GSS_S_COMPLETE;
- }
-
- minorStatus -= ERROR_TABLE_BASE_eapg;
-
- store_uint32_be(majorStatus, &errorData[0]);
- store_uint32_be(minorStatus, &errorData[4]);
-
- errorBuffer.length = sizeof(errorData);
- errorBuffer.value = errorData;
+ *minor = 0;
+ *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
- return duplicateBuffer(minor, &errorBuffer, outputToken);
+ return GSS_S_COMPLETE;
}
-static struct gss_eap_acceptor_sm {
- enum gss_eap_token_type inputTokenType;
- enum gss_eap_token_type outputTokenType;
- OM_uint32 (*processToken)(OM_uint32 *,
- gss_ctx_id_t,
- gss_cred_id_t,
- gss_buffer_t,
- 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_EXT_REQ, TOK_TYPE_NONE, eapGssSmAcceptExtensionsReq },
- { TOK_TYPE_NONE, TOK_TYPE_EXT_RESP, eapGssSmAcceptExtensionsResp },
- { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmAcceptEstablished },
- { TOK_TYPE_NONE, TOK_TYPE_CONTEXT_ERR, NULL },
+static struct gss_eap_sm eapGssAcceptorSm[] = {
+ {
+ ITOK_TYPE_ACCEPTOR_NAME_REQ,
+ ITOK_TYPE_ACCEPTOR_NAME_RESP,
+ GSSEAP_STATE_INITIAL,
+ 0,
+ eapGssSmAcceptAcceptorName
+ },
+#ifdef GSSEAP_DEBUG
+ {
+ ITOK_TYPE_VENDOR_INFO,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_INITIAL,
+ 0,
+ eapGssSmAcceptVendorInfo,
+ },
+#endif
+#ifdef GSSEAP_ENABLE_REAUTH
+ {
+ ITOK_TYPE_REAUTH_REQ,
+ ITOK_TYPE_REAUTH_RESP,
+ GSSEAP_STATE_INITIAL,
+ 0,
+ eapGssSmAcceptGssReauth,
+ },
+#endif
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_EAP_REQ,
+ GSSEAP_STATE_INITIAL,
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmAcceptIdentity,
+ },
+ {
+ ITOK_TYPE_EAP_RESP,
+ ITOK_TYPE_EAP_REQ,
+ GSSEAP_STATE_AUTHENTICATE,
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmAcceptAuthenticate
+ },
+ {
+ ITOK_TYPE_GSS_CHANNEL_BINDINGS,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_INITIATOR_EXTS,
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmAcceptGssChannelBindings,
+ },
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_INITIATOR_EXTS,
+ 0,
+ eapGssSmAcceptCompleteInitiatorExts,
+ },
#ifdef GSSEAP_ENABLE_REAUTH
- { TOK_TYPE_GSS_REAUTH, TOK_TYPE_GSS_REAUTH, eapGssSmAcceptGssReauth },
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_REAUTH_CREDS,
+ GSSEAP_STATE_ACCEPTOR_EXTS,
+ 0,
+ eapGssSmAcceptReauthCreds,
+ },
#endif
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_ACCEPTOR_EXTS,
+ 0,
+ eapGssSmAcceptCompleteAcceptorExts
+ },
};
OM_uint32
OM_uint32 *time_rec,
gss_cred_id_t *delegated_cred_handle)
{
- OM_uint32 major;
- OM_uint32 tmpMajor, tmpMinor;
+ OM_uint32 major, tmpMinor;
gss_ctx_id_t ctx = *context_handle;
- 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;
}
GSSEAP_MUTEX_LOCK(&cred->mutex);
- sm = &eapGssAcceptorSm[ctx->state];
-
- major = gssEapVerifyToken(minor, ctx, input_token,
- &tokType, &innerInputToken);
- if (GSS_ERROR(major))
- goto cleanup;
-
- if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
- *minor = GSSEAP_CRED_MECH_MISMATCH;
- major = GSS_S_BAD_MECH;
- goto cleanup;
+ if (cred->name != GSS_C_NO_NAME) {
+ major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
+ 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 = GSSEAP_STATE_KRB_REAUTH;
- } else
-#endif
- if (tokType != sm->inputTokenType) {
- *minor = GSSEAP_WRONG_TOK_ID;
- major = GSS_S_DEFECTIVE_TOKEN;
+ major = gssEapSmStep(minor,
+ cred,
+ ctx,
+ GSS_C_NO_NAME,
+ GSS_C_NO_OID,
+ 0,
+ GSS_C_INDEFINITE,
+ input_chan_bindings,
+ input_token,
+ output_token,
+ eapGssAcceptorSm,
+ sizeof(eapGssAcceptorSm) / sizeof(eapGssAcceptorSm[0]));
+ if (GSS_ERROR(major))
goto cleanup;
- }
-
- do {
- sm = &eapGssAcceptorSm[ctx->state];
-
- major = (sm->processToken)(minor,
- ctx,
- cred,
- &innerInputToken,
- input_chan_bindings,
- &innerOutputToken);
- if (GSS_ERROR(major)) {
- /* Possibly generate an error token */
- tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputToken);
- if (GSS_ERROR(tmpMajor)) {
- major = tmpMajor;
- goto cleanup;
- }
-
- sm = &eapGssAcceptorSm[GSSEAP_STATE_ERROR];
- goto send_token;
- }
- } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.length == 0);
if (mech_type != NULL) {
if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type))
}
}
- assert(ctx->state == GSSEAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED);
-
-send_token:
- if (innerOutputToken.value != NULL) {
- tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken,
- sm->outputTokenType, output_token);
- if (GSS_ERROR(tmpMajor)) {
- major = tmpMajor;
- *minor = tmpMinor;
- goto cleanup;
- }
- }
+ assert(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
cleanup:
if (cred != GSS_C_NO_CREDENTIAL)
if (GSS_ERROR(major))
gssEapReleaseContext(&tmpMinor, context_handle);
- gss_release_buffer(&tmpMinor, &innerOutputToken);
-
return major;
}
if (GSS_ERROR(major))
return major;
- if (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 = GSSEAP_STATE_ESTABLISHED;
-
*minor = 0;
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_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 outputToken)
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags)
{
OM_uint32 major, tmpMinor;
gss_name_t krbInitiator = GSS_C_NO_NAME;
- gss_OID mech = GSS_C_NO_OID;
OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE;
+ /*
+ * 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.
+ */
+
ctx->flags |= CTX_FLAG_KRB_REAUTH;
major = gssAcceptSecContext(minor,
if (major == GSS_S_COMPLETE) {
major = acceptReadyKrb(minor, ctx, cred,
krbInitiator, mech, timeRec);
+ if (major == GSS_S_COMPLETE) {
+ GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
+ }
+ ctx->gssFlags = gssFlags;
+ } else if (GSS_ERROR(major) &&
+ (*smFlags & SM_FLAG_INPUT_TOKEN_CRITICAL) == 0) {
+ /* pretend reauthentication attempt never happened */
+ gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER);
+ ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
+ GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
+ major = GSS_S_CONTINUE_NEEDED;
}
- ctx->gssFlags = gssFlags;
-
gssReleaseName(&tmpMinor, &krbInitiator);
return major;
p = (unsigned char *)token->value;
store_uint32_be(EAP_EXPORT_CONTEXT_V1, &p[0]); /* version */
- store_uint32_be(ctx->state, &p[4]);
+ store_uint32_be(GSSEAP_SM_STATE(ctx), &p[4]);
store_uint32_be(ctx->flags, &p[8]);
store_uint32_be(ctx->gssFlags, &p[12]);
p = store_oid(ctx->mechanismUsed, &p[16]);
#define CTX_IS_INITIATOR(ctx) (((ctx)->flags & CTX_FLAG_INITIATOR) != 0)
-enum gss_eap_state {
- GSSEAP_STATE_IDENTITY = 0, /* identify peer */
- GSSEAP_STATE_AUTHENTICATE, /* exchange EAP messages */
- GSSEAP_STATE_EXTENSIONS_REQ, /* initiator extensions */
- GSSEAP_STATE_EXTENSIONS_RESP, /* acceptor extensions */
- GSSEAP_STATE_ESTABLISHED, /* context established */
- GSSEAP_STATE_ERROR, /* context error */
-#ifdef GSSEAP_ENABLE_REAUTH
- GSSEAP_STATE_KRB_REAUTH /* fast reauthentication */
-#endif
-};
-
#define CTX_IS_ESTABLISHED(ctx) ((ctx)->state == GSSEAP_STATE_ESTABLISHED)
/* Initiator context flags */
error_code GSSEAP_TOK_TRUNC, "Token is missing data"
error_code GSSEAP_BAD_DIRECTION, "Packet was replayed in wrong direction"
error_code GSSEAP_WRONG_TOK_ID, "Received token ID does not match expected token ID"
-error_code GSSEAP_CRIT_EXT_UNAVAILABLE, "Critical extension unavailable"
-error_code GSSEAP_MISSING_REQUIRED_EXT, "Missing required extension"
+error_code GSSEAP_CRIT_ITOK_UNAVAILABLE, "Critical inner token type unavailable"
+error_code GSSEAP_MISSING_REQUIRED_ITOK, "Missing required inner token"
+error_code GSSEAP_DUPLICATE_ITOK, "Duplicate inner token received"
+error_code GSSEAP_WRONG_ITOK, "Recieved invalid inner token for current state"
error_code GSSEAP_KEY_UNAVAILABLE, "EAP key unavailable"
error_code GSSEAP_KEY_TOO_SHORT, "EAP key too short"
error_code GSSEAP_RADIUS_AUTH_FAILURE, "Authentication rejected by RADIUS server"
error_code GSSEAP_BAD_SERVICE_NAME, "Name is not a valid service name"
error_code GSSEAP_BAD_INITIATOR_NAME, "Initiator identity must be a valid name"
error_code GSSEAP_NO_HOSTNAME, "Could not determine local host name"
+error_code GSSEAP_NO_ACCEPTOR_NAME, "Could not determine acceptor identity"
#
# Credential errors
#
error_code GSSEAP_LIBEAP_INIT_FAILURE, "Failed to initialize EAP library"
error_code GSSEAP_PEER_SM_INIT_FAILURE, "Failed to create EAP state machine"
+error_code GSSEAP_PEER_SM_STEP_FAILURE, "Failed to step EAP state machine"
error_code GSSEAP_PEER_AUTH_FAILURE, "EAP peer authentication failure"
error_code GSSEAP_PEER_BAD_MESSAGE, "Received bad EAP message"
remain -= 16;
/* Validate state */
- if (ctx->state < GSSEAP_STATE_IDENTITY ||
- ctx->state > GSSEAP_STATE_ESTABLISHED)
+ if (GSSEAP_SM_STATE(ctx) < GSSEAP_STATE_INITIAL ||
+ GSSEAP_SM_STATE(ctx) > GSSEAP_STATE_ESTABLISHED)
return GSS_S_DEFECTIVE_TOKEN;
/* Only acceptor can export partial context tokens */
#include "gssapiP_eap.h"
-#ifdef GSSEAP_ENABLE_REAUTH
-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)
{
gss_OID mech,
OM_uint32 reqFlags,
OM_uint32 timeReq,
- gss_channel_bindings_t chanBindings,
- gss_buffer_t inputToken,
- gss_buffer_t outputToken)
+ gss_channel_bindings_t chanBindings)
{
OM_uint32 major;
if (GSS_ERROR(major))
return major;
- GSSEAP_MUTEX_LOCK(&target->mutex);
+ if (target != GSS_C_NO_NAME) {
+ GSSEAP_MUTEX_LOCK(&target->mutex);
+
+ major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
+ if (GSS_ERROR(major)) {
+ GSSEAP_MUTEX_UNLOCK(&target->mutex);
+ return major;
+ }
- major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
- if (GSS_ERROR(major)) {
GSSEAP_MUTEX_UNLOCK(&target->mutex);
- return major;
}
- GSSEAP_MUTEX_UNLOCK(&target->mutex);
-
if (mech == GSS_C_NULL_OID) {
major = gssEapDefaultMech(minor, &ctx->mechanismUsed);
} else if (gssEapIsConcreteMechanismOid(mech)) {
}
static OM_uint32
+eapGssSmInitError(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 *smFlags)
+{
+ OM_uint32 major;
+ unsigned char *p;
+
+ if (inputToken->length < 8) {
+ *minor = GSSEAP_TOK_TRUNC;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ p = (unsigned char *)inputToken->value;
+
+ major = load_uint32_be(&p[0]);
+ *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
+
+ if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
+ major = GSS_S_FAILURE;
+ *minor = GSSEAP_BAD_ERROR_TOKEN;
+ }
+
+ assert(GSS_ERROR(major));
+
+ return major;
+}
+
+#ifdef GSSEAP_ENABLE_REAUTH
+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 *smFlags)
+{
+ 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);
+
+ if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
+ if (!gssEapCanReauthP(cred, target, timeReq))
+ return GSS_S_CONTINUE_NEEDED;
+
+ ctx->flags |= CTX_FLAG_KRB_REAUTH;
+ } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
+ major = GSS_S_DEFECTIVE_TOKEN;
+ *minor = GSSEAP_WRONG_ITOK;
+ 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_MUTUAL_FLAG,
+ timeReq,
+ chanBindings,
+ inputToken,
+ &actualMech,
+ outputToken,
+ &gssFlags,
+ &timeRec);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ ctx->gssFlags = gssFlags;
+
+ if (major == GSS_S_COMPLETE) {
+ assert(GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE);
+
+ major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
+ if (GSS_ERROR(major))
+ goto cleanup;
+ GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
+ } else {
+ GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
+ }
+
+cleanup:
+ gssReleaseName(&tmpMinor, &mechTarget);
+
+ return major;
+}
+#endif /* GSSEAP_ENABLE_REAUTH */
+
+#ifdef GSSEAP_DEBUG
+static OM_uint32
+eapGssSmInitVendorInfo(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 *smFlags)
+{
+ OM_uint32 major;
+
+ major = makeStringBuffer(minor, "JANET(UK)", outputToken);
+ if (GSS_ERROR(major))
+ return major;
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+#endif
+
+static OM_uint32
+eapGssSmInitAcceptorName(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 *smFlags)
+{
+ OM_uint32 major;
+
+ if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL &&
+ ctx->acceptorName != GSS_C_NO_NAME) {
+
+ /* Send desired target name to acceptor */
+ major = gssEapDisplayName(minor, ctx->acceptorName,
+ outputToken, NULL);
+ if (GSS_ERROR(major))
+ return major;
+ } else if (inputToken != GSS_C_NO_BUFFER &&
+ ctx->acceptorName == GSS_C_NO_NAME) {
+ /* Accept target name hint from acceptor */
+ major = gssEapImportName(minor, inputToken,
+ GSS_C_NT_USER_NAME, &ctx->acceptorName);
+ if (GSS_ERROR(major))
+ return major;
+ }
+
+ /*
+ * Currently, other parts of the code assume that the acceptor name
+ * is available, hence this check.
+ */
+ if (ctx->acceptorName == GSS_C_NO_NAME) {
+ *minor = GSSEAP_NO_ACCEPTOR_NAME;
+ return GSS_S_FAILURE;
+ }
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
eapGssSmInitIdentity(OM_uint32 *minor,
gss_cred_id_t cred,
gss_ctx_id_t ctx,
OM_uint32 timeReq,
gss_channel_bindings_t chanBindings,
gss_buffer_t inputToken,
- gss_buffer_t outputToken)
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags)
{
- OM_uint32 major;
- int initialContextToken;
+ struct eap_config eapConfig;
- initialContextToken = (inputToken->length == 0);
- if (!initialContextToken) {
- *minor = GSSEAP_WRONG_SIZE;
- return GSS_S_DEFECTIVE_TOKEN;
+#ifdef GSSEAP_ENABLE_REAUTH
+ if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
+ OM_uint32 tmpMinor;
+
+ /* server didn't support reauthentication, sent EAP request */
+ gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER);
+ ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
+ GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
+ } else
+#endif
+ *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
+
+ assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
+ assert(inputToken == GSS_C_NO_BUFFER);
+
+ memset(&eapConfig, 0, sizeof(eapConfig));
+
+ ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
+ &gssEapPolicyCallbacks,
+ ctx,
+ &eapConfig);
+ if (ctx->initiatorCtx.eap == NULL) {
+ *minor = GSSEAP_PEER_SM_INIT_FAILURE;
+ return GSS_S_FAILURE;
}
- major = initBegin(minor, cred, ctx, target, mech,
- reqFlags, timeReq, chanBindings,
- inputToken, outputToken);
- if (GSS_ERROR(major))
- return major;
+ ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
+
+ /* poke EAP state machine */
+ if (eap_peer_sm_step(ctx->initiatorCtx.eap) != 0) {
+ *minor = GSSEAP_PEER_SM_STEP_FAILURE;
+ return GSS_S_FAILURE;
+ }
- ctx->state = GSSEAP_STATE_AUTHENTICATE;
+ GSSEAP_SM_TRANSITION_NEXT(ctx);
*minor = 0;
+
return GSS_S_CONTINUE_NEEDED;
}
-static struct wpabuf emptyWpaBuffer;
-
static OM_uint32
eapGssSmInitAuthenticate(OM_uint32 *minor,
gss_cred_id_t cred,
OM_uint32 timeReq,
gss_channel_bindings_t chanBindings,
gss_buffer_t inputToken,
- gss_buffer_t outputToken)
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags)
{
OM_uint32 major;
OM_uint32 tmpMinor;
int code;
struct wpabuf *resp = NULL;
- int initialContextToken;
*minor = 0;
- initialContextToken = (inputToken == GSS_C_NO_BUFFER ||
- inputToken->length == 0);
+ assert(inputToken != GSS_C_NO_BUFFER);
major = peerConfigInit(minor, cred, ctx);
if (GSS_ERROR(major))
goto cleanup;
- if (ctx->initiatorCtx.eap == NULL) {
- struct eap_config eapConfig;
-
- memset(&eapConfig, 0, sizeof(eapConfig));
-
- ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
- &gssEapPolicyCallbacks,
- ctx,
- &eapConfig);
- if (ctx->initiatorCtx.eap == NULL) {
- major = GSS_S_FAILURE;
- *minor = GSSEAP_PEER_SM_INIT_FAILURE;
- goto cleanup;
- }
-
- ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
- }
+ assert(ctx->initiatorCtx.eap != NULL);
+ assert(ctx->flags & CTX_FLAG_EAP_PORT_ENABLED);
ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
major = GSS_S_CONTINUE_NEEDED;
- ctx->state = GSSEAP_STATE_EXTENSIONS_REQ;
+ GSSEAP_SM_TRANSITION_NEXT(ctx);
} else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
major = GSS_S_DEFECTIVE_CREDENTIAL;
*minor = GSSEAP_PEER_AUTH_FAILURE;
- } else if (code == 0 && initialContextToken) {
- resp = &emptyWpaBuffer;
- major = GSS_S_CONTINUE_NEEDED;
} else {
major = GSS_S_DEFECTIVE_TOKEN;
*minor = GSSEAP_PEER_BAD_MESSAGE;
major = tmpMajor;
*minor = tmpMinor;
}
+
+ *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
}
wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
}
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)
+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,
+ OM_uint32 *smFlags)
{
OM_uint32 major;
+ gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
+
+ if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
+ buffer = chanBindings->application_data;
- major = gssEapMakeExtensions(minor, cred, ctx, chanBindings, outputToken);
+ major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
+ &buffer, NULL, outputToken);
if (GSS_ERROR(major))
return major;
assert(outputToken->value != NULL);
- ctx->state = GSSEAP_STATE_EXTENSIONS_RESP;
-
*minor = 0;
- 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)
-{
- OM_uint32 major;
+ *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
- major = gssEapVerifyExtensions(minor, cred, ctx, chanBindings, inputToken);
- if (GSS_ERROR(major))
- return major;
-
- ctx->state = GSSEAP_STATE_ESTABLISHED;
-
- *minor = 0;
- return GSS_S_COMPLETE;
+ return GSS_S_CONTINUE_NEEDED;
}
+#ifdef GSSEAP_ENABLE_REAUTH
static OM_uint32
-eapGssSmInitEstablished(OM_uint32 *minor,
+eapGssSmInitReauthCreds(OM_uint32 *minor,
gss_cred_id_t cred,
gss_ctx_id_t ctx,
gss_name_t target,
OM_uint32 timeReq,
gss_channel_bindings_t chanBindings,
gss_buffer_t inputToken,
- gss_buffer_t outputToken)
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags)
{
- /* Called with already established context */
- *minor = GSSEAP_CONTEXT_ESTABLISHED;
- return GSS_S_BAD_STATUS;
+ OM_uint32 major;
+
+ if (ctx->gssFlags & GSS_C_MUTUAL_FLAG) {
+ major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
+ if (GSS_ERROR(major))
+ return major;
+ }
+
+ *minor = 0;
+ return GSS_S_CONTINUE_NEEDED;
}
+#endif /* GSSEAP_ENABLE_REAUTH */
static OM_uint32
-eapGssSmInitError(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)
+eapGssSmInitCompleteInitiatorExts(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 *smFlags)
{
- OM_uint32 major;
- unsigned char *p;
+ GSSEAP_SM_TRANSITION_NEXT(ctx);
- if (inputToken->length < 8) {
- *minor = GSSEAP_TOK_TRUNC;
- return GSS_S_DEFECTIVE_TOKEN;
- }
+ *minor = 0;
+ *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
- p = (unsigned char *)inputToken->value;
+ return GSS_S_CONTINUE_NEEDED;
+}
- major = load_uint32_be(&p[0]);
- *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
+static OM_uint32
+eapGssSmInitCompleteAcceptorExts(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 *smFlags)
+{
+ GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
- if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
- major = GSS_S_FAILURE;
- *minor = GSSEAP_BAD_ERROR_TOKEN;
- }
+ *minor = 0;
- return major;
+ return GSS_S_COMPLETE;
}
-static struct gss_eap_initiator_sm {
- enum gss_eap_token_type inputTokenType;
- enum gss_eap_token_type outputTokenType;
- OM_uint32 (*processToken)(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_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_EXT_REQ, eapGssSmInitExtensionsReq },
- { TOK_TYPE_EXT_RESP, TOK_TYPE_NONE, eapGssSmInitExtensionsResp },
- { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmInitEstablished },
- { TOK_TYPE_CONTEXT_ERR, TOK_TYPE_NONE, eapGssSmInitError },
+static struct gss_eap_sm eapGssInitiatorSm[] = {
+ {
+ ITOK_TYPE_CONTEXT_ERR,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_ALL & ~(GSSEAP_STATE_INITIAL),
+ 0,
+ eapGssSmInitError
+ },
+ {
+ ITOK_TYPE_ACCEPTOR_NAME_RESP,
+ ITOK_TYPE_ACCEPTOR_NAME_REQ,
+ GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
+ 0,
+ eapGssSmInitAcceptorName
+ },
+#ifdef GSSEAP_DEBUG
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_VENDOR_INFO,
+ GSSEAP_STATE_INITIAL,
+ 0,
+ eapGssSmInitVendorInfo
+ },
+#endif
+#ifdef GSSEAP_ENABLE_REAUTH
+ {
+ ITOK_TYPE_REAUTH_RESP,
+ ITOK_TYPE_REAUTH_REQ,
+ GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
+ 0,
+ eapGssSmInitGssReauth
+ },
+#endif
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_NONE,
+#ifdef GSSEAP_ENABLE_REAUTH
+ GSSEAP_STATE_REAUTHENTICATE |
+#endif
+ GSSEAP_STATE_INITIAL,
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmInitIdentity
+ },
+ {
+ ITOK_TYPE_EAP_REQ,
+ ITOK_TYPE_EAP_RESP,
+ GSSEAP_STATE_AUTHENTICATE,
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmInitAuthenticate
+ },
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_GSS_CHANNEL_BINDINGS,
+ GSSEAP_STATE_INITIATOR_EXTS,
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmInitGssChannelBindings
+ },
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_INITIATOR_EXTS,
+ 0,
+ eapGssSmInitCompleteInitiatorExts
+ },
#ifdef GSSEAP_ENABLE_REAUTH
- { TOK_TYPE_GSS_REAUTH, TOK_TYPE_GSS_REAUTH, eapGssSmInitGssReauth },
+ {
+ ITOK_TYPE_REAUTH_CREDS,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_ACCEPTOR_EXTS,
+ 0,
+ eapGssSmInitReauthCreds
+ },
#endif
+ /* other extensions go here */
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_ACCEPTOR_EXTS,
+ 0,
+ eapGssSmInitCompleteAcceptorExts
+ }
};
OM_uint32
OM_uint32 *ret_flags,
OM_uint32 *time_rec)
{
- OM_uint32 major;
- OM_uint32 tmpMajor, tmpMinor;
+ OM_uint32 major, tmpMinor;
gss_ctx_id_t ctx = *context_handle;
- 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;
- int initialContextToken = 0;
*minor = 0;
ctx->flags |= CTX_FLAG_INITIATOR;
- initialContextToken = 1;
+ major = initBegin(minor, cred, ctx, target_name, mech_type,
+ req_flags, time_req, input_chan_bindings);
+ if (GSS_ERROR(major)) {
+ gssEapReleaseContext(minor, &ctx);
+ return major;
+ }
+
*context_handle = ctx;
}
GSSEAP_MUTEX_LOCK(&cred->mutex);
-#ifdef GSSEAP_ENABLE_REAUTH
- if (initialContextToken && gssEapCanReauthP(cred, target_name, time_req))
- ctx->state = GSSEAP_STATE_KRB_REAUTH;
-#endif
if ((cred->flags & CRED_FLAG_INITIATE) == 0) {
major = GSS_S_NO_CRED;
goto cleanup;
}
- sm = &eapGssInitiatorSm[ctx->state];
-
- if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
- major = gssEapVerifyToken(minor, ctx, input_token,
- &tokType, &innerInputToken);
- if (GSS_ERROR(major))
- goto cleanup;
-
- if (tokType == TOK_TYPE_CONTEXT_ERR) {
- ctx->state = GSSEAP_STATE_ERROR;
- } else if (tokType != sm->inputTokenType) {
- major = GSS_S_DEFECTIVE_TOKEN;
- *minor = GSSEAP_WRONG_TOK_ID;
- goto cleanup;
- }
- } else {
- innerInputToken.length = 0;
- innerInputToken.value = NULL;
- }
-
- /*
- * Advance through state machine whilst empty tokens are emitted and
- * the status is not GSS_S_COMPLETE or an error status.
- */
- do {
- sm = &eapGssInitiatorSm[ctx->state];
-
- major = (sm->processToken)(minor,
- cred,
- ctx,
- target_name,
- mech_type,
- req_flags,
- time_req,
- input_chan_bindings,
- &innerInputToken,
- &innerOutputToken);
- if (GSS_ERROR(major))
- goto cleanup;
- } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.value == NULL);
+ major = gssEapSmStep(minor,
+ cred,
+ ctx,
+ target_name,
+ mech_type,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ output_token,
+ eapGssInitiatorSm,
+ sizeof(eapGssInitiatorSm) / sizeof(eapGssInitiatorSm[0]));
+ if (GSS_ERROR(major))
+ goto cleanup;
if (actual_mech_type != NULL) {
if (!gssEapInternalizeOid(ctx->mechanismUsed, actual_mech_type))
duplicateOid(&tmpMinor, ctx->mechanismUsed, actual_mech_type);
}
- if (innerOutputToken.value != NULL) {
- tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken,
- sm->outputTokenType, output_token);
- if (GSS_ERROR(tmpMajor)) {
- major = tmpMajor;
- *minor = tmpMinor;
- goto cleanup;
- }
- }
if (ret_flags != NULL)
*ret_flags = ctx->gssFlags;
if (time_rec != NULL)
gssEapContextTime(&tmpMinor, ctx, time_rec);
- assert(ctx->state == GSSEAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED);
+ assert(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
cleanup:
if (cred != GSS_C_NO_CREDENTIAL)
if (GSS_ERROR(major))
gssEapReleaseContext(&tmpMinor, context_handle);
- gss_release_buffer(&tmpMinor, &innerOutputToken);
-
return major;
}
-
-#ifdef GSSEAP_ENABLE_REAUTH
-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;
-
- 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 = GSSEAP_STATE_ESTABLISHED;
- }
-
-cleanup:
- gssReleaseName(&tmpMinor, &mechTarget);
-
- return major;
-}
-#endif /* GSSEAP_ENABLE_REAUTH */
#ifdef HAVE_HEIMDAL_VERSION
krb5_crypto krbCrypto,
#else
- krb5_keyblock *unused __attribute__((__unused__)),
+ krb5_keyblock *unused,
#endif
int *conf_state,
gss_qop_t *qop_state,
TOK_TYPE_EXPORT_NAME = 0x0401, /* RFC 2743 exported name */
TOK_TYPE_EXPORT_NAME_COMPOSITE = 0x0402, /* exported composite name */
TOK_TYPE_DELETE_CONTEXT = 0x0405, /* RFC 2743 delete context */
- TOK_TYPE_EAP_RESP = 0x0601, /* EAP response */
- TOK_TYPE_EAP_REQ = 0x0602, /* EAP request */
- TOK_TYPE_EXT_REQ = 0x0603, /* GSS EAP extensions request */
- TOK_TYPE_EXT_RESP = 0x0604, /* GSS EAP extensions response */
- TOK_TYPE_GSS_REAUTH = 0x0605, /* GSS EAP fast reauthentication token */
- TOK_TYPE_CONTEXT_ERR = 0x0606, /* context error */
+ TOK_TYPE_INITIATOR_CONTEXT = 0x0601, /* initiator-sent context token */
+ TOK_TYPE_ACCEPTOR_CONTEXT = 0x0602, /* acceptor-sent context token */
};
+/* inner token types and flags */
+#define ITOK_TYPE_NONE 0x00000000
+#define ITOK_TYPE_CONTEXT_ERR 0x00000001 /* critical */
+#define ITOK_TYPE_ACCEPTOR_NAME_REQ 0x00000002 /* TBD */
+#define ITOK_TYPE_ACCEPTOR_NAME_RESP 0x00000003 /* TBD */
+#define ITOK_TYPE_EAP_RESP 0x00000004 /* critical, required, if not reauth */
+#define ITOK_TYPE_EAP_REQ 0x00000005 /* critical, required, if not reauth */
+#define ITOK_TYPE_GSS_CHANNEL_BINDINGS 0x00000006 /* critical, required, if not reauth */
+#define ITOK_TYPE_REAUTH_CREDS 0x00000007 /* optional */
+#define ITOK_TYPE_REAUTH_REQ 0x00000008 /* optional */
+#define ITOK_TYPE_REAUTH_RESP 0x00000009 /* optional */
+#define ITOK_TYPE_VERSION_INFO 0x0000000A /* optional */
+#define ITOK_TYPE_VENDOR_INFO 0x0000000B /* optional */
+
+#define ITOK_FLAG_CRITICAL 0x80000000 /* critical, wire flag */
+#define ITOK_FLAG_VERIFIED 0x40000000 /* verified, API flag */
+
+#define ITOK_TYPE_MASK (~(ITOK_FLAG_CRITICAL | ITOK_FLAG_VERIFIED))
+
OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
OM_uint32 gssEapReleaseContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
krb5_enctype enctype,
krb5_keyblock *pKey);
-/* util_exts.c */
-#define EXT_FLAG_CRITICAL 0x80000000 /* critical, wire flag */
-#define EXT_FLAG_VERIFIED 0x40000000 /* verified, API flag */
-
-#define EXT_TYPE_GSS_CHANNEL_BINDINGS 0x00000000
-#define EXT_TYPE_REAUTH_CREDS 0x00000001
-#define EXT_TYPE_MASK (~(EXT_FLAG_CRITICAL | EXT_FLAG_VERIFIED))
-
-struct gss_eap_extension_provider {
- OM_uint32 type;
- int critical; /* client */
- int required; /* server */
- OM_uint32 (*make)(OM_uint32 *,
- gss_cred_id_t,
- gss_ctx_id_t,
- gss_channel_bindings_t,
- gss_buffer_t);
- OM_uint32 (*verify)(OM_uint32 *,
- gss_cred_id_t,
- gss_ctx_id_t,
- gss_channel_bindings_t,
- const gss_buffer_t);
-};
-
-OM_uint32
-gssEapMakeExtensions(OM_uint32 *minor,
- gss_cred_id_t cred,
- gss_ctx_id_t ctx,
- gss_channel_bindings_t chanBindings,
- gss_buffer_t buffer);
-
-OM_uint32
-gssEapVerifyExtensions(OM_uint32 *minor,
- gss_cred_id_t cred,
- gss_ctx_id_t ctx,
- gss_channel_bindings_t chanBindings,
- const gss_buffer_t buffer);
-
/* util_krb.c */
#ifdef HAVE_HEIMDAL_VERSION
sequenceInit(OM_uint32 *minor, void **vqueue, uint64_t seqnum,
int do_replay, int do_sequence, int wide_nums);
+/* util_sm.c */
+enum gss_eap_state {
+ GSSEAP_STATE_INITIAL = 0x01, /* initial state */
+ GSSEAP_STATE_AUTHENTICATE = 0x02, /* exchange EAP messages */
+ GSSEAP_STATE_INITIATOR_EXTS = 0x04, /* initiator extensions */
+ GSSEAP_STATE_ACCEPTOR_EXTS = 0x08, /* acceptor extensions */
+#ifdef GSSEAP_ENABLE_REAUTH
+ GSSEAP_STATE_REAUTHENTICATE = 0x10, /* GSS reauthentication messages */
+#endif
+ GSSEAP_STATE_ESTABLISHED = 0x20, /* context established */
+ GSSEAP_STATE_ALL = 0x3F
+};
+
+#define GSSEAP_STATE_NEXT(s) ((s) << 1)
+
+#define GSSEAP_SM_STATE(ctx) ((ctx)->state)
+
+#ifdef GSSEAP_DEBUG
+void gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state);
+#define GSSEAP_SM_TRANSITION(ctx, state) gssEapSmTransition((ctx), (state))
+#else
+#define GSSEAP_SM_TRANSITION(ctx, newstate) do { (ctx)->state = (newstate); } while (0)
+#endif
+
+#define GSSEAP_SM_TRANSITION_NEXT(ctx) GSSEAP_SM_TRANSITION((ctx), GSSEAP_STATE_NEXT(GSSEAP_SM_STATE((ctx))))
+
+/* state machine entry */
+struct gss_eap_sm {
+ OM_uint32 inputTokenType;
+ OM_uint32 outputTokenType;
+ enum gss_eap_state validStates;
+ OM_uint32 itokFlags;
+ OM_uint32 (*processToken)(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_buffer_t,
+ OM_uint32 *);
+};
+
+/* state machine flags, set by handler */
+#define SM_FLAG_FORCE_SEND_TOKEN 0x00000001 /* send token even if no inner tokens */
+#define SM_FLAG_OUTPUT_TOKEN_CRITICAL 0x00000002 /* output token is critical */
+
+/* state machine flags, set by state machine */
+#define SM_FLAG_INPUT_TOKEN_CRITICAL 0x10000000 /* input token was critical */
+
+#define SM_ITOK_FLAG_REQUIRED 0x00000001 /* received tokens must be present */
+
+OM_uint32
+gssEapSmStep(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,
+ struct gss_eap_sm *sm,
+ size_t smCount);
+
+void
+gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state);
+
/* util_token.c */
+OM_uint32
+gssEapEncodeInnerTokens(OM_uint32 *minor,
+ gss_buffer_set_t extensions,
+ OM_uint32 *types,
+ gss_buffer_t buffer);
+OM_uint32
+gssEapDecodeInnerTokens(OM_uint32 *minor,
+ const gss_buffer_t buffer,
+ gss_buffer_set_t *pExtensions,
+ OM_uint32 **pTypes);
+
size_t
tokenSize(const gss_OID_desc *mech, size_t body_size);
return GSS_S_FAILURE;
}
- ctx->state = GSSEAP_STATE_IDENTITY;
+ ctx->state = GSSEAP_STATE_INITIAL;
/*
* Integrity, confidentiality, sequencing and replay detection are
+++ /dev/null
-/*
- * Copyright (c) 2011, 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.
- */
-
-/*
- * Extension token support.
- */
-
-#include "gssapiP_eap.h"
-
-static OM_uint32
-encodeExtensions(OM_uint32 *minor,
- gss_buffer_set_t extensions,
- OM_uint32 *types,
- gss_buffer_t buffer);
-
-static OM_uint32
-decodeExtensions(OM_uint32 *minor,
- const gss_buffer_t buffer,
- gss_buffer_set_t *pExtensions,
- OM_uint32 **pTypes);
-
-/*
- * Initiator extensions
- */
-static OM_uint32
-makeGssChannelBindings(OM_uint32 *minor,
- gss_cred_id_t cred,
- gss_ctx_id_t ctx,
- gss_channel_bindings_t chanBindings,
- gss_buffer_t outputToken)
-{
- OM_uint32 major;
- gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
-
- if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
- buffer = chanBindings->application_data;
-
- major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
- &buffer, NULL, outputToken);
- if (GSS_ERROR(major))
- return major;
-
- return GSS_S_COMPLETE;
-}
-
-static OM_uint32
-verifyGssChannelBindings(OM_uint32 *minor,
- gss_cred_id_t cred,
- gss_ctx_id_t ctx,
- gss_channel_bindings_t chanBindings,
- gss_buffer_t inputToken)
-{
- OM_uint32 major, tmpMinor;
- gss_iov_buffer_desc iov[2];
-
- iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
- iov[0].buffer.length = 0;
- iov[0].buffer.value = NULL;
-
- iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM;
- iov[1].buffer = *inputToken;
-
- major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
- iov, 2, TOK_TYPE_WRAP);
- if (GSS_ERROR(major))
- return GSS_S_BAD_BINDINGS;
-
- if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS &&
- !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
- major = GSS_S_BAD_BINDINGS;
- *minor = GSSEAP_BINDINGS_MISMATCH;
- } else {
- major = GSS_S_COMPLETE;
- }
-
- gss_release_buffer(&tmpMinor, &iov[0].buffer);
-
- return major;
-}
-
-static struct gss_eap_extension_provider
-eapGssInitExtensions[] = {
- {
- EXT_TYPE_GSS_CHANNEL_BINDINGS,
- 1, /* critical */
- 1, /* required */
- makeGssChannelBindings,
- verifyGssChannelBindings
- },
-};
-
-/*
- * Acceptor extensions
- */
-static OM_uint32
-makeReauthCreds(OM_uint32 *minor,
- gss_cred_id_t cred,
- gss_ctx_id_t ctx,
- gss_channel_bindings_t chanBindings,
- gss_buffer_t outputToken)
-{
- OM_uint32 major = GSS_S_UNAVAILABLE;
-
-#ifdef GSSEAP_ENABLE_REAUTH
- /*
- * If we're built with fast reauthentication enabled, then
- * fabricate a ticket from the initiator to ourselves.
- */
- major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken);
-#endif
-
- return major;
-}
-
-static OM_uint32
-verifyReauthCreds(OM_uint32 *minor,
- gss_cred_id_t cred,
- gss_ctx_id_t ctx,
- gss_channel_bindings_t chanBindings,
- gss_buffer_t inputToken)
-{
- OM_uint32 major = GSS_S_UNAVAILABLE;
-
-#ifdef GSSEAP_ENABLE_REAUTH
- major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
-#endif
-
- return major;
-}
-
-static struct gss_eap_extension_provider
-eapGssAcceptExtensions[] = {
- {
- EXT_TYPE_REAUTH_CREDS,
- 0, /* critical */
- 0, /* required */
- makeReauthCreds,
- verifyReauthCreds
- },
-};
-
-OM_uint32
-makeExtensions(OM_uint32 *minor,
- gss_cred_id_t cred,
- gss_ctx_id_t ctx,
- const struct gss_eap_extension_provider *exts,
- size_t nexts,
- gss_channel_bindings_t chanBindings,
- gss_buffer_t buffer)
-{
- OM_uint32 major, tmpMinor;
- size_t i, j;
- gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET;
- OM_uint32 *types;
-
- assert(buffer != GSS_C_NO_BUFFER);
-
- buffer->length = 0;
- buffer->value = NULL;
-
- types = GSSEAP_CALLOC(nexts, sizeof(OM_uint32));
- if (types == NULL) {
- major = GSS_S_FAILURE;
- *minor = ENOMEM;
- goto cleanup;
- }
-
- for (i = 0, j = 0; i < nexts; i++) {
- const struct gss_eap_extension_provider *ext = &exts[i];
- gss_buffer_desc extension = GSS_C_EMPTY_BUFFER;
-
- types[j] = ext->type;
- if (ext->critical)
- types[j] |= EXT_FLAG_CRITICAL;
-
- major = ext->make(minor, cred, ctx, chanBindings, &extension);
- if (GSS_ERROR(major)) {
- if (ext->critical)
- goto cleanup;
- else
- continue;
- }
-
- major = gss_add_buffer_set_member(minor, &extension, &extensions);
- if (GSS_ERROR(major))
- goto cleanup;
-
- j++;
- }
-
- assert(j == (extensions == GSS_C_NO_BUFFER_SET ? 0 : extensions->count));
-
- major = encodeExtensions(minor, extensions, types, buffer);
- if (GSS_ERROR(major))
- goto cleanup;
-
-cleanup:
- gss_release_buffer_set(&tmpMinor, &extensions);
- if (types != NULL)
- GSSEAP_FREE(types);
-
- return major;
-}
-
-OM_uint32
-gssEapMakeExtensions(OM_uint32 *minor,
- gss_cred_id_t cred,
- gss_ctx_id_t ctx,
- gss_channel_bindings_t chanBindings,
- gss_buffer_t buffer)
-{
- size_t nexts;
- const struct gss_eap_extension_provider *exts;
-
- if (CTX_IS_INITIATOR(ctx)) {
- exts = eapGssInitExtensions;
- nexts = sizeof(eapGssInitExtensions) / sizeof(eapGssInitExtensions[0]);
- } else {
- exts = eapGssAcceptExtensions;
- nexts = sizeof(eapGssAcceptExtensions) / sizeof(eapGssAcceptExtensions[0]);
- }
-
- return makeExtensions(minor, cred, ctx, exts, nexts, chanBindings, buffer);
-}
-
-static OM_uint32
-verifyExtensions(OM_uint32 *minor,
- gss_cred_id_t cred,
- gss_ctx_id_t ctx,
- const struct gss_eap_extension_provider *exts,
- size_t nexts,
- gss_channel_bindings_t chanBindings,
- const gss_buffer_t buffer)
-{
- OM_uint32 major, tmpMinor;
- gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET;
- OM_uint32 *types = NULL;
- size_t i;
-
- major = decodeExtensions(minor, buffer, &extensions, &types);
- if (GSS_ERROR(major))
- goto cleanup;
-
- for (i = 0; i < nexts; i++) {
- const struct gss_eap_extension_provider *ext = &exts[i];
- gss_buffer_t extension = GSS_C_NO_BUFFER;
- size_t j;
-
- for (j = 0; j < extensions->count; j++) {
- if ((types[j] & EXT_TYPE_MASK) == ext->type) {
- extension = &extensions->elements[j];
- break;
- }
- }
-
- if (extension != GSS_C_NO_BUFFER) {
- /* Process extension and mark as verified */
- major = ext->verify(minor, cred, ctx, chanBindings,
- &extensions->elements[j]);
- if (GSS_ERROR(major))
- goto cleanup;
-
- types[j] |= EXT_FLAG_VERIFIED;
- } else if (ext->required) {
- /* Required extension missing */
- major = GSS_S_UNAVAILABLE;
- *minor = GSSEAP_MISSING_REQUIRED_EXT;
- goto cleanup;
- }
- }
-
- /* Check we processed all critical extensions */
- for (i = 0; i < extensions->count; i++) {
- if ((types[i] & EXT_FLAG_CRITICAL) &&
- (types[i] & EXT_FLAG_VERIFIED) == 0) {
- major = GSS_S_UNAVAILABLE;
- *minor = GSSEAP_CRIT_EXT_UNAVAILABLE;
- goto cleanup;
- }
- }
-
- major = GSS_S_COMPLETE;
- *minor = 0;
-
-cleanup:
- gss_release_buffer_set(&tmpMinor, &extensions);
- if (types != NULL)
- GSSEAP_FREE(types);
-
- return major;
-}
-
-OM_uint32
-gssEapVerifyExtensions(OM_uint32 *minor,
- gss_cred_id_t cred,
- gss_ctx_id_t ctx,
- gss_channel_bindings_t chanBindings,
- const gss_buffer_t buffer)
-{
- size_t nexts;
- const struct gss_eap_extension_provider *exts;
-
- if (CTX_IS_INITIATOR(ctx)) {
- exts = eapGssAcceptExtensions;
- nexts = sizeof(eapGssAcceptExtensions) / sizeof(eapGssAcceptExtensions[0]);
- } else {
- exts = eapGssInitExtensions;
- nexts = sizeof(eapGssInitExtensions) / sizeof(eapGssInitExtensions[0]);
- }
-
- return verifyExtensions(minor, cred, ctx, exts, nexts, chanBindings, buffer);
-}
-
-static OM_uint32
-encodeExtensions(OM_uint32 *minor,
- gss_buffer_set_t extensions,
- OM_uint32 *types,
- gss_buffer_t buffer)
-{
- OM_uint32 major, tmpMinor;
- size_t required = 0, i;
- unsigned char *p;
-
- buffer->value = NULL;
- buffer->length = 0;
-
- if (extensions != GSS_C_NO_BUFFER_SET) {
- for (i = 0; i < extensions->count; i++) {
- required += 8 + extensions->elements[i].length;
- }
- }
-
- /*
- * We must always return a non-NULL token otherwise the calling state
- * machine assumes we are finished. Hence care in case malloc(0) does
- * return NULL.
- */
- buffer->value = GSSEAP_MALLOC(required ? required : 1);
- if (buffer->value == NULL) {
- major = GSS_S_FAILURE;
- *minor = ENOMEM;
- goto cleanup;
- }
-
- buffer->length = required;
- p = (unsigned char *)buffer->value;
-
- if (extensions != GSS_C_NO_BUFFER_SET) {
- for (i = 0; i < extensions->count; i++) {
- gss_buffer_t extension = &extensions->elements[i];
-
- assert((types[i] & EXT_FLAG_VERIFIED) == 0); /* private flag */
-
- /*
- * Extensions are encoded as type-length-value, where the upper
- * bit of the type indicates criticality.
- */
- store_uint32_be(types[i], &p[0]);
- store_uint32_be(extension->length, &p[4]);
- memcpy(&p[8], extension->value, extension->length);
-
- p += 8 + extension->length;
- }
- }
-
- assert(p == (unsigned char *)buffer->value + required);
- assert(buffer->value != NULL);
-
- major = GSS_S_COMPLETE;
- *minor = 0;
-
-cleanup:
- if (GSS_ERROR(major)) {
- gss_release_buffer(&tmpMinor, buffer);
- }
-
- return major;
-}
-
-static OM_uint32
-decodeExtensions(OM_uint32 *minor,
- const gss_buffer_t buffer,
- gss_buffer_set_t *pExtensions,
- OM_uint32 **pTypes)
-{
- OM_uint32 major, tmpMinor;
- gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET;
- OM_uint32 *types = NULL;
- unsigned char *p;
- size_t remain;
-
- *pExtensions = GSS_C_NO_BUFFER_SET;
- *pTypes = NULL;
-
- major = gss_create_empty_buffer_set(minor, &extensions);
- if (GSS_ERROR(major))
- goto cleanup;
-
- if (buffer->length == 0) {
- major = GSS_S_COMPLETE;
- goto cleanup;
- }
-
- p = (unsigned char *)buffer->value;
- remain = buffer->length;
-
- do {
- OM_uint32 *ntypes;
- gss_buffer_desc extension;
-
- if (remain < 8) {
- major = GSS_S_DEFECTIVE_TOKEN;
- *minor = GSSEAP_TOK_TRUNC;
- goto cleanup;
- }
-
- ntypes = GSSEAP_REALLOC(types,
- (extensions->count + 1) * sizeof(OM_uint32));
- if (ntypes == NULL) {
- major = GSS_S_FAILURE;
- *minor = ENOMEM;
- goto cleanup;
- }
- types = ntypes;
-
- types[extensions->count] = load_uint32_be(&p[0]);
- extension.length = load_uint32_be(&p[4]);
-
- if (remain < 8 + extension.length) {
- major = GSS_S_DEFECTIVE_TOKEN;
- *minor = GSSEAP_TOK_TRUNC;
- goto cleanup;
- }
- extension.value = &p[8];
-
- major = gss_add_buffer_set_member(minor, &extension, &extensions);
- if (GSS_ERROR(major))
- goto cleanup;
-
- p += 8 + extension.length;
- remain -= 8 + extension.length;
- } while (remain != 0);
-
-cleanup:
- if (GSS_ERROR(major)) {
- gss_release_buffer_set(&tmpMinor, &extensions);
- if (types != NULL)
- GSSEAP_FREE(types);
- } else {
- *pExtensions = extensions;
- *pTypes = types;
- }
-
- return major;
-}
bool
gss_eap_radius_attr_provider::init(void)
{
+ struct rs_context *radContext;
+
gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS,
"urn:ietf:params:gss-eap:radius-avp",
createAttrContext);
+
+#if 1
+ /*
+ * This hack is necessary in order to force the loading of the global
+ * dictionary, otherwise accepting reauthentication tokens fails unless
+ * the acceptor has already accepted a normal authentication token.
+ */
+ if (rs_context_create(&radContext, RS_DICT_FILE) != 0) {
+ return false;
+ }
+
+ rs_context_destroy(radContext);
+#endif
+
return true;
}
remain -= 4;
da = dict_attrbyvalue(attrid);
- if (da == NULL)
- goto fail;
-
- vp = pairalloc(da);
+ if (da != NULL) {
+ vp = pairalloc(da);
+ } else {
+ vp = paircreate(attrid, PW_TYPE_STRING);
+ }
if (vp == NULL) {
throw new std::bad_alloc;
goto fail;
return true;
fail:
- pairbasicfree(vp);
+ if (vp != NULL)
+ pairbasicfree(vp);
+ *pVp = NULL;
return false;
}
struct rs_error *err);
#define RS_CONFIG_FILE SYSCONFDIR "/radsec.conf"
-#define RS_DICT_FILE DATAROOTDIR "/freeradius/dictionary"
+#define RS_DICT_FILE SYSCONFDIR "/raddb/dictionary"
#define VENDORPEC_MS 311 /* RFC 2548 */
*/
OM_uint32
gssEapReauthComplete(OM_uint32 *minor,
- gss_ctx_id_t ctx,
- gss_cred_id_t cred,
- const gss_OID mech,
- OM_uint32 timeRec)
+ 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;
--- /dev/null
+/*
+ * Copyright (c) 2011, 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.
+ */
+
+/*
+ * Context establishment state machine.
+ */
+
+#include "gssapiP_eap.h"
+
+/* private flags */
+#define SM_FLAG_TRANSITED 0x80000000
+
+#define SM_ASSERT_VALID(ctx, status) do { \
+ assert(GSS_ERROR((status)) || \
+ ((status) == GSS_S_CONTINUE_NEEDED && ((ctx)->state > GSSEAP_STATE_INITIAL && (ctx)->state < GSSEAP_STATE_ESTABLISHED)) || \
+ ((status) == GSS_S_COMPLETE && (ctx)->state == GSSEAP_STATE_ESTABLISHED)); \
+ } while (0)
+
+#ifdef GSSEAP_DEBUG
+static const char *
+gssEapStateToString(enum gss_eap_state state)
+{
+ const char *s;
+
+ switch (state) {
+ case GSSEAP_STATE_INITIAL:
+ s = "INITIAL";
+ break;
+ case GSSEAP_STATE_AUTHENTICATE:
+ s = "AUTHENTICATE";
+ break;
+ case GSSEAP_STATE_INITIATOR_EXTS:
+ s = "INITIATOR_EXTS";
+ break;
+ case GSSEAP_STATE_ACCEPTOR_EXTS:
+ s = "ACCEPTOR_EXTS";
+ break;
+#ifdef GSSEAP_ENABLE_REAUTH
+ case GSSEAP_STATE_REAUTHENTICATE:
+ s = "REAUTHENTICATE";
+ break;
+#endif
+ case GSSEAP_STATE_ESTABLISHED:
+ s = "ESTABLISHED";
+ break;
+ default:
+ s = "INVALID";
+ break;
+ }
+
+ return s;
+}
+
+void
+gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state)
+{
+ assert(state >= GSSEAP_STATE_INITIAL);
+ assert(state <= GSSEAP_STATE_ESTABLISHED);
+
+ fprintf(stderr, "GSS-EAP: state transition %s->%s\n",
+ gssEapStateToString(GSSEAP_SM_STATE(ctx)),
+ gssEapStateToString(state));
+
+ ctx->state = state;
+}
+#endif /* GSSEAP_DEBUG */
+
+static OM_uint32
+makeErrorToken(OM_uint32 *minor,
+ OM_uint32 majorStatus,
+ OM_uint32 minorStatus,
+ gss_buffer_set_t *outputToken)
+{
+ OM_uint32 major;
+ unsigned char errorData[8];
+ gss_buffer_desc errorBuffer;
+
+ assert(GSS_ERROR(majorStatus));
+
+ major = gss_create_empty_buffer_set(minor, outputToken);
+ if (GSS_ERROR(major))
+ return major;
+
+ /*
+ * Only return error codes that the initiator could have caused,
+ * to avoid information leakage.
+ */
+ if (IS_RADIUS_ERROR(minorStatus)) {
+ /* Squash RADIUS error codes */
+ minorStatus = GSSEAP_RADIUS_PROT_FAILURE;
+ } else if (!IS_WIRE_ERROR(minorStatus)) {
+ /* Don't return non-wire error codes */
+ return GSS_S_COMPLETE;
+ }
+
+ minorStatus -= ERROR_TABLE_BASE_eapg;
+
+ store_uint32_be(majorStatus, &errorData[0]);
+ store_uint32_be(minorStatus, &errorData[4]);
+
+ errorBuffer.length = sizeof(errorData);
+ errorBuffer.value = errorData;
+
+ major = gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
+ if (GSS_ERROR(major))
+ return major;
+
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+allocInnerTokens(OM_uint32 *minor,
+ size_t count,
+ gss_buffer_set_t *pTokens,
+ OM_uint32 **pTokenTypes)
+{
+ OM_uint32 major, tmpMinor;
+ gss_buffer_set_t tokens = GSS_C_NO_BUFFER_SET;
+ OM_uint32 *tokenTypes = NULL;
+
+ major = gss_create_empty_buffer_set(minor, &tokens);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ assert(tokens->count == 0);
+ assert(tokens->elements == NULL);
+
+ tokens->elements = (gss_buffer_desc *)GSSEAP_CALLOC(count, sizeof(gss_buffer_desc));
+ if (tokens->elements == NULL) {
+ major = GSS_S_FAILURE;
+ *minor = ENOMEM;
+ goto cleanup;
+ }
+
+ tokenTypes = (OM_uint32 *)GSSEAP_CALLOC(count, sizeof(OM_uint32));
+ if (tokenTypes == NULL) {
+ major = GSS_S_FAILURE;
+ *minor = ENOMEM;
+ goto cleanup;
+ }
+
+ major = GSS_S_COMPLETE;
+ *minor = 0;
+
+cleanup:
+ if (GSS_ERROR(major)) {
+ gss_release_buffer_set(&tmpMinor, &tokens);
+ tokens = GSS_C_NO_BUFFER_SET;
+ if (tokenTypes != NULL) {
+ GSSEAP_FREE(tokenTypes);
+ tokenTypes = NULL;
+ }
+ }
+
+ *pTokens = tokens;
+ *pTokenTypes = tokenTypes;
+
+ return major;
+}
+
+OM_uint32
+gssEapSmStep(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,
+ struct gss_eap_sm *sm, /* ordered by state */
+ size_t smCount)
+{
+ OM_uint32 major, tmpMajor, tmpMinor;
+ gss_buffer_desc unwrappedInputToken = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc unwrappedOutputToken = GSS_C_EMPTY_BUFFER;
+ gss_buffer_set_t innerInputTokens = GSS_C_NO_BUFFER_SET;
+ gss_buffer_set_t innerOutputTokens = GSS_C_NO_BUFFER_SET;
+ OM_uint32 *inputTokenTypes = NULL, *outputTokenTypes = NULL;
+ unsigned int smFlags = 0;
+ size_t i, j;
+ int initialContextToken = 0;
+ enum gss_eap_token_type tokType;
+
+ assert(smCount > 0);
+
+ *minor = 0;
+
+ outputToken->length = 0;
+ outputToken->value = NULL;
+
+ if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
+ major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
+ &unwrappedInputToken);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ if (tokType != (CTX_IS_INITIATOR(ctx)
+ ? TOK_TYPE_ACCEPTOR_CONTEXT : TOK_TYPE_INITIATOR_CONTEXT)) {
+ major = GSS_S_DEFECTIVE_TOKEN;
+ *minor = GSSEAP_WRONG_TOK_ID;
+ goto cleanup;
+ }
+ } else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) {
+ major = GSS_S_DEFECTIVE_TOKEN;
+ *minor = GSSEAP_WRONG_SIZE;
+ goto cleanup;
+ } else {
+ initialContextToken = 1;
+ }
+
+ if (CTX_IS_ESTABLISHED(ctx)) {
+ major = GSS_S_BAD_STATUS;
+ *minor = GSSEAP_CONTEXT_ESTABLISHED;
+ goto cleanup;
+ }
+
+ assert(ctx->state < GSSEAP_STATE_ESTABLISHED);
+
+ major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken,
+ &innerInputTokens, &inputTokenTypes);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ assert(innerInputTokens != GSS_C_NO_BUFFER_SET);
+
+ major = allocInnerTokens(minor, smCount, &innerOutputTokens, &outputTokenTypes);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ /* Process all the tokens that are valid for the current state. */
+ for (i = 0; i < smCount; i++) {
+ struct gss_eap_sm *smp = &sm[i];
+ int processToken = 0;
+ gss_buffer_t innerInputToken = GSS_C_NO_BUFFER;
+ OM_uint32 *inputTokenType = NULL;
+ gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
+
+ if ((smp->validStates & ctx->state) == 0)
+ continue;
+
+ /*
+ * We special case the first call to gss_init_sec_context so that
+ * all token providers have the opportunity to generate an initial
+ * context token. Providers where inputTokenType is ITOK_TYPE_NONE
+ * are always called and generally act on state transition boundaries,
+ * for example to advance the state after a series of optional tokens
+ * (as is the case with the extension token exchange) or to generate
+ * a new token after the state was advanced by a provider which did
+ * not emit a token.
+ */
+ if (smp->inputTokenType == ITOK_TYPE_NONE || initialContextToken) {
+ processToken = 1;
+ } else if ((smFlags & SM_FLAG_TRANSITED) == 0) {
+ /* Don't regurgitate a token which belonds to a previous state. */
+ for (j = 0; j < innerInputTokens->count; j++) {
+ if ((inputTokenTypes[j] & ITOK_TYPE_MASK) == smp->inputTokenType) {
+ if (processToken) {
+ /* Check for duplicate inner tokens */
+ major = GSS_S_DEFECTIVE_TOKEN;
+ *minor = GSSEAP_DUPLICATE_ITOK;
+ break;
+ }
+ processToken = 1;
+ innerInputToken = &innerInputTokens->elements[j];
+ inputTokenType = &inputTokenTypes[j];
+ }
+ }
+ if (GSS_ERROR(major))
+ break;
+ }
+
+ if (processToken) {
+ enum gss_eap_state oldState = ctx->state;
+
+ smFlags = 0;
+ if (inputTokenType != NULL && (*inputTokenType & ITOK_FLAG_CRITICAL))
+ smFlags |= SM_FLAG_INPUT_TOKEN_CRITICAL;
+
+ major = smp->processToken(minor, cred, ctx, target, mech, reqFlags,
+ timeReq, chanBindings, innerInputToken,
+ &innerOutputToken, &smFlags);
+ if (GSS_ERROR(major))
+ break;
+
+ if (inputTokenType != NULL)
+ *inputTokenType |= ITOK_FLAG_VERIFIED;
+ if (ctx->state < oldState)
+ i = 0; /* restart */
+ else if (ctx->state != oldState)
+ smFlags |= SM_FLAG_TRANSITED;
+
+ if (innerOutputToken.value != NULL) {
+ innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken;
+ assert(smp->outputTokenType != ITOK_TYPE_NONE);
+ outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType;
+ if (smFlags & SM_FLAG_OUTPUT_TOKEN_CRITICAL)
+ outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
+ innerOutputTokens->count++;
+ }
+ /*
+ * Break out if we made a state transition and have some tokens to send.
+ */
+ if ((smFlags & SM_FLAG_TRANSITED) &&
+ ((smFlags & SM_FLAG_FORCE_SEND_TOKEN) || innerOutputTokens->count != 0)) {
+ SM_ASSERT_VALID(ctx, major);
+ break;
+ }
+ } else if ((smp->itokFlags & SM_ITOK_FLAG_REQUIRED) &&
+ smp->inputTokenType != ITOK_TYPE_NONE) {
+ /* Check for required inner tokens */
+ major = GSS_S_DEFECTIVE_TOKEN;
+ *minor = GSSEAP_MISSING_REQUIRED_ITOK;
+ break;
+ }
+ }
+
+ assert(innerOutputTokens->count <= smCount);
+
+ /* Check we understood all critical tokens sent by peer */
+ if (!GSS_ERROR(major)) {
+ for (j = 0; j < innerInputTokens->count; j++) {
+ if ((inputTokenTypes[j] & ITOK_FLAG_CRITICAL) &&
+ (inputTokenTypes[j] & ITOK_FLAG_VERIFIED) == 0) {
+ major = GSS_S_UNAVAILABLE;
+ *minor = GSSEAP_CRIT_ITOK_UNAVAILABLE;
+ goto cleanup;
+ }
+ }
+ }
+
+ /* Optionaly emit an error token if we are the acceptor */
+ if (GSS_ERROR(major)) {
+ if (CTX_IS_INITIATOR(ctx))
+ goto cleanup; /* return error directly to caller */
+
+ /* replace any emitted tokens with error token */
+ gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
+
+ tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputTokens);
+ if (GSS_ERROR(tmpMajor)) {
+ major = tmpMajor;
+ *minor = tmpMinor;
+ goto cleanup;
+ }
+
+ if (innerOutputTokens->count != 0)
+ outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
+ }
+
+ /* Format output token from inner tokens */
+ if (innerOutputTokens->count != 0 || /* inner tokens to send */
+ !CTX_IS_INITIATOR(ctx) || /* any leg acceptor */
+ !CTX_IS_ESTABLISHED(ctx)) { /* non-last leg initiator */
+ tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens,
+ outputTokenTypes, &unwrappedOutputToken);
+ if (tmpMajor == GSS_S_COMPLETE) {
+ if (CTX_IS_INITIATOR(ctx))
+ tokType = TOK_TYPE_INITIATOR_CONTEXT;
+ else
+ tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
+
+ tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
+ tokType, outputToken);
+ if (GSS_ERROR(tmpMajor)) {
+ major = tmpMajor;
+ *minor = tmpMinor;
+ goto cleanup;
+ }
+ }
+ }
+
+ /* If the context is established, empty tokens only to be emitted by initiator */
+ assert(!CTX_IS_ESTABLISHED(ctx) || ((outputToken->length == 0) == CTX_IS_INITIATOR(ctx)));
+
+ SM_ASSERT_VALID(ctx, major);
+
+cleanup:
+ gss_release_buffer_set(&tmpMinor, &innerInputTokens);
+ gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
+ if (inputTokenTypes != NULL)
+ GSSEAP_FREE(inputTokenTypes);
+ if (outputTokenTypes != NULL)
+ gss_release_buffer(&tmpMinor, &unwrappedOutputToken);
+ GSSEAP_FREE(outputTokenTypes);
+
+ return major;
+}
* SUCH DAMAGE.
*/
/*
- * Copyright 1993 by OpenVision Technologies, Inc.
+ * Portions Copyright 1993 by OpenVision Technologies, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without fee,
#include "gssapiP_eap.h"
+OM_uint32
+gssEapEncodeInnerTokens(OM_uint32 *minor,
+ gss_buffer_set_t extensions,
+ OM_uint32 *types,
+ gss_buffer_t buffer)
+{
+ OM_uint32 major, tmpMinor;
+ size_t required = 0, i;
+ unsigned char *p;
+
+ buffer->value = NULL;
+ buffer->length = 0;
+
+ if (extensions != GSS_C_NO_BUFFER_SET) {
+ for (i = 0; i < extensions->count; i++) {
+ required += 8 + extensions->elements[i].length;
+ }
+ }
+
+ /*
+ * We must always return a non-NULL token otherwise the calling state
+ * machine assumes we are finished. Hence care in case malloc(0) does
+ * return NULL.
+ */
+ buffer->value = GSSEAP_MALLOC(required ? required : 1);
+ if (buffer->value == NULL) {
+ major = GSS_S_FAILURE;
+ *minor = ENOMEM;
+ goto cleanup;
+ }
+
+ buffer->length = required;
+ p = (unsigned char *)buffer->value;
+
+ if (extensions != GSS_C_NO_BUFFER_SET) {
+ for (i = 0; i < extensions->count; i++) {
+ gss_buffer_t extension = &extensions->elements[i];
+
+ assert((types[i] & ITOK_FLAG_VERIFIED) == 0); /* private flag */
+
+ /*
+ * Extensions are encoded as type-length-value, where the upper
+ * bit of the type indicates criticality.
+ */
+ store_uint32_be(types[i], &p[0]);
+ store_uint32_be(extension->length, &p[4]);
+ memcpy(&p[8], extension->value, extension->length);
+
+ p += 8 + extension->length;
+ }
+ }
+
+ assert(p == (unsigned char *)buffer->value + required);
+ assert(buffer->value != NULL);
+
+ major = GSS_S_COMPLETE;
+ *minor = 0;
+
+cleanup:
+ if (GSS_ERROR(major)) {
+ gss_release_buffer(&tmpMinor, buffer);
+ }
+
+ return major;
+}
+
+OM_uint32
+gssEapDecodeInnerTokens(OM_uint32 *minor,
+ const gss_buffer_t buffer,
+ gss_buffer_set_t *pExtensions,
+ OM_uint32 **pTypes)
+{
+ OM_uint32 major, tmpMinor;
+ gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET;
+ OM_uint32 *types = NULL;
+ unsigned char *p;
+ size_t remain;
+
+ *pExtensions = GSS_C_NO_BUFFER_SET;
+ *pTypes = NULL;
+
+ major = gss_create_empty_buffer_set(minor, &extensions);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ if (buffer->length == 0) {
+ major = GSS_S_COMPLETE;
+ goto cleanup;
+ }
+
+ p = (unsigned char *)buffer->value;
+ remain = buffer->length;
+
+ do {
+ OM_uint32 *ntypes;
+ gss_buffer_desc extension;
+
+ if (remain < 8) {
+ major = GSS_S_DEFECTIVE_TOKEN;
+ *minor = GSSEAP_TOK_TRUNC;
+ goto cleanup;
+ }
+
+ ntypes = GSSEAP_REALLOC(types,
+ (extensions->count + 1) * sizeof(OM_uint32));
+ if (ntypes == NULL) {
+ major = GSS_S_FAILURE;
+ *minor = ENOMEM;
+ goto cleanup;
+ }
+ types = ntypes;
+
+ types[extensions->count] = load_uint32_be(&p[0]);
+ extension.length = load_uint32_be(&p[4]);
+
+ if (remain < 8 + extension.length) {
+ major = GSS_S_DEFECTIVE_TOKEN;
+ *minor = GSSEAP_TOK_TRUNC;
+ goto cleanup;
+ }
+ extension.value = &p[8];
+
+ major = gss_add_buffer_set_member(minor, &extension, &extensions);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ p += 8 + extension.length;
+ remain -= 8 + extension.length;
+ } while (remain != 0);
+
+cleanup:
+ if (GSS_ERROR(major)) {
+ gss_release_buffer_set(&tmpMinor, &extensions);
+ if (types != NULL)
+ GSSEAP_FREE(types);
+ } else {
+ *pExtensions = extensions;
+ *pTypes = types;
+ }
+
+ return major;
+}
+
/*
* $Id: util_token.c 23457 2009-12-08 00:04:48Z tlyu $
*/
size_t
tokenSize(const gss_OID_desc *mech, size_t body_size)
{
+ assert(mech != GSS_C_NO_OID);
+
/* set body_size to sequence contents size */
body_size += 4 + (size_t) mech->length; /* NEED overflow check */
return 1 + der_length_size(body_size) + body_size;