From e353652831978d3cf1b756def7782b52dc495668 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 29 Feb 2016 16:51:22 -0500
Subject: [PATCH] Skip unnecessary mech calls in gss_inquire_cred()

If the caller does not request a name, lifetime, or cred_usage when
calling gss_inquire_cred(), service the call by copying the mechanism
list (if requested) but do not call into the mech.

This change alleviates an issue (reported by Adam Bernstein) where
SPNEGO can fail in the presence of expired krb5 credentials rather
than proceeding with a different mechanism, or can resolve a krb5
credential with the benefit of the target name.

ticket: 8373
target_version: 1.14-next
target_version: 1.13-next
tags: pullup
---
 src/lib/gssapi/mechglue/g_inq_cred.c | 39 ++++++++++++++++++++----------------
 1 file changed, 22 insertions(+), 17 deletions(-)

diff --git a/src/lib/gssapi/mechglue/g_inq_cred.c b/src/lib/gssapi/mechglue/g_inq_cred.c
index c5577d4..9111962 100644
--- a/src/lib/gssapi/mechglue/g_inq_cred.c
+++ b/src/lib/gssapi/mechglue/g_inq_cred.c
@@ -92,27 +92,32 @@ gss_OID_set *		mechanisms;
 	mech_cred = GSS_C_NO_CREDENTIAL;
 	mech = gssint_get_mechanism(GSS_C_NULL_OID);
     }
-    if (mech == NULL)
-	return (GSS_S_DEFECTIVE_CREDENTIAL);
-    if (!mech->gss_inquire_cred)
-	return (GSS_S_UNAVAILABLE);
 
-    status = mech->gss_inquire_cred(minor_status, mech_cred,
-				    name ? &mech_name : NULL,
-				    lifetime, cred_usage, NULL);
-    if (status != GSS_S_COMPLETE) {
-	map_error(minor_status, mech);
-	return(status);
-    }
+    /* Skip the call into the mech if the caller doesn't care about any of the
+     * values we would ask for. */
+    if (name != NULL || lifetime != NULL || cred_usage != NULL) {
+	if (mech == NULL)
+	    return (GSS_S_DEFECTIVE_CREDENTIAL);
+	if (!mech->gss_inquire_cred)
+	    return (GSS_S_UNAVAILABLE);
 
-    if (name) {
-	/* Convert mech_name into a union_name equivalent. */
-	status = gssint_convert_name_to_union_name(&temp_minor_status,
-						   mech, mech_name, name);
+	status = mech->gss_inquire_cred(minor_status, mech_cred,
+					name ? &mech_name : NULL,
+					lifetime, cred_usage, NULL);
 	if (status != GSS_S_COMPLETE) {
-	    *minor_status = temp_minor_status;
 	    map_error(minor_status, mech);
-	    return (status);
+	    return(status);
+	}
+
+	if (name) {
+	    /* Convert mech_name into a union_name equivalent. */
+	    status = gssint_convert_name_to_union_name(&temp_minor_status,
+						       mech, mech_name, name);
+	    if (status != GSS_S_COMPLETE) {
+		*minor_status = temp_minor_status;
+		map_error(minor_status, mech);
+		return (status);
+	    }
 	}
     }