cmCurl.cxx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmCurl.h"
  4. #include <cm/string_view>
  5. #include <cmext/string_view>
  6. #if !defined(CMAKE_USE_SYSTEM_CURL) && !defined(_WIN32) && \
  7. !defined(__APPLE__) && !defined(CURL_CA_BUNDLE) && !defined(CURL_CA_PATH)
  8. # define CMAKE_FIND_CAFILE
  9. #endif
  10. #include "cmStringAlgorithms.h"
  11. #include "cmSystemTools.h"
  12. #if defined(_WIN32)
  13. # include <vector>
  14. # include <windows.h>
  15. # include "cmsys/Encoding.hxx"
  16. #endif
  17. // curl versions before 7.21.5 did not provide this error code
  18. #if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM < 0x071505
  19. # define CURLE_NOT_BUILT_IN 4
  20. #endif
  21. #define check_curl_result(result, errstr) \
  22. do { \
  23. if ((result) != CURLE_OK && (result) != CURLE_NOT_BUILT_IN) { \
  24. e += e.empty() ? "" : "\n"; \
  25. e += (errstr); \
  26. e += ::curl_easy_strerror(result); \
  27. } \
  28. } while (false)
  29. // curl versions before 7.52.0 did not provide TLS 1.3 support
  30. #if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM < 0x073400
  31. # define CURL_SSLVERSION_TLSv1_3 CURL_SSLVERSION_LAST
  32. #endif
  33. // curl versions before 7.64.1 referred to Secure Transport as DarwinSSL
  34. #if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM < 0x074001
  35. # define CURLSSLBACKEND_SECURETRANSPORT CURLSSLBACKEND_DARWINSSL
  36. #endif
  37. // Make sure we keep up with new TLS versions supported by curl.
  38. // Do this only for our vendored curl to avoid breaking builds
  39. // against external future versions of curl.
  40. #if !defined(CMAKE_USE_SYSTEM_CURL)
  41. static_assert(CURL_SSLVERSION_LAST == 8,
  42. "A new CURL_SSLVERSION_ may be available!");
  43. #endif
  44. void cmCurlInitOnce()
  45. {
  46. // curl 7.56.0 introduced curl_global_sslset.
  47. #if defined(__APPLE__) && defined(CMAKE_USE_SYSTEM_CURL) && \
  48. defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM >= 0x073800
  49. static bool initialized = false;
  50. if (initialized) {
  51. return;
  52. }
  53. initialized = true;
  54. cm::optional<std::string> curl_ssl_backend =
  55. cmSystemTools::GetEnvVar("CURL_SSL_BACKEND");
  56. if (!curl_ssl_backend || curl_ssl_backend->empty()) {
  57. curl_version_info_data* cv = curl_version_info(CURLVERSION_FIRST);
  58. // curl 8.3.0 through 8.5.x did not re-initialize LibreSSL correctly,
  59. // so prefer the Secure Transport backend by default in those versions.
  60. if (cv->version_num >= 0x080300 && cv->version_num < 0x080600) {
  61. curl_global_sslset(CURLSSLBACKEND_SECURETRANSPORT, NULL, NULL);
  62. }
  63. }
  64. #endif
  65. }
  66. cm::optional<int> cmCurlParseTLSVersion(cm::string_view tls_version)
  67. {
  68. cm::optional<int> v;
  69. if (tls_version == "1.0"_s) {
  70. v = CURL_SSLVERSION_TLSv1_0;
  71. } else if (tls_version == "1.1"_s) {
  72. v = CURL_SSLVERSION_TLSv1_1;
  73. } else if (tls_version == "1.2"_s) {
  74. v = CURL_SSLVERSION_TLSv1_2;
  75. } else if (tls_version == "1.3"_s) {
  76. v = CURL_SSLVERSION_TLSv1_3;
  77. }
  78. return v;
  79. }
  80. cm::optional<std::string> cmCurlPrintTLSVersion(int curl_tls_version)
  81. {
  82. cm::optional<std::string> s;
  83. switch (curl_tls_version) {
  84. case CURL_SSLVERSION_TLSv1_0:
  85. s = "CURL_SSLVERSION_TLSv1_0"_s;
  86. break;
  87. case CURL_SSLVERSION_TLSv1_1:
  88. s = "CURL_SSLVERSION_TLSv1_1"_s;
  89. break;
  90. case CURL_SSLVERSION_TLSv1_2:
  91. s = "CURL_SSLVERSION_TLSv1_2"_s;
  92. break;
  93. case CURL_SSLVERSION_TLSv1_3:
  94. s = "CURL_SSLVERSION_TLSv1_3"_s;
  95. break;
  96. }
  97. return s;
  98. }
  99. std::string cmCurlSetCAInfo(::CURL* curl, const std::string& cafile)
  100. {
  101. std::string e;
  102. std::string env_ca;
  103. if (!cafile.empty()) {
  104. ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, cafile.c_str());
  105. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  106. }
  107. /* Honor the user-configurable OpenSSL environment variables. */
  108. else if (cmSystemTools::GetEnv("SSL_CERT_FILE", env_ca) &&
  109. cmSystemTools::FileExists(env_ca, true)) {
  110. ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, env_ca.c_str());
  111. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  112. } else if (cmSystemTools::GetEnv("SSL_CERT_DIR", env_ca) &&
  113. cmSystemTools::FileIsDirectory(env_ca)) {
  114. ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAPATH, env_ca.c_str());
  115. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  116. }
  117. #ifdef CMAKE_FIND_CAFILE
  118. # define CMAKE_CAFILE_FEDORA "/etc/pki/tls/certs/ca-bundle.crt"
  119. else if (cmSystemTools::FileExists(CMAKE_CAFILE_FEDORA, true)) {
  120. ::CURLcode res =
  121. ::curl_easy_setopt(curl, CURLOPT_CAINFO, CMAKE_CAFILE_FEDORA);
  122. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  123. }
  124. # undef CMAKE_CAFILE_FEDORA
  125. else {
  126. # define CMAKE_CAFILE_COMMON "/etc/ssl/certs/ca-certificates.crt"
  127. if (cmSystemTools::FileExists(CMAKE_CAFILE_COMMON, true)) {
  128. ::CURLcode res =
  129. ::curl_easy_setopt(curl, CURLOPT_CAINFO, CMAKE_CAFILE_COMMON);
  130. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  131. }
  132. # undef CMAKE_CAFILE_COMMON
  133. # define CMAKE_CAPATH_COMMON "/etc/ssl/certs"
  134. if (cmSystemTools::FileIsDirectory(CMAKE_CAPATH_COMMON)) {
  135. ::CURLcode res =
  136. ::curl_easy_setopt(curl, CURLOPT_CAPATH, CMAKE_CAPATH_COMMON);
  137. check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: ");
  138. }
  139. # undef CMAKE_CAPATH_COMMON
  140. }
  141. #endif
  142. return e;
  143. }
  144. std::string cmCurlSetNETRCOption(::CURL* curl, const std::string& netrc_level,
  145. const std::string& netrc_file)
  146. {
  147. std::string e;
  148. CURL_NETRC_OPTION curl_netrc_level = CURL_NETRC_LAST;
  149. ::CURLcode res;
  150. if (!netrc_level.empty()) {
  151. if (netrc_level == "OPTIONAL") {
  152. curl_netrc_level = CURL_NETRC_OPTIONAL;
  153. } else if (netrc_level == "REQUIRED") {
  154. curl_netrc_level = CURL_NETRC_REQUIRED;
  155. } else if (netrc_level == "IGNORED") {
  156. curl_netrc_level = CURL_NETRC_IGNORED;
  157. } else {
  158. e = cmStrCat("NETRC accepts OPTIONAL, IGNORED or REQUIRED but got: ",
  159. netrc_level);
  160. return e;
  161. }
  162. }
  163. if (curl_netrc_level != CURL_NETRC_LAST &&
  164. curl_netrc_level != CURL_NETRC_IGNORED) {
  165. res = ::curl_easy_setopt(curl, CURLOPT_NETRC, curl_netrc_level);
  166. check_curl_result(res, "Unable to set netrc level: ");
  167. if (!e.empty()) {
  168. return e;
  169. }
  170. // check to see if a .netrc file has been specified
  171. if (!netrc_file.empty()) {
  172. res = ::curl_easy_setopt(curl, CURLOPT_NETRC_FILE, netrc_file.c_str());
  173. check_curl_result(res, "Unable to set .netrc file path : ");
  174. }
  175. }
  176. return e;
  177. }
  178. std::string cmCurlFixFileURL(std::string url)
  179. {
  180. if (!cmHasLiteralPrefix(url, "file://")) {
  181. return url;
  182. }
  183. // libcurl 7.77 and below accidentally allowed spaces in URLs in some cases.
  184. // One such case was file:// URLs, which CMake has long accepted as a result.
  185. // Explicitly encode spaces for a URL.
  186. cmSystemTools::ReplaceString(url, " ", "%20");
  187. #if defined(_WIN32)
  188. // libcurl doesn't support file:// urls for unicode filenames on Windows.
  189. // Convert string from UTF-8 to ACP if this is a file:// URL.
  190. std::wstring wurl = cmsys::Encoding::ToWide(url);
  191. if (!wurl.empty()) {
  192. int mblen = WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, nullptr, 0,
  193. nullptr, nullptr);
  194. if (mblen > 0) {
  195. std::vector<char> chars(mblen);
  196. mblen = WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, &chars[0],
  197. mblen, nullptr, nullptr);
  198. if (mblen > 0) {
  199. url = &chars[0];
  200. }
  201. }
  202. }
  203. #endif
  204. return url;
  205. }
  206. ::CURL* cm_curl_easy_init()
  207. {
  208. ::CURL* curl = curl_easy_init();
  209. if (curl_version_info_data* cv = curl_version_info(CURLVERSION_FIRST)) {
  210. // curl 8.7.x returns incorrect HTTP/2 error codes.
  211. if (cv->version_num >= 0x080700 && cv->version_num < 0x080800) {
  212. curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  213. }
  214. }
  215. return curl;
  216. }