commit 56f7b1bc95a2a3eeb420e069e7655fb181ade5cf
Author: Greg Hudson <ghudson@mit.edu>
Date:   Fri Jul 14 13:02:46 2017 -0400

    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

diff --git a/src/lib/gssapi/mechglue/g_accept_sec_context.c b/src/lib/gssapi/mechglue/g_accept_sec_context.c
index 95730165f..5ec47be56 100644
--- a/src/lib/gssapi/mechglue/g_accept_sec_context.c
+++ b/src/lib/gssapi/mechglue/g_accept_sec_context.c
@@ -170,11 +170,11 @@ gss_cred_id_t *		d_cred;
 	    return (status);
 	}
 
-	/* set the new context handle to caller's data */
-	*context_handle = (gss_ctx_id_t)union_ctx_id;
     } else {
 	union_ctx_id = (gss_union_ctx_id_t)*context_handle;
 	token_mech_type = union_ctx_id->mech_type;
+        if (union_ctx_id->internal_ctx_id == GSS_C_NO_CONTEXT)
+            return GSS_S_NO_CONTEXT;
     }
     
     /* 
@@ -208,8 +208,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)
-		return 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)
@@ -332,21 +334,28 @@ gss_cred_id_t *		d_cred;
 
 	    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) {
-	if (union_ctx_id->mech_type) {
-	    if (union_ctx_id->mech_type->elements)
-		free(union_ctx_id->mech_type->elements);
-	    free(union_ctx_id->mech_type);
-	    *context_handle = GSS_C_NO_CONTEXT;
-	}
-	free(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);
+            free(union_ctx_id->mech_type);
+        }
+        free(union_ctx_id);
     }
 
     if (output_token->length)
diff --git a/src/lib/gssapi/mechglue/g_context_time.c b/src/lib/gssapi/mechglue/g_context_time.c
index 5ce6b56d8..43851449f 100644
--- a/src/lib/gssapi/mechglue/g_context_time.c
+++ b/src/lib/gssapi/mechglue/g_context_time.c
@@ -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) {
diff --git a/src/lib/gssapi/mechglue/g_delete_sec_context.c b/src/lib/gssapi/mechglue/g_delete_sec_context.c
index bf2e9010f..af1f3204b 100644
--- a/src/lib/gssapi/mechglue/g_delete_sec_context.c
+++ b/src/lib/gssapi/mechglue/g_delete_sec_context.c
@@ -91,14 +91,16 @@ gss_buffer_t		output_token;
     
     if (mech) {
 
-	if (mech->gss_delete_sec_context)
-	    status = mech->gss_delete_sec_context(
-						  mech->context,
-						  minor_status,
-						  &ctx->internal_ctx_id,
-						  output_token);
-	else
-	    status = GSS_S_UNAVAILABLE;
+    if (ctx->internal_ctx_id != GSS_C_NO_CONTEXT) {
+        if (mech->gss_delete_sec_context)
+            status = mech->gss_delete_sec_context(
+                              mech->context,
+                              minor_status,
+                              &ctx->internal_ctx_id,
+                              output_token);
+        else
+            status = GSS_S_UNAVAILABLE;
+    }
 
 	/* now free up the space for the union context structure */
 	free(ctx->mech_type->elements);
diff --git a/src/lib/gssapi/mechglue/g_exp_sec_context.c b/src/lib/gssapi/mechglue/g_exp_sec_context.c
index 1490a2b37..72365fd32 100644
--- a/src/lib/gssapi/mechglue/g_exp_sec_context.c
+++ b/src/lib/gssapi/mechglue/g_exp_sec_context.c
@@ -94,6 +94,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;
diff --git a/src/lib/gssapi/mechglue/g_init_sec_context.c b/src/lib/gssapi/mechglue/g_init_sec_context.c
index 52f93f124..6b3ffa87a 100644
--- a/src/lib/gssapi/mechglue/g_init_sec_context.c
+++ b/src/lib/gssapi/mechglue/g_init_sec_context.c
@@ -193,8 +193,13 @@ OM_uint32 *		time_rec;
 
 	/* copy the supplied context handle */
 	union_ctx_id->internal_ctx_id = GSS_C_NO_CONTEXT;
-    } else
-	union_ctx_id = *context_handle;
+    } else {
+	    union_ctx_id = *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.
@@ -225,12 +230,13 @@ OM_uint32 *		time_rec;
 	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
-	 */
+        /*
+         * 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.
+        */
 	if (*context_handle == GSS_C_NO_CONTEXT) {
 	    free(union_ctx_id->mech_type->elements);
 	    free(union_ctx_id->mech_type);
diff --git a/src/lib/gssapi/mechglue/g_inq_context.c b/src/lib/gssapi/mechglue/g_inq_context.c
index aeab57d60..b512af007 100644
--- a/src/lib/gssapi/mechglue/g_inq_context.c
+++ b/src/lib/gssapi/mechglue/g_inq_context.c
@@ -115,6 +115,8 @@ int *		open;
      */
     
     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 ||
diff --git a/src/lib/gssapi/mechglue/g_process_context.c b/src/lib/gssapi/mechglue/g_process_context.c
index 18b12050c..d737cc43f 100644
--- a/src/lib/gssapi/mechglue/g_process_context.c
+++ b/src/lib/gssapi/mechglue/g_process_context.c
@@ -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) {
diff --git a/src/lib/gssapi/mechglue/g_seal.c b/src/lib/gssapi/mechglue/g_seal.c
index f784be1ab..0fdcad736 100644
--- a/src/lib/gssapi/mechglue/g_seal.c
+++ b/src/lib/gssapi/mechglue/g_seal.c
@@ -103,6 +103,8 @@ gss_buffer_t		output_message_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) {
@@ -182,6 +184,8 @@ gss_wrap_size_limit(minor_status, context_handle, conf_req_flag,
      */
     
     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)
diff --git a/src/lib/gssapi/mechglue/g_sign.c b/src/lib/gssapi/mechglue/g_sign.c
index c0510afe2..1fd56b8b5 100644
--- a/src/lib/gssapi/mechglue/g_sign.c
+++ b/src/lib/gssapi/mechglue/g_sign.c
@@ -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) {
diff --git a/src/lib/gssapi/mechglue/g_unseal.c b/src/lib/gssapi/mechglue/g_unseal.c
index 56f567359..30e64dd2e 100644
--- a/src/lib/gssapi/mechglue/g_unseal.c
+++ b/src/lib/gssapi/mechglue/g_unseal.c
@@ -77,6 +77,8 @@ int *			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) {
diff --git a/src/lib/gssapi/mechglue/g_verify.c b/src/lib/gssapi/mechglue/g_verify.c
index 5f0374643..1f61d0b5f 100644
--- a/src/lib/gssapi/mechglue/g_verify.c
+++ b/src/lib/gssapi/mechglue/g_verify.c
@@ -65,6 +65,8 @@ int *			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) {
-- 
2.19.2

