cmCurl.cxx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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. // Make sure we keep up with new TLS versions supported by curl.
  34. // Do this only for our vendored curl to avoid breaking builds
  35. // against external future versions of curl.
  36. #if !defined(CMAKE_USE_SYSTEM_CURL)
  37. static_assert(CURL_SSLVERSION_LAST == 8,
  38. "A new CURL_SSLVERSION_ may be available!");
  39. #endif
  40. cm::optional<int> cmCurlParseTLSVersion(cm::string_view tls_version)
  41. {
  42. cm::optional<int> v;
  43. if (tls_version == "1.0"_s) {
  44. v = CURL_SSLVERSION_TLSv1_0;
  45. } else if (tls_version == "1.1"_s) {
  46. v = CURL_SSLVERSION_TLSv1_1;
  47. } else if (tls_version == "1.2"_s) {
  48. v = CURL_SSLVERSION_TLSv1_2;
  49. } else if (tls_version == "1.3"_s) {
  50. v = CURL_SSLVERSION_TLSv1_3;
  51. }
  52. return v;
  53. }
  54. cm::optional<std::string> cmCurlPrintTLSVersion(int curl_tls_version)
  55. {
  56. cm::optional<std::string> s;
  57. switch (curl_tls_version) {
  58. case CURL_SSLVERSION_TLSv1_0:
  59. s = "CURL_SSLVERSION_TLSv1_0"_s;
  60. break;
  61. case CURL_SSLVERSION_TLSv1_1:
  62. s = "CURL_SSLVERSION_TLSv1_1"_s;
  63. break;
  64. case CURL_SSLVERSION_TLSv1_2:
  65. s = "CURL_SSLVERSION_TLSv1_2"_s;
  66. break;
  67. case CURL_SSLVERSION_TLSv1_3:
  68. s = "CURL_SSLVERSION_TLSv1_3"_s;
  69. break;
  70. }
  71. return s;
  72. }
  73. std::string cmCurlSetCAInfo(::CURL* curl, const std::string& cafile)
  74. {
  75. std::string e;
  76. std::string env_ca;
  77. if (!cafile.empty()) {
  78. ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, cafile.c_str());
  79. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  80. }
  81. /* Honor the user-configurable OpenSSL environment variables. */
  82. else if (cmSystemTools::GetEnv("SSL_CERT_FILE", env_ca) &&
  83. cmSystemTools::FileExists(env_ca, true)) {
  84. ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, env_ca.c_str());
  85. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  86. } else if (cmSystemTools::GetEnv("SSL_CERT_DIR", env_ca) &&
  87. cmSystemTools::FileIsDirectory(env_ca)) {
  88. ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAPATH, env_ca.c_str());
  89. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  90. }
  91. #ifdef CMAKE_FIND_CAFILE
  92. # define CMAKE_CAFILE_FEDORA "/etc/pki/tls/certs/ca-bundle.crt"
  93. else if (cmSystemTools::FileExists(CMAKE_CAFILE_FEDORA, true)) {
  94. ::CURLcode res =
  95. ::curl_easy_setopt(curl, CURLOPT_CAINFO, CMAKE_CAFILE_FEDORA);
  96. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  97. }
  98. # undef CMAKE_CAFILE_FEDORA
  99. else {
  100. # define CMAKE_CAFILE_COMMON "/etc/ssl/certs/ca-certificates.crt"
  101. if (cmSystemTools::FileExists(CMAKE_CAFILE_COMMON, true)) {
  102. ::CURLcode res =
  103. ::curl_easy_setopt(curl, CURLOPT_CAINFO, CMAKE_CAFILE_COMMON);
  104. check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
  105. }
  106. # undef CMAKE_CAFILE_COMMON
  107. # define CMAKE_CAPATH_COMMON "/etc/ssl/certs"
  108. if (cmSystemTools::FileIsDirectory(CMAKE_CAPATH_COMMON)) {
  109. ::CURLcode res =
  110. ::curl_easy_setopt(curl, CURLOPT_CAPATH, CMAKE_CAPATH_COMMON);
  111. check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: ");
  112. }
  113. # undef CMAKE_CAPATH_COMMON
  114. }
  115. #endif
  116. return e;
  117. }
  118. std::string cmCurlSetNETRCOption(::CURL* curl, const std::string& netrc_level,
  119. const std::string& netrc_file)
  120. {
  121. std::string e;
  122. CURL_NETRC_OPTION curl_netrc_level = CURL_NETRC_LAST;
  123. ::CURLcode res;
  124. if (!netrc_level.empty()) {
  125. if (netrc_level == "OPTIONAL") {
  126. curl_netrc_level = CURL_NETRC_OPTIONAL;
  127. } else if (netrc_level == "REQUIRED") {
  128. curl_netrc_level = CURL_NETRC_REQUIRED;
  129. } else if (netrc_level == "IGNORED") {
  130. curl_netrc_level = CURL_NETRC_IGNORED;
  131. } else {
  132. e = cmStrCat("NETRC accepts OPTIONAL, IGNORED or REQUIRED but got: ",
  133. netrc_level);
  134. return e;
  135. }
  136. }
  137. if (curl_netrc_level != CURL_NETRC_LAST &&
  138. curl_netrc_level != CURL_NETRC_IGNORED) {
  139. res = ::curl_easy_setopt(curl, CURLOPT_NETRC, curl_netrc_level);
  140. check_curl_result(res, "Unable to set netrc level: ");
  141. if (!e.empty()) {
  142. return e;
  143. }
  144. // check to see if a .netrc file has been specified
  145. if (!netrc_file.empty()) {
  146. res = ::curl_easy_setopt(curl, CURLOPT_NETRC_FILE, netrc_file.c_str());
  147. check_curl_result(res, "Unable to set .netrc file path : ");
  148. }
  149. }
  150. return e;
  151. }
  152. std::string cmCurlFixFileURL(std::string url)
  153. {
  154. if (!cmHasLiteralPrefix(url, "file://")) {
  155. return url;
  156. }
  157. // libcurl 7.77 and below accidentally allowed spaces in URLs in some cases.
  158. // One such case was file:// URLs, which CMake has long accepted as a result.
  159. // Explicitly encode spaces for a URL.
  160. cmSystemTools::ReplaceString(url, " ", "%20");
  161. #if defined(_WIN32)
  162. // libcurl doesn't support file:// urls for unicode filenames on Windows.
  163. // Convert string from UTF-8 to ACP if this is a file:// URL.
  164. std::wstring wurl = cmsys::Encoding::ToWide(url);
  165. if (!wurl.empty()) {
  166. int mblen = WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, nullptr, 0,
  167. nullptr, nullptr);
  168. if (mblen > 0) {
  169. std::vector<char> chars(mblen);
  170. mblen = WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, &chars[0],
  171. mblen, nullptr, nullptr);
  172. if (mblen > 0) {
  173. url = &chars[0];
  174. }
  175. }
  176. }
  177. #endif
  178. return url;
  179. }