static OM_uint32
send_reject (OM_uint32 *minor_status,
+ gss_buffer_t mech_token,
gss_buffer_t output_token)
{
NegotiationToken nt;
size_t size;
+ heim_octet_string responseToken;
nt.element = choice_NegotiationToken_negTokenResp;
*(nt.u.negTokenResp.negState) = reject;
nt.u.negTokenResp.supportedMech = NULL;
nt.u.negTokenResp.responseToken = NULL;
+
+ if (mech_token != GSS_C_NO_BUFFER && mech_token->value != NULL) {
+ responseToken.length = mech_token->length;
+ responseToken.data = mech_token->value;
+ nt.u.negTokenResp.responseToken = &responseToken;
+ } else
+ nt.u.negTokenResp.responseToken = NULL;
nt.u.negTokenResp.mechListMIC = NULL;
ASN1_MALLOC_ENCODE(NegotiationToken,
ret = _gss_spnego_verify_mechtypes_mic(minor_status, ctx, mic);
if (ret) {
if (*get_mic)
- send_reject(minor_status, output_token);
+ send_reject(minor_status, GSS_C_NO_BUFFER, output_token);
if (buf.value)
free(buf.value);
return ret;
input_chan_bindings,
&obuf,
delegated_cred_handle);
- if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
- mech_output_token = &obuf;
- }
+ mech_output_token = &obuf;
if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
free_NegotiationToken(&nt);
- send_reject(&junk, output_token);
+ send_reject(&junk, mech_output_token, output_token);
+ gss_release_buffer(&junk, mech_output_token);
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
return ret;
}
OM_uint32 ret, minor;
NegotiationToken resp;
gss_buffer_desc mech_output_token;
+ NegStateEnum negState;
*minor_status = 0;
if (ret)
return ret;
+ /* The SPNEGO token must be a negTokenResp */
if (resp.element != choice_NegotiationToken_negTokenResp) {
free_NegotiationToken(&resp);
*minor_status = 0;
return GSS_S_BAD_MECH;
}
- if (resp.u.negTokenResp.negState == NULL
- || *(resp.u.negTokenResp.negState) == reject)
- {
- free_NegotiationToken(&resp);
- return GSS_S_BAD_MECH;
- }
+ /*
+ * When negState is absent, the actual state should be inferred from
+ * the state of the negotiated mechanism context. (RFC 4178 4.2.2.)
+ */
+ if (resp.u.negTokenResp.negState != NULL)
+ negState = *resp.u.negTokenResp.negState;
+ else
+ negState = accept_incomplete;
/*
* Pick up the mechanism that the acceptor selected, only pick up
"SPNEGO acceptor didn't send supportedMech");
}
- /* if a token (of non zero length), or no context, pass to underlaying mech */
+ /* if a token (of non zero length) pass to underlaying mech */
if ((resp.u.negTokenResp.responseToken != NULL && resp.u.negTokenResp.responseToken->length) ||
ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
gss_buffer_desc mech_input_token;
&mech_output_token,
&ctx->mech_flags,
&ctx->mech_time_rec);
- if (GSS_ERROR(ret))
+ if (GSS_ERROR(ret)) {
gss_mg_collect_error(ctx->selected_mech_type, ret, minor);
+ }
}
+ /*
+ * If the acceptor rejected, we're out even if the inner context is
+ * now complete. Note that the rejection is not integrity-protected.
+ */
+ if (negState == reject)
+ ret = GSS_S_BAD_MECH;
if (GSS_ERROR(ret)) {
free_NegotiationToken(&resp);
*minor_status = minor;
if (ret == GSS_S_COMPLETE) {
ctx->flags.open = 1;
}
- } else if (*resp.u.negTokenResp.negState == accept_completed) {
+ } else if (negState == reject) {
+ free_NegotiationToken(&resp);
+ return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
+ GSS_S_BAD_MECH, (*minor_status = EPERM),
+ "SPNEGO acceptor rejected initiator token");
+ } else if (negState == accept_completed) {
+ /*
+ * Note that the accept_completed isn't integrity-protected, but
+ * ctx->maybe_open can only be true if the inner context is fully
+ * established.
+ */
if (ctx->flags.maybe_open)
ctx->flags.open = 1;
if (!ctx->flags.open) {
+ free_NegotiationToken(&resp);
return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
GSS_S_BAD_MECH, (*minor_status = EINVAL),
"SPNEGO acceptor sent acceptor complete, "
}
}
- if (*resp.u.negTokenResp.negState == request_mic) {
+ if (negState == request_mic) {
ctx->flags.peer_require_mic = 1;
}
if (ctx->flags.open) {
- if (*resp.u.negTokenResp.negState == accept_completed && ctx->flags.safe_omit) {
+ if (negState == accept_completed && ctx->flags.safe_omit) {
ctx->initiator_state = step_completed;
ret = GSS_S_COMPLETE;
} else if (ctx->flags.require_mic != 0 && ctx->flags.verified_mic == 0) {
}
}
- if (*resp.u.negTokenResp.negState != accept_completed ||
+ if (negState != accept_completed ||
ctx->initiator_state != step_completed ||
mech_output_token.length)
{