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 498bfc4..ac872e4 100644
--- a/src/lib/gssapi/mechglue/g_accept_sec_context.c
+++ b/src/lib/gssapi/mechglue/g_accept_sec_context.c
@@ -174,8 +174,10 @@ gss_cred_id_t *		d_cred;
 	/* 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;
+        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);
     }
 
     /*
@@ -207,8 +209,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) {
@@ -339,21 +343,28 @@ gss_cred_id_t *		d_cred;
 		(void) gss_release_oid(&temp_minor_status, &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) {
-	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 (src_name)
diff --git a/src/lib/gssapi/mechglue/g_complete_auth_token.c b/src/lib/gssapi/mechglue/g_complete_auth_token.c
index 9181551..3b8bfea 100644
--- a/src/lib/gssapi/mechglue/g_complete_auth_token.c
+++ b/src/lib/gssapi/mechglue/g_complete_auth_token.c
@@ -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) {
diff --git a/src/lib/gssapi/mechglue/g_context_time.c b/src/lib/gssapi/mechglue/g_context_time.c
index 2ff8d09..74110b3 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 4bf0dec..8a55a22 100644
--- a/src/lib/gssapi/mechglue/g_delete_sec_context.c
+++ b/src/lib/gssapi/mechglue/g_delete_sec_context.c
@@ -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);
diff --git a/src/lib/gssapi/mechglue/g_exp_sec_context.c b/src/lib/gssapi/mechglue/g_exp_sec_context.c
index 03a6f2b..236fd85 100644
--- a/src/lib/gssapi/mechglue/g_exp_sec_context.c
+++ b/src/lib/gssapi/mechglue/g_exp_sec_context.c
@@ -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;
diff --git a/src/lib/gssapi/mechglue/g_init_sec_context.c b/src/lib/gssapi/mechglue/g_init_sec_context.c
index 21bc345..8265ddf 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.
@@ -224,12 +229,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.
+        */
 	map_error(minor_status, mech);
 	if (*context_handle == GSS_C_NO_CONTEXT) {
 	    free(union_ctx_id->mech_type->elements);
diff --git a/src/lib/gssapi/mechglue/g_inq_context.c b/src/lib/gssapi/mechglue/g_inq_context.c
index bb6d569..d2a0745 100644
--- a/src/lib/gssapi/mechglue/g_inq_context.c
+++ b/src/lib/gssapi/mechglue/g_inq_context.c
@@ -103,6 +103,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 ||
diff --git a/src/lib/gssapi/mechglue/g_prf.c b/src/lib/gssapi/mechglue/g_prf.c
index 74a3a21..303ef9f 100644
--- a/src/lib/gssapi/mechglue/g_prf.c
+++ b/src/lib/gssapi/mechglue/g_prf.c
@@ -62,6 +62,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) {
diff --git a/src/lib/gssapi/mechglue/g_process_context.c b/src/lib/gssapi/mechglue/g_process_context.c
index bc260ae..829a3ab 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 f17241c..3db1ee0 100644
--- a/src/lib/gssapi/mechglue/g_seal.c
+++ b/src/lib/gssapi/mechglue/g_seal.c
@@ -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)
diff --git a/src/lib/gssapi/mechglue/g_sign.c b/src/lib/gssapi/mechglue/g_sign.c
index 86d641a..77f8f66 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 3e8053c..c94b1db 100644
--- a/src/lib/gssapi/mechglue/g_unseal.c
+++ b/src/lib/gssapi/mechglue/g_unseal.c
@@ -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) {
diff --git a/src/lib/gssapi/mechglue/g_unwrap_aead.c b/src/lib/gssapi/mechglue/g_unwrap_aead.c
index 8be6d6a..490c35b 100644
--- a/src/lib/gssapi/mechglue/g_unwrap_aead.c
+++ b/src/lib/gssapi/mechglue/g_unwrap_aead.c
@@ -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)
diff --git a/src/lib/gssapi/mechglue/g_unwrap_iov.c b/src/lib/gssapi/mechglue/g_unwrap_iov.c
index aad9c76..37499f9 100644
--- a/src/lib/gssapi/mechglue/g_unwrap_iov.c
+++ b/src/lib/gssapi/mechglue/g_unwrap_iov.c
@@ -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) {
diff --git a/src/lib/gssapi/mechglue/g_verify.c b/src/lib/gssapi/mechglue/g_verify.c
index 1578ae1..96163b4 100644
--- a/src/lib/gssapi/mechglue/g_verify.c
+++ b/src/lib/gssapi/mechglue/g_verify.c
@@ -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) {
diff --git a/src/lib/gssapi/mechglue/g_wrap_aead.c b/src/lib/gssapi/mechglue/g_wrap_aead.c
index 7c059b4..fec06a6 100644
--- a/src/lib/gssapi/mechglue/g_wrap_aead.c
+++ b/src/lib/gssapi/mechglue/g_wrap_aead.c
@@ -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);
diff --git a/src/lib/gssapi/mechglue/g_wrap_iov.c b/src/lib/gssapi/mechglue/g_wrap_iov.c
index 9586c58..bb27e6c 100644
--- a/src/lib/gssapi/mechglue/g_wrap_iov.c
+++ b/src/lib/gssapi/mechglue/g_wrap_iov.c
@@ -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) {
-- 
2.14.1

