Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmCurl.cxx
Line
Count
Source
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
5
#include <utility>
6
7
#include <cm/string_view>
8
#include <cmext/string_view>
9
10
#if !defined(CMAKE_USE_SYSTEM_CURL) && !defined(_WIN32) &&                    \
11
  !defined(__APPLE__) && !defined(CURL_CA_BUNDLE) && !defined(CURL_CA_PATH)
12
#  define CMAKE_FIND_CAFILE
13
#endif
14
#include "cmStringAlgorithms.h"
15
#include "cmSystemTools.h"
16
17
#if defined(_WIN32)
18
#  include <vector>
19
20
#  include <windows.h>
21
22
#  include "cmsys/Encoding.hxx"
23
#endif
24
25
// curl versions before 7.21.5 did not provide this error code
26
#if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM < 0x071505
27
#  define CURLE_NOT_BUILT_IN 4
28
#endif
29
30
#define check_curl_result(result, errstr)                                     \
31
0
  do {                                                                        \
32
0
    if ((result) != CURLE_OK && (result) != CURLE_NOT_BUILT_IN) {             \
33
0
      bool isNeedNewline = !e.empty();                                        \
34
0
      e = cmStrCat(std::move(e), isNeedNewline ? "\n"_s : ""_s, (errstr),     \
35
0
                   ::curl_easy_strerror(result));                             \
36
0
    }                                                                         \
37
0
  } while (false)
38
39
// curl versions before 7.52.0 did not provide TLS 1.3 support
40
#if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM < 0x073400
41
#  define CURL_SSLVERSION_TLSv1_3 CURL_SSLVERSION_LAST
42
#endif
43
44
// curl versions before 7.64.1 referred to Secure Transport as DarwinSSL
45
#if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM < 0x074001
46
#  define CURLSSLBACKEND_SECURETRANSPORT CURLSSLBACKEND_DARWINSSL
47
#endif
48
49
// Make sure we keep up with new TLS versions supported by curl.
50
// Do this only for our vendored curl to avoid breaking builds
51
// against external future versions of curl.
52
#if !defined(CMAKE_USE_SYSTEM_CURL)
53
// NOLINTNEXTLINE(misc-redundant-expression)
54
static_assert(CURL_SSLVERSION_LAST == 8,
55
              "A new CURL_SSLVERSION_ may be available!");
56
#endif
57
58
::CURLcode cm_curl_global_init(long flags)
59
0
{
60
  // curl 7.56.0 introduced curl_global_sslset.
61
#if defined(__APPLE__) && defined(CMAKE_USE_SYSTEM_CURL) &&                   \
62
  defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM >= 0x073800
63
  cm::optional<std::string> curl_ssl_backend =
64
    cmSystemTools::GetEnvVar("CURL_SSL_BACKEND");
65
  if (!curl_ssl_backend || curl_ssl_backend->empty()) {
66
    curl_version_info_data* cv = curl_version_info(CURLVERSION_FIRST);
67
    // curl 8.3.0 through 8.5.x did not re-initialize LibreSSL correctly,
68
    // so prefer the Secure Transport backend by default in those versions.
69
    if (cv->version_num >= 0x080300 && cv->version_num < 0x080600) {
70
#  if defined(__clang__)
71
#    define CM_CURL_DEPRECATED_CURLSSLBACKEND_SECURETRANSPORT_CLANG
72
#    pragma clang diagnostic push
73
#    pragma clang diagnostic ignored "-Wdeprecated-declarations"
74
#  elif defined(__GNUC__)
75
#    define CM_CURL_DEPRECATED_CURLSSLBACKEND_SECURETRANSPORT_GCC
76
#    pragma GCC diagnostic push
77
#    pragma GCC diagnostic ignored "-Wdeprecated-declarations"
78
#  endif
79
      curl_global_sslset(CURLSSLBACKEND_SECURETRANSPORT, NULL, NULL);
80
#  if defined(CM_CURL_DEPRECATED_CURLSSLBACKEND_SECURETRANSPORT_CLANG)
81
#    undef CM_CURL_DEPRECATED_CURLSSLBACKEND_SECURETRANSPORT_CLANG
82
#    pragma clang diagnostic pop
83
#  elif defined(CM_CURL_DEPRECATED_CURLSSLBACKEND_SECURETRANSPORT_GCC)
84
#    undef CM_CURL_DEPRECATED_CURLSSLBACKEND_SECURETRANSPORT_GCC
85
#    pragma GCC diagnostic pop
86
#  endif
87
    }
88
  }
89
#endif
90
0
  return ::curl_global_init(flags);
91
0
}
92
93
cm::optional<int> cmCurlParseTLSVersion(cm::string_view tls_version)
94
0
{
95
0
  cm::optional<int> v;
96
0
  if (tls_version == "1.0"_s) {
97
0
    v = CURL_SSLVERSION_TLSv1_0;
98
0
  } else if (tls_version == "1.1"_s) {
99
0
    v = CURL_SSLVERSION_TLSv1_1;
100
0
  } else if (tls_version == "1.2"_s) {
101
0
    v = CURL_SSLVERSION_TLSv1_2;
102
0
  } else if (tls_version == "1.3"_s) {
103
0
    v = CURL_SSLVERSION_TLSv1_3;
104
0
  }
105
0
  return v;
106
0
}
107
108
cm::optional<std::string> cmCurlPrintTLSVersion(int curl_tls_version)
109
0
{
110
0
  cm::optional<std::string> s;
111
0
  switch (curl_tls_version) {
112
0
    case CURL_SSLVERSION_TLSv1_0:
113
0
      s = "CURL_SSLVERSION_TLSv1_0"_s;
114
0
      break;
115
0
    case CURL_SSLVERSION_TLSv1_1:
116
0
      s = "CURL_SSLVERSION_TLSv1_1"_s;
117
0
      break;
118
0
    case CURL_SSLVERSION_TLSv1_2:
119
0
      s = "CURL_SSLVERSION_TLSv1_2"_s;
120
0
      break;
121
0
    case CURL_SSLVERSION_TLSv1_3:
122
0
      s = "CURL_SSLVERSION_TLSv1_3"_s;
123
0
      break;
124
0
  }
125
0
  return s;
126
0
}
127
128
std::string cmCurlSetCAInfo(::CURL* curl, std::string const& cafile)
129
0
{
130
0
  std::string e;
131
0
  std::string env_ca;
132
0
  if (!cafile.empty()) {
133
0
    ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, cafile.c_str());
134
0
    check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
135
0
  }
136
  /* Honor the user-configurable OpenSSL environment variables. */
137
0
  else if (cmSystemTools::GetEnv("SSL_CERT_FILE", env_ca) &&
138
0
           cmSystemTools::FileExists(env_ca, true)) {
139
0
    ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, env_ca.c_str());
140
0
    check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
141
0
  } else if (cmSystemTools::GetEnv("SSL_CERT_DIR", env_ca) &&
142
0
             cmSystemTools::FileIsDirectory(env_ca)) {
143
0
    ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAPATH, env_ca.c_str());
144
0
    check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
145
0
  }
146
0
#ifdef CMAKE_FIND_CAFILE
147
0
#  define CMAKE_CAFILE_FEDORA "/etc/pki/tls/certs/ca-bundle.crt"
148
0
  else if (cmSystemTools::FileExists(CMAKE_CAFILE_FEDORA, true)) {
149
0
    ::CURLcode res =
150
0
      ::curl_easy_setopt(curl, CURLOPT_CAINFO, CMAKE_CAFILE_FEDORA);
151
0
    check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
152
0
  }
153
0
#  undef CMAKE_CAFILE_FEDORA
154
0
  else {
155
0
#  define CMAKE_CAFILE_COMMON "/etc/ssl/certs/ca-certificates.crt"
156
0
    if (cmSystemTools::FileExists(CMAKE_CAFILE_COMMON, true)) {
157
0
      ::CURLcode res =
158
0
        ::curl_easy_setopt(curl, CURLOPT_CAINFO, CMAKE_CAFILE_COMMON);
159
0
      check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
160
0
    }
161
0
#  undef CMAKE_CAFILE_COMMON
162
0
#  define CMAKE_CAPATH_COMMON "/etc/ssl/certs"
163
0
    if (cmSystemTools::FileIsDirectory(CMAKE_CAPATH_COMMON)) {
164
0
      ::CURLcode res =
165
0
        ::curl_easy_setopt(curl, CURLOPT_CAPATH, CMAKE_CAPATH_COMMON);
166
0
      check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: ");
167
0
    }
168
0
#  undef CMAKE_CAPATH_COMMON
169
#  ifdef _AIX
170
#    define CMAKE_CAPATH_AIX "/var/ssl/certs"
171
    if (cmSystemTools::FileIsDirectory(CMAKE_CAPATH_AIX)) {
172
      ::CURLcode res =
173
        ::curl_easy_setopt(curl, CURLOPT_CAPATH, CMAKE_CAPATH_AIX);
174
      check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: ");
175
    }
176
#    undef CMAKE_CAPATH_AIX
177
#  endif
178
#  ifdef __sun
179
#    define CMAKE_CAPATH_SUNOS_CSW "/etc/opt/csw/ssl/certs"
180
    if (cmSystemTools::FileIsDirectory(CMAKE_CAPATH_SUNOS_CSW)) {
181
      ::CURLcode res =
182
        ::curl_easy_setopt(curl, CURLOPT_CAPATH, CMAKE_CAPATH_SUNOS_CSW);
183
      check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: ");
184
    }
185
#    undef CMAKE_CAPATH_SUNOS_CSW
186
#  endif
187
0
  }
188
0
#endif
189
0
  return e;
190
0
}
191
192
std::string cmCurlSetNETRCOption(::CURL* curl, std::string const& netrc_level,
193
                                 std::string const& netrc_file)
194
0
{
195
0
  std::string e;
196
0
  long curl_netrc_level = CURL_NETRC_LAST;
197
0
  ::CURLcode res;
198
199
0
  if (!netrc_level.empty()) {
200
0
    if (netrc_level == "OPTIONAL") {
201
0
      curl_netrc_level = CURL_NETRC_OPTIONAL;
202
0
    } else if (netrc_level == "REQUIRED") {
203
0
      curl_netrc_level = CURL_NETRC_REQUIRED;
204
0
    } else if (netrc_level == "IGNORED") {
205
0
      curl_netrc_level = CURL_NETRC_IGNORED;
206
0
    } else {
207
0
      e = cmStrCat("NETRC accepts OPTIONAL, IGNORED or REQUIRED but got: ",
208
0
                   netrc_level);
209
0
      return e;
210
0
    }
211
0
  }
212
213
0
  if (curl_netrc_level != CURL_NETRC_LAST &&
214
0
      curl_netrc_level != CURL_NETRC_IGNORED) {
215
0
    res = ::curl_easy_setopt(curl, CURLOPT_NETRC, curl_netrc_level);
216
0
    check_curl_result(res, "Unable to set netrc level: ");
217
0
    if (!e.empty()) {
218
0
      return e;
219
0
    }
220
221
    // check to see if a .netrc file has been specified
222
0
    if (!netrc_file.empty()) {
223
0
      res = ::curl_easy_setopt(curl, CURLOPT_NETRC_FILE, netrc_file.c_str());
224
0
      check_curl_result(res, "Unable to set .netrc file path : ");
225
0
    }
226
0
  }
227
0
  return e;
228
0
}
229
230
::CURL* cm_curl_easy_init()
231
0
{
232
0
  ::CURL* curl = curl_easy_init();
233
0
  if (curl_version_info_data* cv = curl_version_info(CURLVERSION_FIRST)) {
234
    // curl 8.7.x returns incorrect HTTP/2 error codes.
235
0
    if (cv->version_num >= 0x080700 && cv->version_num < 0x080800) {
236
      curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
237
0
    }
238
0
  }
239
0
  return curl;
240
0
}