cmCurl.cxx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file LICENSE.rst 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. // NOLINTNEXTLINE(misc-redundant-expression)
  42. static_assert(CURL_SSLVERSION_LAST == 8,
  43. "A new CURL_SSLVERSION_ may be available!");
  44. #endif
  45. ::CURLcode cm_curl_global_init(long flags)
  46. {
  47. // curl 7.56.0 introduced curl_global_sslset.
  48. #if defined(__APPLE__) && defined(CMAKE_USE_SYSTEM_CURL) && \
  49. defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM >= 0x073800
  50. cm::optional<std::string> curl_ssl_backend =
  51. cmSystemTools::GetEnvVar("CURL_SSL_BACKEND");
  52. if (!curl_ssl_backend || curl_ssl_backend->empty()) {
  53. curl_version_info_data* cv = curl_version_info(CURLVERSION_FIRST);
  54. // curl 8.3.0 through 8.5.x did not re-initialize LibreSSL correctly,
  55. // so prefer the Secure Transport backend by default in those versions.
  56. if (cv->version_num >= 0x080300 && cv->version_num < 0x080600) {
  57. curl_global_sslset(CURLSSLBACKEND_SECURETRANSPORT, NULL, NULL);
  58. }
  59. }
  60. #endif
  61. return ::curl_global_init(flags);
  62. }
  63. cm::optional<int> cmCurlParseTLSVersion(cm::string_view tls_version)
  64. {
  65. cm::optional<int> v;
  66. if (tls_version == "1.0"_s) {
  67. v = CURL_SSLVERSION_TLSv1_0;
  68. } else if (tls_version == "1.1"_s) {
  69. v = CURL_SSLVERSION_TLSv1_1;
  70. } else if (tls_version == "1.2"_s) {
  71. v = CURL_SSLVERSION_TLSv1_2;
  72. } else if (tls_version == "1.3"_s) {
  73. v = CURL_SSLVERSION_TLSv1_3;
  74. }
  75. return v;
  76. }
  77. cm::optional<std::string> cmCurlPrintTLSVersion(int curl_tls_version)
  78. {
  79. cm::optional<std::string> s;
  80. switch (curl_tls_version) {
  81. case CURL_SSLVERSION_TLSv1_0:
  82. s = "CURL_SSLVERSION_TLSv1_0"_s;
  83. break;
  84. case CURL_SSLVERSION_TLSv1_1:
  85. s = "CURL_SSLVERSION_TLSv1_1"_s;
  86. break;
  87. case CURL_SSLVERSION_TLSv1_2:
  88. s = "CURL_SSLVERSION_TLSv1_2"_s;
  89. break;
  90. case CURL_SSLVERSION_TLSv1_3:
  91. s = "CURL_SSLVERSION_TLSv1_3"_s;
  92. break;
  93. }
  94. return s;
  95. }
  96. std::string cmCurlSetCAInfo(::CURL* curl, std::string const& cafile)
  97. {
  98. std::string e;
  99. std::string env_ca;
  100. if (!cafile.empty()) {
  101. ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, cafile.c_str());
  102. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  103. }
  104. /* Honor the user-configurable OpenSSL environment variables. */
  105. else if (cmSystemTools::GetEnv("SSL_CERT_FILE", env_ca) &&
  106. cmSystemTools::FileExists(env_ca, true)) {
  107. ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, env_ca.c_str());
  108. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  109. } else if (cmSystemTools::GetEnv("SSL_CERT_DIR", env_ca) &&
  110. cmSystemTools::FileIsDirectory(env_ca)) {
  111. ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAPATH, env_ca.c_str());
  112. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  113. }
  114. #ifdef CMAKE_FIND_CAFILE
  115. # define CMAKE_CAFILE_FEDORA "/etc/pki/tls/certs/ca-bundle.crt"
  116. else if (cmSystemTools::FileExists(CMAKE_CAFILE_FEDORA, true)) {
  117. ::CURLcode res =
  118. ::curl_easy_setopt(curl, CURLOPT_CAINFO, CMAKE_CAFILE_FEDORA);
  119. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  120. }
  121. # undef CMAKE_CAFILE_FEDORA
  122. else {
  123. # define CMAKE_CAFILE_COMMON "/etc/ssl/certs/ca-certificates.crt"
  124. if (cmSystemTools::FileExists(CMAKE_CAFILE_COMMON, true)) {
  125. ::CURLcode res =
  126. ::curl_easy_setopt(curl, CURLOPT_CAINFO, CMAKE_CAFILE_COMMON);
  127. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  128. }
  129. # undef CMAKE_CAFILE_COMMON
  130. # define CMAKE_CAPATH_COMMON "/etc/ssl/certs"
  131. if (cmSystemTools::FileIsDirectory(CMAKE_CAPATH_COMMON)) {
  132. ::CURLcode res =
  133. ::curl_easy_setopt(curl, CURLOPT_CAPATH, CMAKE_CAPATH_COMMON);
  134. check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: ");
  135. }
  136. # undef CMAKE_CAPATH_COMMON
  137. # ifdef _AIX
  138. # define CMAKE_CAPATH_AIX "/var/ssl/certs"
  139. if (cmSystemTools::FileIsDirectory(CMAKE_CAPATH_AIX)) {
  140. ::CURLcode res =
  141. ::curl_easy_setopt(curl, CURLOPT_CAPATH, CMAKE_CAPATH_AIX);
  142. check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: ");
  143. }
  144. # undef CMAKE_CAPATH_AIX
  145. # endif
  146. # ifdef __sun
  147. # define CMAKE_CAPATH_SUNOS_CSW "/etc/opt/csw/ssl/certs"
  148. if (cmSystemTools::FileIsDirectory(CMAKE_CAPATH_SUNOS_CSW)) {
  149. ::CURLcode res =
  150. ::curl_easy_setopt(curl, CURLOPT_CAPATH, CMAKE_CAPATH_SUNOS_CSW);
  151. check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: ");
  152. }
  153. # undef CMAKE_CAPATH_SUNOS_CSW
  154. # endif
  155. }
  156. #endif
  157. return e;
  158. }
  159. std::string cmCurlSetNETRCOption(::CURL* curl, std::string const& netrc_level,
  160. std::string const& netrc_file)
  161. {
  162. std::string e;
  163. long curl_netrc_level = CURL_NETRC_LAST;
  164. ::CURLcode res;
  165. if (!netrc_level.empty()) {
  166. if (netrc_level == "OPTIONAL") {
  167. curl_netrc_level = CURL_NETRC_OPTIONAL;
  168. } else if (netrc_level == "REQUIRED") {
  169. curl_netrc_level = CURL_NETRC_REQUIRED;
  170. } else if (netrc_level == "IGNORED") {
  171. curl_netrc_level = CURL_NETRC_IGNORED;
  172. } else {
  173. e = cmStrCat("NETRC accepts OPTIONAL, IGNORED or REQUIRED but got: ",
  174. netrc_level);
  175. return e;
  176. }
  177. }
  178. if (curl_netrc_level != CURL_NETRC_LAST &&
  179. curl_netrc_level != CURL_NETRC_IGNORED) {
  180. res = ::curl_easy_setopt(curl, CURLOPT_NETRC, curl_netrc_level);
  181. check_curl_result(res, "Unable to set netrc level: ");
  182. if (!e.empty()) {
  183. return e;
  184. }
  185. // check to see if a .netrc file has been specified
  186. if (!netrc_file.empty()) {
  187. res = ::curl_easy_setopt(curl, CURLOPT_NETRC_FILE, netrc_file.c_str());
  188. check_curl_result(res, "Unable to set .netrc file path : ");
  189. }
  190. }
  191. return e;
  192. }
  193. std::string cmCurlFixFileURL(std::string url)
  194. {
  195. if (!cmHasLiteralPrefix(url, "file://")) {
  196. return url;
  197. }
  198. // libcurl 7.77 and below accidentally allowed spaces in URLs in some cases.
  199. // One such case was file:// URLs, which CMake has long accepted as a result.
  200. // Explicitly encode spaces for a URL.
  201. cmSystemTools::ReplaceString(url, " ", "%20");
  202. #if defined(_WIN32)
  203. // libcurl doesn't support file:// urls for unicode filenames on Windows.
  204. // Convert string from UTF-8 to ACP if this is a file:// URL.
  205. std::wstring wurl = cmsys::Encoding::ToWide(url);
  206. if (!wurl.empty()) {
  207. int mblen = WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, nullptr, 0,
  208. nullptr, nullptr);
  209. if (mblen > 0) {
  210. std::vector<char> chars(mblen);
  211. mblen = WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, &chars[0],
  212. mblen, nullptr, nullptr);
  213. if (mblen > 0) {
  214. url = &chars[0];
  215. }
  216. }
  217. }
  218. #endif
  219. return url;
  220. }
  221. ::CURL* cm_curl_easy_init()
  222. {
  223. ::CURL* curl = curl_easy_init();
  224. if (curl_version_info_data* cv = curl_version_info(CURLVERSION_FIRST)) {
  225. // curl 8.7.x returns incorrect HTTP/2 error codes.
  226. if (cv->version_num >= 0x080700 && cv->version_num < 0x080800) {
  227. curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  228. }
  229. }
  230. return curl;
  231. }