CVE-2017-11462 (published 2017-09-13T16:29Z)

Double free vulnerability in MIT Kerberos 5 (aka krb5) allows attackers to have unspecified impact via vectors involving automatic deletion of security contexts on error.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-415
cwe_name: Double Free
description: The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations.
extended_description: When a program calls free() twice with the same argument, the program's memory management data structures become corrupted. This corruption can cause the program to crash or, in some circumstances, cause two later calls to malloc() to return the same pointer. If malloc() returns the same value twice and the program later gives the attacker control over the data that is written into this doubly-allocated memory, the program becomes vulnerable to a buffer overflow attack.
         

Fix information:
repo_url: https://github.com/krb5/krb5
commit_details: repo_url: https://github.com/krb5/krb5
author: Greg Hudson
author_date: 2017-07-14 13:02:46-04:00
msg: 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
repository: repo_url: https://github.com/krb5/krb5
repo_name: krb5/krb5
description: mirror of MIT krb5 repository
repo_language: C
owner: krb5
file_changes: filename: g_accept_sec_context.c
change_type: ModificationType.MODIFY
diff: @@ -216,6 +216,8 @@ gss_cred_id_t *		d_cred;
     } else {
 	union_ctx_id = (gss_union_ctx_id_t)*context_handle;
 	selected_mech = union_ctx_id->mech_type;
+	if (union_ctx_id->internal_ctx_id == GSS_C_NO_CONTEXT)
+	    return (GSS_S_NO_CONTEXT);
     }
 
     /* Now create a new context if we didn't get one. */
@@ -234,9 +236,6 @@ gss_cred_id_t *		d_cred;
 	    free(union_ctx_id);
 	    return (status);
 	}
-
-	/* set the new context handle to caller's data */
-	*context_handle = (gss_ctx_id_t)union_ctx_id;
     }
 
     /*
@@ -277,8 +276,10 @@ gss_cred_id_t *		d_cred;
 					d_cred ? &tmp_d_cred : NULL);
 
 	    /* If there's more work to do, keep going... */
-	    if (status == GSS_S_CONTINUE_NEEDED)
+	    if (status == GSS_S_CONTINUE_NEEDED) {
+		*context_handle = (gss_ctx_id_t)union_ctx_id;
 		return GSS_S_CONTINUE_NEEDED;
+	    }
 
 	    /* if the call failed, return with failure */
 	    if (status != GSS_S_COMPLETE) {
@@ -364,14 +365,22 @@ gss_cred_id_t *		d_cred;
 		*mech_type = gssint_get_public_oid(actual_mech);
 	    if (ret_flags != NULL)
 		*ret_flags = temp_ret_flags;
-	    return	(status);
+	    *context_handle = (gss_ctx_id_t)union_ctx_id;
+	    return GSS_S_COMPLETE;
     } else {
 
 	status = GSS_S_BAD_MECH;
     }
 
 error_out:
-    if (union_ctx_id) {
+	/*
+	 * RFC 2744 5.1 requires that we not create a context on a failed first
+	 * call to accept, and recommends that on a failed subsequent call we
+	 * make the caller responsible for calling gss_delete_sec_context.
+	 * Even if the mech deleted its context, keep the union context around
+	 * for the caller to delete.
+	 */
+    if (union_ctx_id && *context_handle == GSS_C_NO_CONTEXT) {
 	if (union_ctx_id->mech_type) {
 	    if (union_ctx_id->mech_type->elements)
 		free(union_ctx_id->mech_type->elements);
@@ -384,7 +393,6 @@ error_out:
 					 GSS_C_NO_BUFFER);
 	}
 	free(union_ctx_id);
-	*context_handle = GSS_C_NO_CONTEXT;
     }
 
     if (src_name)

programming_language: C
filename: g_complete_auth_token.c
change_type: ModificationType.MODIFY
diff: @@ -52,6 +52,8 @@ gss_complete_auth_token (OM_uint32 *minor_status,
      */
 
     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 != NULL) {

programming_language: C
filename: g_context_time.c
change_type: ModificationType.MODIFY
diff: @@ -58,6 +58,8 @@ OM_uint32 *		time_rec;
      */
 
     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) {

programming_language: C
filename: g_delete_sec_context.c
change_type: ModificationType.MODIFY
diff: @@ -87,12 +87,14 @@ gss_buffer_t		output_token;
     if (GSSINT_CHK_LOOP(ctx))
 	return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);
 
-    status = gssint_delete_internal_sec_context(minor_status,
-						ctx->mech_type,
-						&ctx->internal_ctx_id,
-						output_token);
-    if (status)
-	return status;
+    if (ctx->internal_ctx_id != GSS_C_NO_CONTEXT) {
+	status = gssint_delete_internal_sec_context(minor_status,
+						    ctx->mech_type,
+						    &ctx->internal_ctx_id,
+						    output_token);
+	if (status)
+	    return status;
+    }
 
     /* now free up the space for the union context structure */
     free(ctx->mech_type->elements);

programming_language: C
filename: g_exp_sec_context.c
change_type: ModificationType.MODIFY
diff: @@ -95,6 +95,8 @@ gss_buffer_t		interprocess_token;
      */
 
     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)
 	return GSS_S_BAD_MECH;

programming_language: C
filename: g_init_sec_context.c
change_type: ModificationType.MODIFY
diff: @@ -192,8 +192,13 @@ OM_uint32 *		time_rec;
 
 	/* copy the supplied context handle */
 	union_ctx_id->internal_ctx_id = GSS_C_NO_CONTEXT;
-    } else
+    } else {
 	union_ctx_id = (gss_union_ctx_id_t)*context_handle;
+	if (union_ctx_id->internal_ctx_id == GSS_C_NO_CONTEXT) {
+	    status = GSS_S_NO_CONTEXT;
+	    goto end;
+	}
+    }
 
     /*
      * get the appropriate cred handle from the union cred struct.
@@ -224,15 +229,13 @@ OM_uint32 *		time_rec;
 
     if (status != GSS_S_COMPLETE && status != GSS_S_CONTINUE_NEEDED) {
 	/*
-	 * The spec says the preferred method is to delete all context info on
-	 * the first call to init, and on all subsequent calls make the caller
-	 * responsible for calling gss_delete_sec_context.  However, if the
-	 * mechanism decided to delete the internal context, we should also
-	 * delete the union context.
+	 * RFC 2744 5.19 requires that we not create a context on a failed
+	 * first call to init, and recommends that on a failed subsequent call
+	 * we make the caller responsible for calling gss_delete_sec_context.
+	 * Even if the mech deleted its context, keep the union context around
+	 * for the caller to delete.
 	 */
 	map_error(minor_status, mech);
-	if (union_ctx_id->internal_ctx_id == GSS_C_NO_CONTEXT)
-	    *context_handle = GSS_C_NO_CONTEXT;
 	if (*context_handle == GSS_C_NO_CONTEXT) {
 	    free(union_ctx_id->mech_type->elements);
 	    free(union_ctx_id->mech_type);

programming_language: C
filename: g_inq_context.c
change_type: ModificationType.MODIFY
diff: @@ -104,6 +104,8 @@ gss_inquire_context(
      */
 
     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 || !mech->gss_inquire_context || !mech->gss_display_name ||

programming_language: C
filename: g_prf.c
change_type: ModificationType.MODIFY
diff: @@ -59,6 +59,8 @@ gss_pseudo_random (OM_uint32 *minor_status,
      */
 
     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 != NULL) {

programming_language: C
filename: g_process_context.c
change_type: ModificationType.MODIFY
diff: @@ -61,6 +61,8 @@ gss_buffer_t		token_buffer;
      */
 
     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) {

programming_language: C
filename: g_seal.c
change_type: ModificationType.MODIFY
diff: @@ -92,6 +92,8 @@ gss_wrap( OM_uint32 *minor_status,
      */
 
     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) {
@@ -226,6 +228,8 @@ gss_wrap_size_limit(OM_uint32  *minor_status,
      */
 
     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)

programming_language: C
filename: g_sign.c
change_type: ModificationType.MODIFY
diff: @@ -94,6 +94,8 @@ gss_buffer_t		msg_token;
      */
 
     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) {

programming_language: C
filename: g_unseal.c
change_type: ModificationType.MODIFY
diff: @@ -76,6 +76,8 @@ gss_qop_t *		qop_state;
      * 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) {

programming_language: C
filename: g_unwrap_aead.c
change_type: ModificationType.MODIFY
diff: @@ -186,6 +186,8 @@ gss_qop_t		*qop_state;
      * 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)

programming_language: C
filename: g_unwrap_iov.c
change_type: ModificationType.MODIFY
diff: @@ -89,6 +89,8 @@ int			iov_count;
      */
 
     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) {
@@ -128,6 +130,8 @@ gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
 
     /* 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 == NULL)
 	return GSS_S_BAD_MECH;

programming_language: C
filename: g_verify.c
change_type: ModificationType.MODIFY
diff: @@ -65,6 +65,8 @@ gss_qop_t *		qop_state;
      */
 
     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) {

programming_language: C
filename: g_wrap_aead.c
change_type: ModificationType.MODIFY
diff: @@ -256,6 +256,8 @@ gss_buffer_t		output_message_buffer;
      * 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)
 	return (GSS_S_BAD_MECH);

programming_language: C
filename: g_wrap_iov.c
change_type: ModificationType.MODIFY
diff: @@ -93,6 +93,8 @@ int			iov_count;
      */
 
     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) {
@@ -151,6 +153,8 @@ int			iov_count;
      */
 
     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) {
@@ -190,6 +194,8 @@ gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
 
     /* 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 == NULL)
 	return GSS_S_BAD_MECH;
@@ -218,6 +224,8 @@ gss_get_mic_iov_length(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
 
     /* 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 == NULL)
 	return GSS_S_BAD_MECH;

programming_language: C