((status) == GSS_S_COMPLETE && (ctx)->state == GSSEAP_STATE_ESTABLISHED)); \
} while (0)
+#ifdef GSSEAP_DEBUG
static const char *
gssEapStateToString(enum gss_eap_state state)
{
return s;
}
-#ifdef GSSEAP_DEBUG
void
gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state)
{
- assert(state > ctx->state);
+ assert(state >= GSSEAP_STATE_INITIAL);
assert(state <= GSSEAP_STATE_ESTABLISHED);
fprintf(stderr, "GSS-EAP: state transition %s->%s\n",
- gssEapStateToString(ctx->state), gssEapStateToString(state));
+ gssEapStateToString(GSSEAP_SM_STATE(ctx)),
+ gssEapStateToString(state));
ctx->state = state;
}
-#endif
+#endif /* GSSEAP_DEBUG */
static OM_uint32
makeErrorToken(OM_uint32 *minor,
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.
errorBuffer.length = sizeof(errorData);
errorBuffer.value = errorData;
- return gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
+ major = gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
+ if (GSS_ERROR(major))
+ return major;
+
+ return GSS_S_COMPLETE;
}
static OM_uint32
gss_channel_bindings_t chanBindings,
gss_buffer_t inputToken,
gss_buffer_t outputToken,
- struct gss_eap_sm *sm,
+ struct gss_eap_sm *sm, /* ordered by state */
size_t smCount)
{
OM_uint32 major, tmpMajor, tmpMinor;
initialContextToken = 1;
}
- if (ctx->state == GSSEAP_STATE_ESTABLISHED) {
+ if (CTX_IS_ESTABLISHED(ctx)) {
major = GSS_S_BAD_STATUS;
*minor = GSSEAP_CONTEXT_ESTABLISHED;
goto cleanup;
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) {
- processToken = 1;
- if (innerInputToken != GSS_C_NO_BUFFER) {
+ 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];
}
- 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,
if (inputTokenType != NULL)
*inputTokenType |= ITOK_FLAG_VERIFIED;
- if (ctx->state != oldState)
+ if (smFlags & SM_FLAG_RESTART) {
+ assert(ctx->state < oldState);
+ i = 0;
+ } 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 (smp->itokFlags & SM_ITOK_FLAG_CRITICAL)
+ if (smFlags & SM_FLAG_OUTPUT_TOKEN_CRITICAL)
outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
innerOutputTokens->count++;
}
}
} 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 */
+ /* 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) &&
}
}
- /* Emit an error token if we are the acceptor */
+ /* 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 */
goto cleanup;
}
- outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
+ if (innerOutputTokens->count != 0)
+ outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
}
- /* Format composite output token */
+ /* Format output token from inner tokens */
if (innerOutputTokens->count != 0 || /* inner tokens to send */
!CTX_IS_INITIATOR(ctx) || /* any leg acceptor */
- ctx->state != GSSEAP_STATE_ESTABLISHED) { /* non-last leg initiator */
+ !CTX_IS_ESTABLISHED(ctx)) { /* non-last leg initiator */
tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens,
outputTokenTypes, &unwrappedOutputToken);
if (tmpMajor == GSS_S_COMPLETE) {
}
}
+ /* 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: