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) $<
#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 __attribute__((__unused__)),
+ gss_OID mech __attribute__((__unused__)),
+ OM_uint32 reqFlags __attribute__((__unused__)),
+ OM_uint32 timeReq __attribute__((__unused__)),
gss_channel_bindings_t chanBindings,
+ gss_buffer_t inputToken,
gss_buffer_t outputToken);
#endif
*/
static OM_uint32
eapGssSmAcceptIdentity(OM_uint32 *minor,
- gss_ctx_id_t ctx,
gss_cred_id_t cred,
+ gss_ctx_id_t ctx,
+ gss_name_t target __attribute__((__unused__)),
+ gss_OID mech __attribute__((__unused__)),
+ OM_uint32 reqFlags __attribute__((__unused__)),
+ OM_uint32 timeReq __attribute__((__unused__)),
+ gss_channel_bindings_t chanBindings __attribute__((__unused__)),
gss_buffer_t inputToken,
- gss_channel_bindings_t chanBindings,
gss_buffer_t outputToken)
{
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;
if (GSS_ERROR(major))
return major;
- ctx->state = GSSEAP_STATE_AUTHENTICATE;
-
wpabuf_free(reqData);
*minor = 0;
- return GSS_S_CONTINUE_NEEDED;
+ return GSS_S_COMPLETE; /* advance state */
}
/*
*/
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 __attribute__((__unused__)),
+ gss_OID mech __attribute__((__unused__)),
+ OM_uint32 reqFlags __attribute__((__unused__)),
+ OM_uint32 timeReq __attribute__((__unused__)),
gss_channel_bindings_t chanBindings,
+ gss_buffer_t inputToken,
gss_buffer_t outputToken)
{
OM_uint32 major, tmpMinor;
switch (frresp->code) {
case PW_AUTHENTICATION_ACK:
case PW_ACCESS_CHALLENGE:
- major = GSS_S_CONTINUE_NEEDED;
break;
case PW_AUTHENTICATION_REJECT:
*minor = GSSEAP_RADIUS_AUTH_FAILURE;
&ctx->acceptorCtx.state, TRUE);
if (GSS_ERROR(major) && *minor != GSSEAP_NO_SUCH_ATTR)
goto cleanup;
+
+ major = GSS_S_CONTINUE_NEEDED;
} else {
ctx->acceptorCtx.vps = frresp->vps;
frresp->vps = NULL;
if (GSS_ERROR(major))
goto cleanup;
- ctx->state = GSSEAP_STATE_EXTENSIONS_REQ;
+ major = GSS_S_COMPLETE; /* advance state */
}
- *minor = 0;
- major = GSS_S_CONTINUE_NEEDED;
-
cleanup:
if (request != NULL)
rs_request_destroy(request);
}
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 __attribute__((__unused__)),
+ gss_OID mech __attribute__((__unused__)),
+ OM_uint32 reqFlags __attribute__((__unused__)),
+ OM_uint32 timeReq __attribute__((__unused__)),
+ gss_channel_bindings_t chanBindings,
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken)
{
- 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; /* process additional extensions */
+ *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 __attribute__((__unused__)),
+ gss_OID mech __attribute__((__unused__)),
+ OM_uint32 reqFlags __attribute__((__unused__)),
+ OM_uint32 timeReq __attribute__((__unused__)),
+ gss_channel_bindings_t chanBindings __attribute__((__unused__)),
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken)
{
OM_uint32 major;
- major = gssEapMakeExtensions(minor, cred, ctx, chanBindings, outputToken);
+ /*
+ * If we're built with fast reauthentication enabled, then
+ * fabricate a ticket from the initiator to ourselves.
+ */
+ major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken);
if (GSS_ERROR(major))
return major;
- ctx->state = GSSEAP_STATE_ESTABLISHED;
-
*minor = 0;
- return GSS_S_COMPLETE;
-}
-
-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)
-{
- /* Called with already established context */
- *minor = GSSEAP_CONTEXT_ESTABLISHED;
- return GSS_S_BAD_STATUS;
+ return GSS_S_CONTINUE_NEEDED; /* process additional extensions */
}
+#endif
static OM_uint32
-makeErrorToken(OM_uint32 *minor,
- OM_uint32 majorStatus,
- OM_uint32 minorStatus,
- gss_buffer_t outputToken)
+eapGssSmAcceptNegoExtFinished(OM_uint32 *minor,
+ gss_cred_id_t cred,
+ gss_ctx_id_t ctx,
+ gss_name_t target __attribute__((__unused__)),
+ gss_OID mech __attribute__((__unused__)),
+ OM_uint32 reqFlags __attribute__((__unused__)),
+ OM_uint32 timeReq __attribute__((__unused__)),
+ gss_channel_bindings_t chanBindings __attribute__((__unused__)),
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken)
{
- unsigned char errorData[8];
- gss_buffer_desc errorBuffer;
-
- 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;
-
- return duplicateBuffer(minor, &errorBuffer, outputToken);
+ *minor = 0;
+ return GSS_S_COMPLETE; /* advance state */
}
-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[] = {
#ifdef GSSEAP_ENABLE_REAUTH
- { TOK_TYPE_GSS_REAUTH, TOK_TYPE_GSS_REAUTH, eapGssSmAcceptGssReauth },
+ {
+ ITOK_TYPE_REAUTH_REQ,
+ ITOK_TYPE_REAUTH_RESP,
+ GSSEAP_STATE_INITIAL,
+ 0, /* critical */
+ 0, /* required */
+ eapGssSmAcceptGssReauth,
+ },
#endif
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_EAP_REQ,
+ GSSEAP_STATE_INITIAL,
+ 1, /* critical */
+ 1, /* required */
+ eapGssSmAcceptIdentity,
+ },
+ {
+ ITOK_TYPE_EAP_RESP,
+ ITOK_TYPE_EAP_REQ,
+ GSSEAP_STATE_AUTHENTICATE,
+ 1, /* critical */
+ 1, /* required */
+ eapGssSmAcceptAuthenticate
+ },
+ {
+ ITOK_TYPE_GSS_CHANNEL_BINDINGS,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_NEGO_EXT,
+ 1, /* critical */
+ 1, /* required */
+ eapGssSmAcceptGssChannelBindings,
+ },
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_REAUTH_CREDS,
+ GSSEAP_STATE_NEGO_EXT,
+ 0, /* critical */
+ 0, /* required */
+ eapGssSmAcceptReauthCreds,
+ },
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_NEGO_EXT,
+ 1, /* critical */
+ 1, /* required */
+ eapGssSmAcceptNegoExtFinished
+ },
};
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);
+ 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;
- if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
- *minor = GSSEAP_CRED_MECH_MISMATCH;
- major = GSS_S_BAD_MECH;
- 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;
- 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))
duplicateOid(&tmpMinor, 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;
- }
- }
-
cleanup:
if (cred != GSS_C_NO_CREDENTIAL)
GSSEAP_MUTEX_UNLOCK(&cred->mutex);
if (GSS_ERROR(major))
gssEapReleaseContext(&tmpMinor, context_handle);
- gss_release_buffer(&tmpMinor, &innerOutputToken);
-
return major;
}
if (GSS_ERROR(major))
return major;
- ctx->state = GSSEAP_STATE_ESTABLISHED;
+ ctx->state = GSSEAP_STATE_NEGO_EXT; /* skip */
*minor = 0;
- return GSS_S_COMPLETE;
+ return GSS_S_COMPLETE; /* advance state */
}
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 __attribute__((__unused__)),
+ gss_OID mech,
+ OM_uint32 reqFlags __attribute__((__unused__)),
+ OM_uint32 timeReq __attribute__((__unused__)),
gss_channel_bindings_t chanBindings,
+ gss_buffer_t inputToken,
gss_buffer_t outputToken)
{
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,
#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
+ GSSEAP_STATE_INITIAL = 0x01, /* initial state */
+ GSSEAP_STATE_AUTHENTICATE = 0x02, /* exchange EAP messages */
+ GSSEAP_STATE_NEGO_EXT = 0x04, /* negotiate extensions */
+ GSSEAP_STATE_ESTABLISHED = 0x08, /* context established */
+ GSSEAP_STATE_ALL = 0x0F
+};
+
+#define GSSEAP_STATE_NEXT(s) ((s) << 1)
+
+/* state machine entry */
+struct gss_eap_sm {
+ OM_uint32 inputTokenType;
+ OM_uint32 outputTokenType;
+ enum gss_eap_state validStates;
+ int critical;
+ int required;
+ 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);
};
#define CTX_IS_ESTABLISHED(ctx) ((ctx)->state == GSSEAP_STATE_ESTABLISHED)
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"
remain -= 16;
/* Validate state */
- if (ctx->state < GSSEAP_STATE_IDENTITY ||
+ if (ctx->state < GSSEAP_STATE_INITIAL ||
ctx->state > GSSEAP_STATE_ESTABLISHED)
return GSS_S_DEFECTIVE_TOKEN;
#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)
{
OM_uint32 reqFlags,
OM_uint32 timeReq,
gss_channel_bindings_t chanBindings,
- gss_buffer_t inputToken,
- gss_buffer_t outputToken)
+ gss_buffer_t inputToken)
{
OM_uint32 major;
}
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 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 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 (ctx->state == GSSEAP_STATE_INITIAL) {
+ if (!gssEapCanReauthP(cred, target, timeReq))
+ return GSS_S_CONTINUE_NEEDED;
+
+ major = initBegin(minor, cred, ctx, target, mech,
+ reqFlags, timeReq, chanBindings,
+ inputToken);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ 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,
+ 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_NEGO_EXT; /* skip */
+ } else {
+ major = GSS_S_COMPLETE; /* advance state */
+ }
+
+cleanup:
+ gssReleaseName(&tmpMinor, &mechTarget);
+
+ return major;
+}
+#endif /* GSSEAP_ENABLE_REAUTH */
+
+static OM_uint32
eapGssSmInitIdentity(OM_uint32 *minor,
gss_cred_id_t cred,
gss_ctx_id_t ctx,
OM_uint32 major;
int initialContextToken;
+ assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
+
initialContextToken = (inputToken->length == 0);
if (!initialContextToken) {
*minor = GSSEAP_WRONG_SIZE;
major = initBegin(minor, cred, ctx, target, mech,
reqFlags, timeReq, chanBindings,
- inputToken, outputToken);
+ inputToken);
if (GSS_ERROR(major))
return major;
- ctx->state = GSSEAP_STATE_AUTHENTICATE;
+ outputToken->length = 0;
+ outputToken->value = NULL;
*minor = 0;
- return GSS_S_CONTINUE_NEEDED;
+ return GSS_S_COMPLETE; /* advance state */
}
static struct wpabuf emptyWpaBuffer;
wpabuf_set(&ctx->initiatorCtx.reqData,
inputToken->value, inputToken->length);
- major = GSS_S_CONTINUE_NEEDED;
-
code = eap_peer_sm_step(ctx->initiatorCtx.eap);
if (ctx->flags & CTX_FLAG_EAP_RESP) {
ctx->flags &= ~(CTX_FLAG_EAP_RESP);
resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
+ major = GSS_S_CONTINUE_NEEDED;
} else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
major = initReady(minor, ctx, reqFlags);
if (GSS_ERROR(major))
goto cleanup;
ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
- major = GSS_S_CONTINUE_NEEDED;
- ctx->state = GSSEAP_STATE_EXTENSIONS_REQ;
+ major = GSS_S_COMPLETE; /* advance state */
} 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;
OM_uint32 tmpMajor;
gss_buffer_desc respBuf;
- assert(major == GSS_S_CONTINUE_NEEDED);
+ assert(!GSS_ERROR(major));
respBuf.length = wpabuf_len(resp);
respBuf.value = (void *)wpabuf_head(resp);
}
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 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;
}
+#ifdef GSSEAP_ENABLE_REAUTH
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;
-
- major = gssEapVerifyExtensions(minor, cred, ctx, chanBindings, inputToken);
- if (GSS_ERROR(major))
- return major;
-
- ctx->state = GSSEAP_STATE_ESTABLISHED;
-
- *minor = 0;
- return GSS_S_COMPLETE;
-}
-
-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,
gss_buffer_t inputToken,
gss_buffer_t outputToken)
{
- /* Called with already established context */
- *minor = GSSEAP_CONTEXT_ESTABLISHED;
- return GSS_S_BAD_STATUS;
-}
-
-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 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]);
+ major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
+ if (GSS_ERROR(major))
+ return major;
- if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
- major = GSS_S_FAILURE;
- *minor = GSSEAP_BAD_ERROR_TOKEN;
- }
+ *minor = 0;
+ return GSS_S_CONTINUE_NEEDED;
+}
+#endif /* GSSEAP_ENABLE_REAUTH */
- return major;
+static OM_uint32
+eapGssSmInitNegoExtFinished(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)
+{
+ *minor = 0;
+ return GSS_S_COMPLETE; /* advance state */
}
-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,
+ 1, /* critical */
+ 0, /* required */
+ eapGssSmInitError,
+ },
+#ifdef GSSEAP_ENABLE_REAUTH
+ {
+ ITOK_TYPE_REAUTH_RESP,
+ ITOK_TYPE_REAUTH_REQ,
+ GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
+ 0, /* critical */
+ 0, /* required */
+ eapGssSmInitGssReauth,
+ },
+#endif
+ /* first-leg extensions go here, they should return GSS_S_CONTINUE_NEEDED */
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_INITIAL,
+ 1, /* critical */
+ 1, /* required */
+ eapGssSmInitIdentity,
+ },
+ {
+ ITOK_TYPE_EAP_REQ,
+ ITOK_TYPE_EAP_RESP,
+ GSSEAP_STATE_AUTHENTICATE,
+ 1, /* critical */
+ 1, /* required */
+ eapGssSmInitAuthenticate,
+ },
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_GSS_CHANNEL_BINDINGS,
+ GSSEAP_STATE_NEGO_EXT,
+ 1, /* critical */
+ 1, /* required */
+ eapGssSmInitGssChannelBindings,
+ },
#ifdef GSSEAP_ENABLE_REAUTH
- { TOK_TYPE_GSS_REAUTH, TOK_TYPE_GSS_REAUTH, eapGssSmInitGssReauth },
+ {
+ ITOK_TYPE_REAUTH_CREDS,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_NEGO_EXT,
+ 0, /* critical */
+ 0, /* required */
+ eapGssSmInitReauthCreds,
+ },
#endif
+ /* other extensions go here */
+ {
+ ITOK_TYPE_NONE,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_NEGO_EXT,
+ 1, /* critical */
+ 1, /* required */
+ eapGssSmInitNegoExtFinished
+ }
};
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;
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)
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 */
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_ESTABLISH_CONTEXT = 0x0601, /* establish context */
};
+/* inner token types and flags */
+#define ITOK_TYPE_NONE 0x00000000
+#define ITOK_TYPE_CONTEXT_ERR 0x00000001
+#define ITOK_TYPE_ACCEPTOR_NAME_REQ 0x00000002
+#define ITOK_TYPE_ACCEPTOR_NAME_RESP 0x00000003
+#define ITOK_TYPE_EAP_RESP 0x00000004
+#define ITOK_TYPE_EAP_REQ 0x00000005
+#define ITOK_TYPE_GSS_CHANNEL_BINDINGS 0x00000006
+#define ITOK_TYPE_REAUTH_CREDS 0x00000007
+#define ITOK_TYPE_REAUTH_REQ 0x00000008
+#define ITOK_TYPE_REAUTH_RESP 0x00000009
+
+#define ITOK_FLAG_CRITICAL 0x80000000 /* critical, wire flag */
+#define ITOK_FLAG_VERIFIED 0x40000000 /* verified, API flag */
+
+#define ITOK_TYPE_MASK (~(EXT_FLAG_CRITICAL | EXT_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 */
+struct gss_eap_sm;
+
+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);
+
/* 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;
-}
--- /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"
+
+static OM_uint32
+makeErrorToken(OM_uint32 *minor,
+ OM_uint32 majorStatus,
+ OM_uint32 minorStatus,
+ gss_buffer_set_t *outputToken)
+{
+ unsigned char errorData[8];
+ gss_buffer_desc errorBuffer;
+
+ 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;
+
+ return gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
+}
+
+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)
+{
+ 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;
+ size_t i, j;
+ enum gss_eap_state inputState = ctx->state;
+
+ assert(smCount > 0);
+
+ *minor = 0;
+
+ outputToken->length = 0;
+ outputToken->value = NULL;
+
+ if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
+ enum gss_eap_token_type tokType;
+
+ major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
+ &unwrappedInputToken);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ if (tokType != TOK_TYPE_ESTABLISH_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;
+ }
+
+ if (ctx->state == GSSEAP_STATE_ESTABLISHED) {
+ 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;
+
+ major = gss_create_empty_buffer_set(minor, &innerOutputTokens);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ assert(innerOutputTokens->count == 0);
+ assert(innerOutputTokens->elements == NULL);
+
+ innerOutputTokens->elements = (gss_buffer_desc *)GSSEAP_CALLOC(smCount,
+ sizeof(gss_buffer_desc));
+ if (innerOutputTokens->elements == NULL) {
+ major = GSS_S_FAILURE;
+ *minor = ENOMEM;
+ goto cleanup;
+ }
+
+ outputTokenTypes = (OM_uint32 *)GSSEAP_CALLOC(smCount, sizeof(OM_uint32));
+ if (outputTokenTypes == NULL) {
+ major = GSS_S_FAILURE;
+ *minor = ENOMEM;
+ goto cleanup;
+ }
+
+ /*
+ * Process all the tokens that are valid for the current state. If
+ * the processToken function returns GSS_S_COMPLETE, the state is
+ * advanced until there is a token to send or the ESTABLISHED state
+ * is reached.
+ */
+ do {
+ major = GSS_S_COMPLETE;
+
+ 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;
+
+ if (innerInputTokens == GSS_C_NO_BUFFER_SET) {
+ processToken = ((smp->validStates & GSSEAP_STATE_INITIAL) != 0);
+ } else if (inputState != ctx->state) {
+ processToken = (smp->inputTokenType == ITOK_TYPE_NONE);
+ } else {
+ for (j = 0; j < innerInputTokens->count; j++) {
+ processToken = (smp->inputTokenType == inputTokenTypes[j]);
+ if (innerInputToken != GSS_C_NO_BUFFER && processToken) {
+ major = GSS_S_DEFECTIVE_TOKEN;
+ *minor = GSSEAP_DUPLICATE_ITOK;
+ break;
+ }
+ innerInputToken = &innerInputTokens->elements[j];
+ inputTokenType = &inputTokenTypes[j];
+ }
+ }
+
+#ifdef GSSEAP_DEBUG
+ fprintf(stderr, "GSS-EAP: state %d processToken %d inputTokenType %08x "
+ "innerInputToken %p innerOutputTokensCount %zd\n",
+ ctx->state, processToken, smp->inputTokenType,
+ innerInputToken, innerOutputTokens->count);
+#endif
+
+ if (processToken) {
+ major = smp->processToken(minor, cred, ctx, target, mech, reqFlags,
+ timeReq, chanBindings, innerInputToken,
+ &innerOutputToken);
+ if (GSS_ERROR(major))
+ break;
+
+ if (inputTokenType != NULL)
+ *inputTokenType |= ITOK_FLAG_VERIFIED;
+
+ if (innerOutputToken.value != NULL) {
+ innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken;
+ assert(smp->outputTokenType != ITOK_TYPE_NONE);
+ outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType;
+ if (smp->critical)
+ outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
+ innerOutputTokens->count++;
+ }
+ if (major == GSS_S_COMPLETE)
+ break;
+ } else if (smp->required && smp->inputTokenType != ITOK_TYPE_NONE) {
+ major = GSS_S_DEFECTIVE_TOKEN;
+ *minor = GSSEAP_MISSING_REQUIRED_ITOK;
+ break;
+ }
+ }
+
+ if (major != GSS_S_COMPLETE)
+ break; /* GSS_S_CONTINUE_NEEDED or error */
+
+ assert(ctx->state < GSSEAP_STATE_ESTABLISHED);
+
+ ctx->state = GSSEAP_STATE_NEXT(ctx->state);
+
+ if (innerOutputTokens->count != 0) {
+ major = GSS_S_CONTINUE_NEEDED;
+ break; /* send any tokens if we have them */
+ }
+ } while (ctx->state != GSSEAP_STATE_ESTABLISHED);
+
+ assert(innerOutputTokens->count <= smCount);
+
+ /* Check we understood all critical tokens */
+ if (!GSS_ERROR(major) && innerInputTokens != GSS_C_NO_BUFFER_SET) {
+ 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;
+ }
+ }
+ }
+
+ /* 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;
+ }
+
+ outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
+ }
+
+#ifdef GSSEAP_DEBUG
+ for (i = 0; i < innerOutputTokens->count; i++) {
+ fprintf(stderr, "GSS-EAP: type %d length %zd value %p\n",
+ outputTokenTypes[i],
+ innerOutputTokens->elements[i].length,
+ innerOutputTokens->elements[i].value);
+ }
+#endif
+
+ /* Format composite output token */
+ if (innerOutputTokens->count != 0 || /* inner tokens to send */
+ !CTX_IS_INITIATOR(ctx) || /* any leg acceptor */
+ ctx->state != GSSEAP_STATE_ESTABLISHED) { /* non-last leg initiator */
+ tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens,
+ outputTokenTypes, &unwrappedOutputToken);
+ if (tmpMajor == GSS_S_COMPLETE) {
+ tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
+ TOK_TYPE_ESTABLISH_CONTEXT, outputToken);
+ if (GSS_ERROR(tmpMajor)) {
+ major = tmpMajor;
+ *minor = tmpMinor;
+ goto cleanup;
+ }
+ }
+ }
+
+ assert(GSS_ERROR(major) ||
+ (major == GSS_S_CONTINUE_NEEDED && ctx->state < GSSEAP_STATE_ESTABLISHED) ||
+ (major == GSS_S_COMPLETE && ctx->state == GSSEAP_STATE_ESTABLISHED));
+
+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 $
*/