diff --git a/src/llm_openai.c b/src/llm_openai.c index fe18de3..84d896d 100644 --- a/src/llm_openai.c +++ b/src/llm_openai.c @@ -22,6 +22,45 @@ #define ERRBODY_MAX 4096 #define LLM_MAX_RETRIES 3 +/* Point libcurl at a CA bundle on Windows. + * + * An OpenSSL-based libcurl keeps the path it was built with -- MSYS2's is + * "/mingw64/etc/ssl/certs/ca-bundle.crt", which resolves to nothing once the + * binary runs outside an MSYS2 shell, and that build ignores CURL_CA_BUNDLE. + * Every HTTPS request then dies with CURLE_SSL_CACERT_BADFILE (77). + * + * Order: $CST_CA_BUNDLE, then ca-bundle.crt beside the executable. If neither + * exists nothing is set, so a libcurl linked against Schannel (the Windows + * certificate store) behaves exactly as before. */ +static void llm_apply_ca_bundle(CURL *curl) { +#if defined(_WIN32) + static char ca[OS_PATH_MAX]; + static int checked; + if (!checked) { + checked = 1; + const char *env = getenv("CST_CA_BUNDLE"); + if (env && env[0]) { + snprintf(ca, sizeof(ca), "%s", env); + } else { + char dir[OS_PATH_MAX]; + if (os_exe_dir(dir, sizeof(dir)) == 0) + os_path_join(ca, sizeof(ca), dir, "ca-bundle.crt"); + } + if (ca[0]) { + FILE *f = fopen(ca, "rb"); + if (f) + fclose(f); + else + ca[0] = '\0'; + } + } + if (ca[0]) + curl_easy_setopt(curl, CURLOPT_CAINFO, ca); +#else + (void)curl; +#endif +} + /* --- wire body ----------------------------------------------------------- */ static size_t wire_tool_cap(const cst_llm_request_t *req) { @@ -621,6 +660,7 @@ int cst_llm_chat_stream(const cst_llm_request_t *req, char *err, ret = -1; break; } + llm_apply_ca_bundle(curl); sse_reset(&s); s.curl = curl; if (rec_dir && rec_dir[0]) { @@ -765,10 +805,11 @@ int cst_llm_list_models(const char *api_base, const char *api_key, } CURL *curl = curl_easy_init(); if (!curl) { - snprintf(err, err_sz, "curl init failed"); - return -1; - } - char url[1024]; + snprintf(err, err_sz, "curl init failed"); + return -1; + } + llm_apply_ca_bundle(curl); + char url[1024]; size_t bl = strlen(api_base); while (bl && api_base[bl - 1] == '/') bl--;