Code: gss_unwrap (minor_status,
            context_handle,
            input_message_buffer,
            output_message_buffer,
            conf_state,
            qop_state)

OM_uint32 *		minor_status;
gss_ctx_id_t		context_handle;
gss_buffer_t		input_message_buffer;
gss_buffer_t		output_message_buffer;
int *			conf_state;
gss_qop_t *		qop_state;

{
/* EXPORT DELETE START */
    OM_uint32		status;
    gss_union_ctx_id_t	ctx;
    gss_mechanism	mech;

    if (minor_status != NULL)
	*minor_status = 0;

    if (output_message_buffer != GSS_C_NO_BUFFER) {
	output_message_buffer->length = 0;
	output_message_buffer->value = NULL;
    }

    if (minor_status == NULL)
	return (GSS_S_CALL_INACCESSIBLE_WRITE);

    if (context_handle == GSS_C_NO_CONTEXT)
	return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);

    if (input_message_buffer == GSS_C_NO_BUFFER ||
	GSS_EMPTY_BUFFER(input_message_buffer))

	return (GSS_S_CALL_INACCESSIBLE_READ);

    if (output_message_buffer == GSS_C_NO_BUFFER)
	return (GSS_S_CALL_INACCESSIBLE_WRITE);

    /*
     * select the approprate underlying mechanism routine and
      * call it.
      */
     ctx = (gss_union_ctx_id_t) context_handle;
     mech = gssint_get_mechanism (ctx->mech_type);
 
     if (mech) {
	if (mech->gss_unwrap) {
	    status = mech->gss_unwrap(minor_status,
				      ctx->internal_ctx_id,
				      input_message_buffer,
				      output_message_buffer,
				      conf_state,
				      qop_state);
	    if (status != GSS_S_COMPLETE)
		map_error(minor_status, mech);
	} else if (mech->gss_unwrap_aead || mech->gss_unwrap_iov) {
	    status = gssint_unwrap_aead(mech,
					minor_status,
					ctx,
					input_message_buffer,
					GSS_C_NO_BUFFER,
					output_message_buffer,
					conf_state,
					(gss_qop_t *)qop_state);
	} else
	    status = GSS_S_UNAVAILABLE;

	return(status);
    }

/* EXPORT DELETE END */

    return (GSS_S_BAD_MECH);
}

Commit Message: Preserve GSS context on init/accept failure

After gss_init_sec_context() or gss_accept_sec_context() has created a
context, don't delete the mechglue context on failures from subsequent
calls, even if the mechanism deletes the mech-specific context (which
is allowed by RFC 2744 but not preferred).  Check for union contexts
with no mechanism context in each GSS function which accepts a
gss_ctx_id_t.

CVE-2017-11462:

RFC 2744 permits a GSS-API implementation to delete an existing
security context on a second or subsequent call to
gss_init_sec_context() or gss_accept_sec_context() if the call results
in an error.  This API behavior has been found to be dangerous,
leading to the possibility of memory errors in some callers.  For
safety, GSS-API implementations should instead preserve existing
security contexts on error until the caller deletes them.

All versions of MIT krb5 prior to this change may delete acceptor
contexts on error.  Versions 1.13.4 through 1.13.7, 1.14.1 through
1.14.5, and 1.15 through 1.15.1 may also delete initiator contexts on
error.

ticket: 8598 (new)
target_version: 1.15-next
target_version: 1.14-next
tags: pullup
CWE ID: CWE-415
gss_unwrap (minor_status,
            context_handle,
            input_message_buffer,
            output_message_buffer,
            conf_state,
            qop_state)

OM_uint32 *		minor_status;
gss_ctx_id_t		context_handle;
gss_buffer_t		input_message_buffer;
gss_buffer_t		output_message_buffer;
int *			conf_state;
gss_qop_t *		qop_state;

{
/* EXPORT DELETE START */
    OM_uint32		status;
    gss_union_ctx_id_t	ctx;
    gss_mechanism	mech;

    if (minor_status != NULL)
	*minor_status = 0;

    if (output_message_buffer != GSS_C_NO_BUFFER) {
	output_message_buffer->length = 0;
	output_message_buffer->value = NULL;
    }

    if (minor_status == NULL)
	return (GSS_S_CALL_INACCESSIBLE_WRITE);

    if (context_handle == GSS_C_NO_CONTEXT)
	return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);

    if (input_message_buffer == GSS_C_NO_BUFFER ||
	GSS_EMPTY_BUFFER(input_message_buffer))

	return (GSS_S_CALL_INACCESSIBLE_READ);

    if (output_message_buffer == GSS_C_NO_BUFFER)
	return (GSS_S_CALL_INACCESSIBLE_WRITE);

    /*
     * select the approprate underlying mechanism routine and
      * call it.
      */
     ctx = (gss_union_ctx_id_t) context_handle;
    if (ctx->internal_ctx_id == GSS_C_NO_CONTEXT)
	return (GSS_S_NO_CONTEXT);
     mech = gssint_get_mechanism (ctx->mech_type);
 
     if (mech) {
	if (mech->gss_unwrap) {
	    status = mech->gss_unwrap(minor_status,
				      ctx->internal_ctx_id,
				      input_message_buffer,
				      output_message_buffer,
				      conf_state,
				      qop_state);
	    if (status != GSS_S_COMPLETE)
		map_error(minor_status, mech);
	} else if (mech->gss_unwrap_aead || mech->gss_unwrap_iov) {
	    status = gssint_unwrap_aead(mech,
					minor_status,
					ctx,
					input_message_buffer,
					GSS_C_NO_BUFFER,
					output_message_buffer,
					conf_state,
					(gss_qop_t *)qop_state);
	} else
	    status = GSS_S_UNAVAILABLE;

	return(status);
    }

/* EXPORT DELETE END */

    return (GSS_S_BAD_MECH);
}
