Fixes #19109 - GSSAPI authentication fails on macOS with Apple's Heimdal
implementation which lacks GSS_C_CHANNEL_BOUND_FLAG support for TLS
channel binding.
Commit
0a5ea09a910e introduced TLS channel binding for SPNEGO/GSSAPI
authentication unconditionally, but Apple's Heimdal fork (used on macOS)
does not support this feature, causing "unsupported mechanism" errors
when authenticating to corporate HTTP services with Kerberos.
Solution:
- Add CURL_GSSAPI_HAS_CHANNEL_BINDING detection in curl_gssapi.h based
on GSS_C_CHANNEL_BOUND_FLAG presence (MIT Kerberos >= 1.19)
- Make negotiatedata.channel_binding_data field conditional in vauth.h
- Guard channel binding collection/cleanup in http_negotiate.c
- Guard channel binding usage in spnego_gssapi.c
This follows the same pattern as GSS_C_DELEG_POLICY_FLAG detection and
ensures graceful degradation when channel binding is unavailable while
maintaining full support for implementations that have it.
Changes:
- lib/curl_gssapi.h: Add feature detection macro
- lib/vauth/vauth.h: Make struct field conditional
- lib/http_negotiate.c: Conditional init/cleanup (2 locations)
- lib/vauth/spnego_gssapi.c: Conditional channel binding usage
Tested on macOS with Apple Heimdal (no channel binding) and Linux with
MIT Kerberos (with channel binding). Both configurations authenticate
successfully without errors.
Closes #19164
#include "urldata.h"
#ifdef HAVE_GSSAPI
+
+#ifdef GSS_C_CHANNEL_BOUND_FLAG /* MIT Kerberos 1.19+, missing from GNU GSS */
+#define CURL_GSSAPI_HAS_CHANNEL_BINDING
+#endif
+
extern gss_OID_desc Curl_spnego_mech_oid;
extern gss_OID_desc Curl_krb5_mech_oid;
neg_ctx->sslContext = conn->sslContext;
#endif
/* Check if the connection is using SSL and get the channel binding data */
-#ifdef HAVE_GSSAPI
+#ifdef CURL_GSSAPI_HAS_CHANNEL_BINDING
#ifdef USE_SSL
curlx_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE + 1);
if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
#else
curlx_dyn_init(&neg_ctx->channel_binding_data, 1);
#endif /* USE_SSL */
-#endif /* HAVE_GSSAPI */
+#endif /* CURL_GSSAPI_HAS_CHANNEL_BINDING */
/* Initialize the security context and decode our challenge */
result = Curl_auth_decode_spnego_message(data, userp, passwdp, service,
host, header, neg_ctx);
-#ifdef HAVE_GSSAPI
+#ifdef CURL_GSSAPI_HAS_CHANNEL_BINDING
curlx_dyn_free(&neg_ctx->channel_binding_data);
#endif
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
gss_channel_bindings_t chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
+#ifdef CURL_GSSAPI_HAS_CHANNEL_BINDING
struct gss_channel_bindings_struct chan;
+#endif
(void)user;
(void)password;
}
/* Set channel binding data if available */
+#ifdef CURL_GSSAPI_HAS_CHANNEL_BINDING
if(curlx_dyn_len(&nego->channel_binding_data)) {
memset(&chan, 0, sizeof(struct gss_channel_bindings_struct));
chan.application_data.length = curlx_dyn_len(&nego->channel_binding_data);
chan.application_data.value = curlx_dyn_ptr(&nego->channel_binding_data);
chan_bindings = &chan;
}
+#endif
/* Generate our challenge-response message */
major_status = Curl_gss_init_sec_context(data,
gss_ctx_id_t context;
gss_name_t spn;
gss_buffer_desc output_token;
+#ifdef CURL_GSSAPI_HAS_CHANNEL_BINDING
struct dynbuf channel_binding_data;
+#endif
#else
#ifdef USE_WINDOWS_SSPI
#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS