Resumen
curl mantiene un pool de conexiones y, antes de abrir una nueva, busca si ya tiene una abierta con la misma configuración TLS. Esa comparación la hace match_ssl_primary_config(), que solo revisa los campos de struct ssl_primary_config. El problema es que el indicador native_ca_store (que decide si se usa el almacén de certificados del sistema, en macOS a través de SecTrust) no vive en esa estructura, sino en struct ssl_config_data (línea 73 de vtls_config.h), así que nunca entra en la comparación.
En macOS esto tiene consecuencias por cómo se rellena la configuración por defecto. Curl_ssl_easy_config_complete() (línea 258) activa native_ca_store = TRUE automáticamente cuando el usuario no ha indicado ningún CA propio, y lo hace sin tocar ssl_options. Si además curl está compilado con CURL_CA_BUNDLE, la ruta de ese bundle se copia a primary.CAfile (línea 269). El resultado es que dos transferencias pueden tener exactamente los mismos campos "primarios" y, sin embargo, una esperar verificación con SecTrust y la otra no:
- Transferencia A: fija
CURLOPT_CAINFOexplícitamente con la misma ruta queCURL_CA_BUNDLE. Quedacustom_cafile=TRUE, no se activa el almacén nativo (native_ca_store=FALSE) y el certificado se verifica solo con OpenSSL. - Transferencia B: no fija
CURLOPT_CAINFO. El autorrelleno pone el mismoCAfile,custom_cafile=FALSEy se activanative_ca_store=TRUE, por lo que debería verificarse con SecTrust.
Como CAfile y ssl_options coinciden, match_ssl_primary_config() da las conexiones por equivalentes y B reutiliza la conexión que abrió A. SecTrust no llega a ejecutarse nunca para B.
El fallo tiene asignado el CVE-2026-80231, con severidad baja.
Pasos de reproducción
El investigador lo reprodujo en un Mac físico con un programa de prueba (poc-native-ca.c) enlazado contra un curl compilado con estas opciones:
-DUSE_APPLE_SECTRUST=ON -DCURL_CA_BUNDLE=<path> -DCURL_USE_OPENSSL=ON
- Lanzar una primera transferencia HTTPS (A) fijando
CURLOPT_CAINFOcon la misma ruta que se usó enCURL_CA_BUNDLE. La verificación la hace OpenSSL. - Lanzar después una segunda transferencia (B) sin fijar
CURLOPT_CAINFO. Por el autorrelleno, B tienenative_ca_store=TRUEy debería verificar con SecTrust. - Comprobar que aparece el mensaje:
Reusing existing https: connection
B se sirve por la conexión de A y SecTrust no se ejecuta.
La cadena de causas, en orden:
Curl_ssl_easy_config_complete()(línea 258): si se cumple!custom_capath && !custom_cafile && !custom_cablob, ponenative_ca_store = TRUE, sin modificarssl_options.- El autorrelleno de
CURL_CA_BUNDLE(línea 269) fijaSTRING_SSL_CAFILE, con lo queprimary.CAfilepasa a ser la ruta del bundle. - La transferencia A fija
CURLOPT_CAINFOa esa misma ruta:custom_cafile=TRUE, no hay activación automática ynative_ca_store=FALSE. Mismoprimary.CAfiley mismossl_options=0que B. match_ssl_primary_config()encuentra todos los campos iguales y reutiliza la conexión, porquenative_ca_storeno forma parte de lo que compara.
Impacto
Una transferencia que espera que el certificado del servidor se valide con la política de confianza del sistema (SecTrust) puede acabar usando, sin aviso, una conexión que solo validó OpenSSL, o al revés. Un certificado que una de las dos políticas acepta y la otra rechazaría pasa sin volver a verificarse. El escenario descrito requiere un build de macOS con SecTrust y OpenSSL activados y CURL_CA_BUNDLE definido.
Remediación
El investigador propuso dos opciones:
- Mover
native_ca_storedentro dessl_primary_config, para quematch_ssl_primary_config()lo compare como el resto de campos. - O añadir una comprobación explícita de ese campo en
Curl_ssl_conn_config_match().
El reporte figura como resuelto, pero no detalla cuál de las dos se aplicó ni en qué versión.