From 2b939412b464bdf5e3ee9878566c5b4291dd230e Mon Sep 17 00:00:00 2001 From: dragos-dinulescu <73701279+dragos-dinulescu@users.noreply.github.com> Date: Tue, 25 Aug 2026 14:29:00 -0400 Subject: [PATCH 1/2] Guard SSPI teardown calls against NULL Curl_pSecFn Curl_sspi_global_cleanup() NULLs the SSPI dispatch table without unloading secur32, so auth state destroyed after curl_global_cleanup() (for example pooled connections torn down by a late curl_multi_cleanup) crashes dereferencing Curl_pSecFn in DeleteSecurityContext / FreeCredentialsHandle. Skip the SSPI calls in that case and still free our own allocations; the OS reclaims the handles at process exit. Co-Authored-By: Claude Fable 5 --- lib/vauth/digest_sspi.c | 6 ++++-- lib/vauth/krb5_sspi.c | 9 ++++++--- lib/vauth/ntlm_sspi.c | 9 ++++++--- lib/vauth/spnego_sspi.c | 9 ++++++--- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index f0b6780fca6f..68bbad0f64f5 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -642,9 +642,11 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) /* Reset any variables */ digest->input_token_len = 0; - /* Delete security context */ + /* Delete security context; Curl_pSecFn is NULL after + curl_global_cleanup(), the OS then reclaims handles at process exit */ if(digest->http_context) { - Curl_pSecFn->DeleteSecurityContext(digest->http_context); + if(Curl_pSecFn) + Curl_pSecFn->DeleteSecurityContext(digest->http_context); curlx_safefree(digest->http_context); } diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c index e7491be022f8..076cb7e64c20 100644 --- a/lib/vauth/krb5_sspi.c +++ b/lib/vauth/krb5_sspi.c @@ -425,16 +425,19 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, */ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) { - /* Free our security context */ + /* Free our security context; Curl_pSecFn is NULL after + curl_global_cleanup(), the OS then reclaims handles at process exit */ if(krb5->context) { - Curl_pSecFn->DeleteSecurityContext(krb5->context); + if(Curl_pSecFn) + Curl_pSecFn->DeleteSecurityContext(krb5->context); curlx_free(krb5->context); krb5->context = NULL; } /* Free our credentials handle */ if(krb5->credentials) { - Curl_pSecFn->FreeCredentialsHandle(krb5->credentials); + if(Curl_pSecFn) + Curl_pSecFn->FreeCredentialsHandle(krb5->credentials); curlx_free(krb5->credentials); krb5->credentials = NULL; } diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 4c41eb21f4e6..d30e3aa9d5a7 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -322,16 +322,19 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, */ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) { - /* Free our security context */ + /* Free our security context; Curl_pSecFn is NULL after + curl_global_cleanup(), the OS then reclaims handles at process exit */ if(ntlm->context) { - Curl_pSecFn->DeleteSecurityContext(ntlm->context); + if(Curl_pSecFn) + Curl_pSecFn->DeleteSecurityContext(ntlm->context); curlx_free(ntlm->context); ntlm->context = NULL; } /* Free our credentials handle */ if(ntlm->credentials) { - Curl_pSecFn->FreeCredentialsHandle(ntlm->credentials); + if(Curl_pSecFn) + Curl_pSecFn->FreeCredentialsHandle(ntlm->credentials); curlx_free(ntlm->credentials); ntlm->credentials = NULL; } diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 1baf59320a37..fcd8a8751dfb 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -310,16 +310,19 @@ CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego, */ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) { - /* Free our security context */ + /* Free our security context; Curl_pSecFn is NULL after + curl_global_cleanup(), the OS then reclaims handles at process exit */ if(nego->context) { - Curl_pSecFn->DeleteSecurityContext(nego->context); + if(Curl_pSecFn) + Curl_pSecFn->DeleteSecurityContext(nego->context); curlx_free(nego->context); nego->context = NULL; } /* Free our credentials handle */ if(nego->credentials) { - Curl_pSecFn->FreeCredentialsHandle(nego->credentials); + if(Curl_pSecFn) + Curl_pSecFn->FreeCredentialsHandle(nego->credentials); curlx_free(nego->credentials); nego->credentials = NULL; } From 7df747ef03aeed0020c24952a01c7fe570e15710 Mon Sep 17 00:00:00 2001 From: dragos-dinulescu <73701279+dragos-dinulescu@users.noreply.github.com> Date: Tue, 25 Aug 2026 15:01:27 -0400 Subject: [PATCH 2/2] Remove comments from the SSPI teardown guards Co-Authored-By: Claude Fable 5 --- lib/vauth/digest_sspi.c | 3 +-- lib/vauth/krb5_sspi.c | 3 +-- lib/vauth/ntlm_sspi.c | 3 +-- lib/vauth/spnego_sspi.c | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index 68bbad0f64f5..3848abc9034b 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -642,8 +642,7 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) /* Reset any variables */ digest->input_token_len = 0; - /* Delete security context; Curl_pSecFn is NULL after - curl_global_cleanup(), the OS then reclaims handles at process exit */ + /* Delete security context */ if(digest->http_context) { if(Curl_pSecFn) Curl_pSecFn->DeleteSecurityContext(digest->http_context); diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c index 076cb7e64c20..55f903782afb 100644 --- a/lib/vauth/krb5_sspi.c +++ b/lib/vauth/krb5_sspi.c @@ -425,8 +425,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, */ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) { - /* Free our security context; Curl_pSecFn is NULL after - curl_global_cleanup(), the OS then reclaims handles at process exit */ + /* Free our security context */ if(krb5->context) { if(Curl_pSecFn) Curl_pSecFn->DeleteSecurityContext(krb5->context); diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index d30e3aa9d5a7..56855724c82c 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -322,8 +322,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, */ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) { - /* Free our security context; Curl_pSecFn is NULL after - curl_global_cleanup(), the OS then reclaims handles at process exit */ + /* Free our security context */ if(ntlm->context) { if(Curl_pSecFn) Curl_pSecFn->DeleteSecurityContext(ntlm->context); diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index fcd8a8751dfb..97f319510b88 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -310,8 +310,7 @@ CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego, */ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) { - /* Free our security context; Curl_pSecFn is NULL after - curl_global_cleanup(), the OS then reclaims handles at process exit */ + /* Free our security context */ if(nego->context) { if(Curl_pSecFn) Curl_pSecFn->DeleteSecurityContext(nego->context);