}
static OM_uint32
+eapGssSmAcceptGssFlags(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken GSSEAP_UNUSED,
+ OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+ unsigned char *p;
+ OM_uint32 initiatorGssFlags;
+
+ assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
+
+ if (inputToken->length < 4) {
+ *minor = GSSEAP_TOK_TRUNC;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ /* allow flags to grow for future expansion */
+ p = (unsigned char *)inputToken->value + inputToken->length - 4;
+
+ initiatorGssFlags = load_uint32_be(p);
+ initiatorGssFlags &= GSSEAP_WIRE_FLAGS_MASK;
+
+ ctx->gssFlags |= initiatorGssFlags;
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
gss_cred_id_t cred GSSEAP_UNUSED,
gss_ctx_id_t ctx,
gss_buffer_t outputToken GSSEAP_UNUSED,
OM_uint32 *smFlags GSSEAP_UNUSED)
{
- OM_uint32 major, tmpMinor;
+ OM_uint32 major;
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;
+ iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM | GSS_IOV_BUFFER_FLAG_ALLOCATED;
+
+ /* XXX necessary because decrypted in place and we verify it later */
+ major = duplicateBuffer(minor, inputToken, &iov[1].buffer);
+ if (GSS_ERROR(major))
+ return major;
major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
iov, 2, TOK_TYPE_WRAP);
- if (GSS_ERROR(major))
+ if (GSS_ERROR(major)) {
+ gssEapReleaseIov(iov, 2);
return major;
+ }
if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS &&
!bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
*minor = 0;
}
- gss_release_buffer(&tmpMinor, &iov[0].buffer);
+ gssEapReleaseIov(iov, 2);
return major;
}
+static OM_uint32
+eapGssSmAcceptInitiatorMIC(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken GSSEAP_UNUSED,
+ OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+ OM_uint32 major;
+
+ major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
+ if (GSS_ERROR(major))
+ return major;
+
+ GSSEAP_SM_TRANSITION_NEXT(ctx);
+
+ *minor = 0;
+ return GSS_S_CONTINUE_NEEDED;
+}
+
#ifdef GSSEAP_ENABLE_REAUTH
static OM_uint32
eapGssSmAcceptReauthCreds(OM_uint32 *minor,
#endif
static OM_uint32
-eapGssSmAcceptCompleteInitiatorExts(OM_uint32 *minor,
- gss_cred_id_t cred GSSEAP_UNUSED,
- gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
- gss_OID mech GSSEAP_UNUSED,
- OM_uint32 reqFlags GSSEAP_UNUSED,
- OM_uint32 timeReq GSSEAP_UNUSED,
- gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
- gss_buffer_t inputToken GSSEAP_UNUSED,
- gss_buffer_t outputToken GSSEAP_UNUSED,
- OM_uint32 *smFlags GSSEAP_UNUSED)
+eapGssSmAcceptAcceptorMIC(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken GSSEAP_UNUSED,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags)
{
- GSSEAP_SM_TRANSITION_NEXT(ctx);
-
- *minor = 0;
+ OM_uint32 major;
- return GSS_S_CONTINUE_NEEDED;
-}
+ major = gssEapMakeTokenMIC(minor, ctx, outputToken);
+ if (GSS_ERROR(major))
+ return major;
-static OM_uint32
-eapGssSmAcceptCompleteAcceptorExts(OM_uint32 *minor,
- gss_cred_id_t cred GSSEAP_UNUSED,
- gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
- gss_OID mech GSSEAP_UNUSED,
- OM_uint32 reqFlags GSSEAP_UNUSED,
- OM_uint32 timeReq GSSEAP_UNUSED,
- gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
- gss_buffer_t inputToken GSSEAP_UNUSED,
- gss_buffer_t outputToken GSSEAP_UNUSED,
- OM_uint32 *smFlags)
-{
GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
*minor = 0;
- *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
+ *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
return GSS_S_COMPLETE;
}
eapGssSmAcceptAuthenticate
},
{
+ ITOK_TYPE_GSS_FLAGS,
+ ITOK_TYPE_NONE,
+ GSSEAP_STATE_INITIATOR_EXTS,
+ 0,
+ eapGssSmAcceptGssFlags
+ },
+ {
ITOK_TYPE_GSS_CHANNEL_BINDINGS,
ITOK_TYPE_NONE,
GSSEAP_STATE_INITIATOR_EXTS,
eapGssSmAcceptGssChannelBindings,
},
{
- ITOK_TYPE_NONE,
+ ITOK_TYPE_INITIATOR_MIC,
ITOK_TYPE_NONE,
GSSEAP_STATE_INITIATOR_EXTS,
- 0,
- eapGssSmAcceptCompleteInitiatorExts,
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmAcceptInitiatorMIC,
},
#ifdef GSSEAP_ENABLE_REAUTH
{
#endif
{
ITOK_TYPE_NONE,
- ITOK_TYPE_NONE,
+ ITOK_TYPE_ACCEPTOR_MIC,
GSSEAP_STATE_ACCEPTOR_EXTS,
0,
- eapGssSmAcceptCompleteAcceptorExts
+ eapGssSmAcceptAcceptorMIC
},
};
#define reauthCtx ctxU.reauth
#endif
} ctxU;
+ const struct gss_eap_token_buffer_set *inputTokens;
+ const struct gss_eap_token_buffer_set *outputTokens;
};
#define TOK_FLAG_SENDER_IS_ACCEPTOR 0x01
}
static OM_uint32
+eapGssSmInitGssFlags(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken GSSEAP_UNUSED,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+ unsigned char wireFlags[4];
+ gss_buffer_desc flagsBuf;
+
+ store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags);
+
+ flagsBuf.length = sizeof(wireFlags);
+ flagsBuf.value = wireFlags;
+
+ return duplicateBuffer(minor, &flagsBuf, outputToken);
+}
+
+static OM_uint32
eapGssSmInitGssChannelBindings(OM_uint32 *minor,
gss_cred_id_t cred GSSEAP_UNUSED,
gss_ctx_id_t ctx,
return GSS_S_CONTINUE_NEEDED;
}
+static OM_uint32
+eapGssSmInitInitiatorMIC(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken GSSEAP_UNUSED,
+ gss_buffer_t outputToken,
+ OM_uint32 *smFlags)
+{
+ OM_uint32 major;
+
+ major = gssEapMakeTokenMIC(minor, ctx, outputToken);
+ if (GSS_ERROR(major))
+ return major;
+
+ GSSEAP_SM_TRANSITION_NEXT(ctx);
+
+ *minor = 0;
+ *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+
#ifdef GSSEAP_ENABLE_REAUTH
static OM_uint32
eapGssSmInitReauthCreds(OM_uint32 *minor,
#endif /* GSSEAP_ENABLE_REAUTH */
static OM_uint32
-eapGssSmInitCompleteInitiatorExts(OM_uint32 *minor,
- gss_cred_id_t cred GSSEAP_UNUSED,
- gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
- gss_OID mech GSSEAP_UNUSED,
- OM_uint32 reqFlags GSSEAP_UNUSED,
- OM_uint32 timeReq GSSEAP_UNUSED,
- gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
- gss_buffer_t inputToken GSSEAP_UNUSED,
- gss_buffer_t outputToken GSSEAP_UNUSED,
- OM_uint32 *smFlags)
+eapGssSmInitAcceptorMIC(OM_uint32 *minor,
+ gss_cred_id_t cred GSSEAP_UNUSED,
+ gss_ctx_id_t ctx,
+ gss_name_t target GSSEAP_UNUSED,
+ gss_OID mech GSSEAP_UNUSED,
+ OM_uint32 reqFlags GSSEAP_UNUSED,
+ OM_uint32 timeReq GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_buffer_t inputToken,
+ gss_buffer_t outputToken GSSEAP_UNUSED,
+ OM_uint32 *smFlags GSSEAP_UNUSED)
{
- GSSEAP_SM_TRANSITION_NEXT(ctx);
-
- *minor = 0;
- *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
+ OM_uint32 major;
- return GSS_S_CONTINUE_NEEDED;
-}
+ major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
+ if (GSS_ERROR(major))
+ return major;
-static OM_uint32
-eapGssSmInitCompleteAcceptorExts(OM_uint32 *minor,
- gss_cred_id_t cred GSSEAP_UNUSED,
- gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
- gss_OID mech GSSEAP_UNUSED,
- OM_uint32 reqFlags GSSEAP_UNUSED,
- OM_uint32 timeReq GSSEAP_UNUSED,
- gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
- gss_buffer_t inputToken GSSEAP_UNUSED,
- gss_buffer_t outputToken GSSEAP_UNUSED,
- OM_uint32 *smFlags GSSEAP_UNUSED)
-{
GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
*minor = 0;
},
{
ITOK_TYPE_NONE,
+ ITOK_TYPE_GSS_FLAGS,
+ GSSEAP_STATE_INITIATOR_EXTS,
+ 0,
+ eapGssSmInitGssFlags
+ },
+ {
+ ITOK_TYPE_NONE,
ITOK_TYPE_GSS_CHANNEL_BINDINGS,
GSSEAP_STATE_INITIATOR_EXTS,
SM_ITOK_FLAG_REQUIRED,
},
{
ITOK_TYPE_NONE,
- ITOK_TYPE_NONE,
+ ITOK_TYPE_INITIATOR_MIC,
GSSEAP_STATE_INITIATOR_EXTS,
- 0,
- eapGssSmInitCompleteInitiatorExts
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmInitInitiatorMIC
},
#ifdef GSSEAP_ENABLE_REAUTH
{
#endif
/* other extensions go here */
{
- ITOK_TYPE_NONE,
+ ITOK_TYPE_ACCEPTOR_MIC,
ITOK_TYPE_NONE,
GSSEAP_STATE_ACCEPTOR_EXTS,
- 0,
- eapGssSmInitCompleteAcceptorExts
+ SM_ITOK_FLAG_REQUIRED,
+ eapGssSmInitAcceptorMIC
}
};
#define ITOK_TYPE_REAUTH_RESP 0x00000009 /* optional */
#define ITOK_TYPE_VERSION_INFO 0x0000000A /* optional */
#define ITOK_TYPE_VENDOR_INFO 0x0000000B /* optional */
+#define ITOK_TYPE_GSS_FLAGS 0x0000000C /* optional */
+#define ITOK_TYPE_INITIATOR_MIC 0x0000000D /* critical, required, if not reauth */
+#define ITOK_TYPE_ACCEPTOR_MIC 0x0000000E /* TBD */
#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))
+#define GSSEAP_WIRE_FLAGS_MASK GSS_C_MUTUAL_FLAG
+
OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
OM_uint32 gssEapReleaseContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
gss_ctx_id_t context_handle,
OM_uint32 *time_rec);
+OM_uint32
+gssEapMakeTokenMIC(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ gss_buffer_t tokenMIC);
+
+OM_uint32
+gssEapVerifyTokenMIC(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ const gss_buffer_t tokenMIC);
+
/* util_cred.c */
OM_uint32 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred);
OM_uint32 gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred);
gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state);
/* util_token.c */
+struct gss_eap_token_buffer_set {
+ gss_buffer_set_desc buffers; /* pointers only */
+ OM_uint32 *types;
+};
+
OM_uint32
gssEapEncodeInnerTokens(OM_uint32 *minor,
- gss_buffer_set_t extensions,
- OM_uint32 *types,
+ struct gss_eap_token_buffer_set *tokens,
gss_buffer_t buffer);
OM_uint32
gssEapDecodeInnerTokens(OM_uint32 *minor,
const gss_buffer_t buffer,
- gss_buffer_set_t *pExtensions,
- OM_uint32 **pTypes);
+ struct gss_eap_token_buffer_set *tokens);
+
+OM_uint32
+gssEapReleaseInnerTokens(OM_uint32 *minor,
+ struct gss_eap_token_buffer_set *tokens,
+ int freeBuffers);
+
+OM_uint32
+gssEapAllocInnerTokens(OM_uint32 *minor,
+ size_t count,
+ struct gss_eap_token_buffer_set *tokens);
size_t
tokenSize(const gss_OID_desc *mech, size_t body_size);
GSS_C_INTEG_FLAG | /* integrity */
GSS_C_CONF_FLAG | /* confidentiality */
GSS_C_SEQUENCE_FLAG | /* sequencing */
- GSS_C_REPLAY_FLAG| /* replay detection */
- GSS_C_MUTUAL_FLAG; /*xxx big hack */
+ GSS_C_REPLAY_FLAG; /* replay detection */
*pCtx = ctx;
return GSS_S_COMPLETE;
}
+
+static OM_uint32
+gssEapMakeOrVerifyTokenMIC(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ gss_buffer_t tokenMIC,
+ int verifyMIC)
+{
+ OM_uint32 major;
+ gss_iov_buffer_desc *iov = NULL;
+ size_t i = 0, j;
+ enum gss_eap_token_type tokType;
+ OM_uint32 micTokType;
+ unsigned char wireTokType[2];
+ unsigned char *innerTokTypes = NULL, *innerTokLengths = NULL;
+ const struct gss_eap_token_buffer_set *tokens;
+
+ tokens = verifyMIC ? ctx->inputTokens : ctx->outputTokens;
+
+ assert(tokens != NULL);
+
+ iov = GSSEAP_CALLOC(2 + (3 * tokens->buffers.count) + 1, sizeof(*iov));
+ if (iov == NULL) {
+ major = GSS_S_FAILURE;
+ *minor = ENOMEM;
+ goto cleanup;
+ }
+
+ innerTokTypes = GSSEAP_MALLOC(4 * tokens->buffers.count);
+ if (innerTokTypes == NULL) {
+ *minor = ENOMEM;
+ major = GSS_S_FAILURE;
+ goto cleanup;
+ }
+
+ innerTokLengths = GSSEAP_MALLOC(4 * tokens->buffers.count);
+ if (innerTokLengths == NULL) {
+ major = GSS_S_FAILURE;
+ *minor = ENOMEM;
+ goto cleanup;
+ }
+
+ /* Mechanism OID */
+ assert(ctx->mechanismUsed != GSS_C_NO_OID);
+ iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[i].buffer.length = ctx->mechanismUsed->length;
+ iov[i].buffer.value = ctx->mechanismUsed->elements;
+ i++;
+
+ /* Token type */
+ if (CTX_IS_INITIATOR(ctx) ^ verifyMIC) {
+ tokType = TOK_TYPE_INITIATOR_CONTEXT;
+ micTokType = ITOK_TYPE_INITIATOR_MIC;
+ } else {
+ tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
+ micTokType = ITOK_TYPE_ACCEPTOR_MIC;
+ }
+ store_uint16_be(tokType, wireTokType);
+
+ iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[i].buffer.length = sizeof(wireTokType);
+ iov[i].buffer.value = wireTokType;
+ i++;
+
+ for (j = 0; j < tokens->buffers.count; j++) {
+ if (verifyMIC &&
+ (tokens->types[j] & ITOK_TYPE_MASK) == micTokType)
+ continue; /* will use this slot for trailer */
+
+ iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[i].buffer.length = 4;
+ iov[i].buffer.value = &innerTokTypes[j * 4];
+ store_uint32_be(tokens->types[j] & ~(ITOK_FLAG_VERIFIED),
+ iov[i].buffer.value);
+ i++;
+
+ iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[i].buffer.length = 4;
+ iov[i].buffer.value = &innerTokLengths[j * 4];
+ store_uint32_be(tokens->buffers.elements[j].length,
+ iov[i].buffer.value);
+ i++;
+
+ iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[i].buffer = tokens->buffers.elements[j];
+ i++;
+ }
+
+ if (verifyMIC) {
+ assert(tokenMIC->length >= 16);
+
+ assert(i < 2 + (3 * tokens->buffers.count));
+
+ iov[i].type = GSS_IOV_BUFFER_TYPE_HEADER;
+ iov[i].buffer.length = 16;
+ iov[i].buffer.value = tokenMIC->value;
+ i++;
+
+ iov[i].type = GSS_IOV_BUFFER_TYPE_TRAILER;
+ iov[i].buffer.length = tokenMIC->length - 16;
+ iov[i].buffer.value = (unsigned char *)tokenMIC->value + 16;
+ i++;
+
+ major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
+ iov, i, TOK_TYPE_MIC);
+ } else {
+ iov[i++].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+ major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL,
+ iov, i, TOK_TYPE_MIC);
+ if (!GSS_ERROR(major))
+ *tokenMIC = iov[i - 1].buffer;
+ }
+
+cleanup:
+ if (iov != NULL)
+ gssEapReleaseIov(iov, tokens->buffers.count);
+ if (innerTokTypes != NULL)
+ GSSEAP_FREE(innerTokTypes);
+ if (innerTokLengths != NULL)
+ GSSEAP_FREE(innerTokLengths);
+
+ return major;
+}
+
+OM_uint32
+gssEapMakeTokenMIC(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ gss_buffer_t tokenMIC)
+{
+ tokenMIC->length = 0;
+ tokenMIC->value = NULL;
+
+ return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, FALSE);
+}
+
+OM_uint32
+gssEapVerifyTokenMIC(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ const gss_buffer_t tokenMIC)
+{
+ if (tokenMIC->length < 16) {
+ *minor = GSSEAP_TOK_TRUNC;
+ return GSS_S_BAD_SIG;
+ }
+
+ return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, TRUE);
+}
makeErrorToken(OM_uint32 *minor,
OM_uint32 majorStatus,
OM_uint32 minorStatus,
- gss_buffer_set_t *outputToken)
+ struct gss_eap_token_buffer_set *token)
{
- OM_uint32 major;
+ OM_uint32 major, tmpMinor;
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.
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))
+ major = gssEapAllocInnerTokens(&tmpMinor, 1, token);
+ if (GSS_ERROR(major)) {
+ *minor = tmpMinor;
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;
+ errorBuffer.length = sizeof(errorData);
+ errorBuffer.value = errorData;
-cleanup:
+ major = duplicateBuffer(&tmpMinor, &errorBuffer, &token->buffers.elements[0]);
if (GSS_ERROR(major)) {
- gss_release_buffer_set(&tmpMinor, &tokens);
- tokens = GSS_C_NO_BUFFER_SET;
- if (tokenTypes != NULL) {
- GSSEAP_FREE(tokenTypes);
- tokenTypes = NULL;
- }
+ gssEapReleaseInnerTokens(&tmpMinor, token, 1);
+ *minor = tmpMinor;
+ return major;
}
- *pTokens = tokens;
- *pTokenTypes = tokenTypes;
+ token->types[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
- return major;
+ *minor = 0;
+ return GSS_S_COMPLETE;
}
OM_uint32
size_t smCount)
{
OM_uint32 major, tmpMajor, tmpMinor;
+ struct gss_eap_token_buffer_set inputTokens = { { 0, GSS_C_NO_BUFFER }, NULL };
+ struct gss_eap_token_buffer_set outputTokens = { { 0, GSS_C_NO_BUFFER }, NULL };
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;
assert(ctx->state < GSSEAP_STATE_ESTABLISHED);
- major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken,
- &innerInputTokens, &inputTokenTypes);
+ major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken, &inputTokens);
if (GSS_ERROR(major))
goto cleanup;
- assert(innerInputTokens != GSS_C_NO_BUFFER_SET);
-
- major = allocInnerTokens(minor, smCount, &innerOutputTokens, &outputTokenTypes);
+ major = gssEapAllocInnerTokens(minor, smCount, &outputTokens);
if (GSS_ERROR(major))
goto cleanup;
+ ctx->inputTokens = &inputTokens;
+ ctx->outputTokens = &outputTokens;
+
/* Process all the tokens that are valid for the current state. */
for (i = 0; i < smCount; i++) {
struct gss_eap_sm *smp = &sm[i];
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) {
+ for (j = 0; j < inputTokens.buffers.count; j++) {
+ if ((inputTokens.types[j] & ITOK_TYPE_MASK) == smp->inputTokenType) {
if (processToken) {
/* Check for duplicate inner tokens */
major = GSS_S_DEFECTIVE_TOKEN;
break;
}
processToken = 1;
- innerInputToken = &innerInputTokens->elements[j];
- inputTokenType = &inputTokenTypes[j];
+ innerInputToken = &inputTokens.buffers.elements[j];
+ inputTokenType = &inputTokens.types[j];
}
}
if (GSS_ERROR(major))
smFlags |= SM_FLAG_TRANSITED;
if (innerOutputToken.value != NULL) {
- innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken;
+ outputTokens.buffers.elements[outputTokens.buffers.count] = innerOutputToken;
assert(smp->outputTokenType != ITOK_TYPE_NONE);
- outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType;
+ outputTokens.types[outputTokens.buffers.count] = smp->outputTokenType;
if (smFlags & SM_FLAG_OUTPUT_TOKEN_CRITICAL)
- outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
- innerOutputTokens->count++;
+ outputTokens.types[outputTokens.buffers.count] |= ITOK_FLAG_CRITICAL;
+ outputTokens.buffers.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)) {
+ ((smFlags & SM_FLAG_FORCE_SEND_TOKEN) || outputTokens.buffers.count != 0)) {
SM_ASSERT_VALID(ctx, major);
break;
}
}
}
- assert(innerOutputTokens->count <= smCount);
+ assert(outputTokens.buffers.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) {
+ for (j = 0; j < inputTokens.buffers.count; j++) {
+ if ((inputTokens.types[j] & ITOK_FLAG_CRITICAL) &&
+ (inputTokens.types[j] & ITOK_FLAG_VERIFIED) == 0) {
major = GSS_S_UNAVAILABLE;
*minor = GSSEAP_CRIT_ITOK_UNAVAILABLE;
goto cleanup;
goto cleanup; /* return error directly to caller */
/* replace any emitted tokens with error token */
- gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
+ gssEapReleaseInnerTokens(&tmpMinor, &outputTokens, 1);
- tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputTokens);
+ tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &outputTokens);
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 */
+ if (outputTokens.buffers.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);
+ tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, &outputTokens, &unwrappedOutputToken);
if (tmpMajor == GSS_S_COMPLETE) {
if (CTX_IS_INITIATOR(ctx))
tokType = TOK_TYPE_INITIATOR_CONTEXT;
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)
+ gssEapReleaseInnerTokens(&tmpMinor, &inputTokens, 0);
+ gssEapReleaseInnerTokens(&tmpMinor, &inputTokens, 1);
+
gss_release_buffer(&tmpMinor, &unwrappedOutputToken);
- GSSEAP_FREE(outputTokenTypes);
+
+ ctx->inputTokens = NULL;
+ ctx->outputTokens = NULL;
return major;
}
OM_uint32
gssEapEncodeInnerTokens(OM_uint32 *minor,
- gss_buffer_set_t extensions,
- OM_uint32 *types,
+ struct gss_eap_token_buffer_set *tokens,
gss_buffer_t buffer)
{
OM_uint32 major, tmpMinor;
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;
- }
+ for (i = 0; i < tokens->buffers.count; i++) {
+ required += 8 + tokens->buffers.elements[i].length;
}
/*
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];
+ for (i = 0; i < tokens->buffers.count; i++) {
+ gss_buffer_t tokenBuffer = &tokens->buffers.elements[i];
- assert((types[i] & ITOK_FLAG_VERIFIED) == 0); /* private flag */
+ assert((tokens->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);
+ /*
+ * Extensions are encoded as type-length-value, where the upper
+ * bit of the type indicates criticality.
+ */
+ store_uint32_be(tokens->types[i], &p[0]);
+ store_uint32_be(tokenBuffer->length, &p[4]);
+ memcpy(&p[8], tokenBuffer->value, tokenBuffer->length);
- p += 8 + extension->length;
- }
+ p += 8 + tokenBuffer->length;
}
assert(p == (unsigned char *)buffer->value + required);
OM_uint32
gssEapDecodeInnerTokens(OM_uint32 *minor,
const gss_buffer_t buffer,
- gss_buffer_set_t *pExtensions,
- OM_uint32 **pTypes)
+ struct gss_eap_token_buffer_set *tokens)
{
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;
+ tokens->buffers.count = 0;
+ tokens->buffers.elements = NULL;
+ tokens->types = NULL;
if (buffer->length == 0) {
major = GSS_S_COMPLETE;
do {
OM_uint32 *ntypes;
- gss_buffer_desc extension;
+ gss_buffer_desc tokenBuffer, *newTokenBuffers;
if (remain < 8) {
major = GSS_S_DEFECTIVE_TOKEN;
goto cleanup;
}
- ntypes = GSSEAP_REALLOC(types,
- (extensions->count + 1) * sizeof(OM_uint32));
+ ntypes = GSSEAP_REALLOC(tokens->types,
+ (tokens->buffers.count + 1) * sizeof(OM_uint32));
if (ntypes == NULL) {
major = GSS_S_FAILURE;
*minor = ENOMEM;
goto cleanup;
}
- types = ntypes;
+ tokens->types = ntypes;
- types[extensions->count] = load_uint32_be(&p[0]);
- extension.length = load_uint32_be(&p[4]);
+ tokens->types[tokens->buffers.count] = load_uint32_be(&p[0]);
+ tokenBuffer.length = load_uint32_be(&p[4]);
- if (remain < 8 + extension.length) {
+ if (remain < 8 + tokenBuffer.length) {
major = GSS_S_DEFECTIVE_TOKEN;
*minor = GSSEAP_TOK_TRUNC;
goto cleanup;
}
- extension.value = &p[8];
+ tokenBuffer.value = &p[8];
- major = gss_add_buffer_set_member(minor, &extension, &extensions);
- if (GSS_ERROR(major))
+ newTokenBuffers = GSSEAP_REALLOC(tokens->buffers.elements,
+ (tokens->buffers.count + 1) * sizeof(gss_buffer_desc));
+ if (newTokenBuffers == NULL) {
+ major = GSS_S_FAILURE;
+ *minor = ENOMEM;
goto cleanup;
+ }
+
+ tokens->buffers.elements = newTokenBuffers;
+ tokens->buffers.elements[tokens->buffers.count] = tokenBuffer;
+ tokens->buffers.count++;
+
+ p += 8 + tokenBuffer.length;
+ remain -= 8 + tokenBuffer.length;
- p += 8 + extension.length;
- remain -= 8 + extension.length;
} while (remain != 0);
+ major = GSS_S_COMPLETE;
+ *minor = 0;
+
cleanup:
- if (GSS_ERROR(major)) {
- gss_release_buffer_set(&tmpMinor, &extensions);
- if (types != NULL)
- GSSEAP_FREE(types);
- } else {
- *pExtensions = extensions;
- *pTypes = types;
- }
+ if (GSS_ERROR(major))
+ gssEapReleaseInnerTokens(&tmpMinor, tokens, 0);
return major;
}
*minor = 0;
return GSS_S_COMPLETE;
}
+
+OM_uint32
+gssEapAllocInnerTokens(OM_uint32 *minor,
+ size_t count,
+ struct gss_eap_token_buffer_set *tokens)
+{
+ OM_uint32 major;
+
+ tokens->buffers.count = 0;
+ tokens->buffers.elements = (gss_buffer_desc *)GSSEAP_CALLOC(count, sizeof(gss_buffer_desc));
+ if (tokens->buffers.elements == NULL) {
+ major = GSS_S_FAILURE;
+ *minor = ENOMEM;
+ goto cleanup;
+ }
+
+ tokens->types = (OM_uint32 *)GSSEAP_CALLOC(count, sizeof(OM_uint32));
+ if (tokens->types == NULL) {
+ major = GSS_S_FAILURE;
+ *minor = ENOMEM;
+ goto cleanup;
+ }
+
+ major = GSS_S_COMPLETE;
+ *minor = 0;
+
+cleanup:
+ if (GSS_ERROR(major)) {
+ if (tokens->buffers.elements != NULL) {
+ GSSEAP_FREE(tokens->buffers.elements);
+ tokens->buffers.elements = NULL;
+ }
+ if (tokens->types != NULL) {
+ GSSEAP_FREE(tokens->types);
+ tokens->types = NULL;
+ }
+ }
+
+ return major;
+}
+
+OM_uint32
+gssEapReleaseInnerTokens(OM_uint32 *minor,
+ struct gss_eap_token_buffer_set *tokens,
+ int freeBuffers)
+{
+ OM_uint32 tmpMinor;
+ size_t i;
+
+ if (tokens->buffers.elements != NULL) {
+ if (freeBuffers) {
+ for (i = 0; i < tokens->buffers.count; i++)
+ gss_release_buffer(&tmpMinor, &tokens->buffers.elements[i]);
+ }
+ GSSEAP_FREE(tokens->buffers.elements);
+ tokens->buffers.elements = NULL;
+ }
+ tokens->buffers.count = 0;
+
+ if (tokens->types != NULL) {
+ GSSEAP_FREE(tokens->types);
+ tokens->types = NULL;
+ }
+
+ *minor = 0;
+ return GSS_S_COMPLETE;
+}