]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
gssapi: make channel binding conditional on GSS_C_CHANNEL_BOUND_FLAG
authorDevdatta Talele <devtalele0@gmail.com>
Mon, 20 Oct 2025 15:21:43 +0000 (20:51 +0530)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 3 Nov 2025 17:16:54 +0000 (18:16 +0100)
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

lib/curl_gssapi.h
lib/http_negotiate.c
lib/vauth/spnego_gssapi.c
lib/vauth/vauth.h

index 6df7e059d325188fc016de3c5a9cd23771b3f1c0..1a2bbabdf5163c07630b3ff5f0f175d1ec23fceb 100644 (file)
 #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;
 
index 8a19c1ad87387c122b0d9e18d1a5c11223926cab..136cb07641c453eb4c30e28a7e2538f1679de8bd 100644 (file)
@@ -124,7 +124,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
   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)) {
@@ -138,13 +138,13 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
 #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
 
index 4ed02a398ca3fadf7919310d7db9daa56ee55d0f..4e9125ba4400e5a5de957b37ddb2422a74170781 100644 (file)
@@ -96,7 +96,9 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
   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;
@@ -157,12 +159,14 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
   }
 
   /* 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,
index 9a33ca0c23172a72d87ab9022c6211d5dba8dc5a..2ba8e471dc0032cfa7d6a0e7fa18f0d42cea67b2 100644 (file)
@@ -306,7 +306,9 @@ struct negotiatedata {
   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