Coverage Report

Created: 2026-10-28 06:38

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
  curl_version_info_data* curlVersion = curl_version_info(CURLVERSION_NOW);
132
0
  if (!curlVersion || curlVersion->ssl_version == nullptr) {
133
0
    return e;
134
0
  }
135
0
  std::string env_ca;
136
0
  if (!cafile.empty()) {
137
0
    ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, cafile.c_str());
138
0
    check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
139
0
  }
140
  /* Honor the user-configurable OpenSSL environment variables. */
141
0
  else if (cmSystemTools::GetEnv("SSL_CERT_FILE", env_ca) &&
142
0
           cmSystemTools::FileExists(env_ca, true)) {
143
0
    ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, env_ca.c_str());
144
0
    check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
145
0
  } else if (cmSystemTools::GetEnv("SSL_CERT_DIR", env_ca) &&
146
0
             cmSystemTools::FileIsDirectory(env_ca)) {
147
0
    ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAPATH, env_ca.c_str());
148
0
    check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
149
0
  }
150
0
#ifdef CMAKE_FIND_CAFILE
151
0
#  define CMAKE_CAFILE_FEDORA "/etc/pki/tls/certs/ca-bundle.crt"
152
0
  else if (cmSystemTools::FileExists(CMAKE_CAFILE_FEDORA, true)) {
153
0
    ::CURLcode res =
154
0
      ::curl_easy_setopt(curl, CURLOPT_CAINFO, CMAKE_CAFILE_FEDORA);
155
0
    check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
156
0
  }
157
0
#  undef CMAKE_CAFILE_FEDORA
158
0
  else {
159
0
#  define CMAKE_CAFILE_COMMON "/etc/ssl/certs/ca-certificates.crt"
160
0
    if (cmSystemTools::FileExists(CMAKE_CAFILE_COMMON, true)) {
161
0
      ::CURLcode res =
162
0
        ::curl_easy_setopt(curl, CURLOPT_CAINFO, CMAKE_CAFILE_COMMON);
163
0
      check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
164
0
    }
165
0
#  undef CMAKE_CAFILE_COMMON
166
0
#  define CMAKE_CAPATH_COMMON "/etc/ssl/certs"
167
0
    if (cmSystemTools::FileIsDirectory(CMAKE_CAPATH_COMMON)) {
168
0
      ::CURLcode res =
169
0
        ::curl_easy_setopt(curl, CURLOPT_CAPATH, CMAKE_CAPATH_COMMON);
170
0
      check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: ");
171
0
    }
172
0
#  undef CMAKE_CAPATH_COMMON
173
#  ifdef _AIX
174
#    define CMAKE_CAPATH_AIX "/var/ssl/certs"
175
    if (cmSystemTools::FileIsDirectory(CMAKE_CAPATH_AIX)) {
176
      ::CURLcode res =
177
        ::curl_easy_setopt(curl, CURLOPT_CAPATH, CMAKE_CAPATH_AIX);
178
      check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: ");
179
    }
180
#    undef CMAKE_CAPATH_AIX
181
#  endif
182
#  ifdef __sun
183
#    define CMAKE_CAPATH_SUNOS_CSW "/etc/opt/csw/ssl/certs"
184
    if (cmSystemTools::FileIsDirectory(CMAKE_CAPATH_SUNOS_CSW)) {
185
      ::CURLcode res =
186
        ::curl_easy_setopt(curl, CURLOPT_CAPATH, CMAKE_CAPATH_SUNOS_CSW);
187
      check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: ");
188
    }
189
#    undef CMAKE_CAPATH_SUNOS_CSW
190
#  endif
191
0
  }
192
0
#endif
193
0
  return e;
194
0
}
195
196
std::string cmCurlSetNETRCOption(::CURL* curl, std::string const& netrc_level,
197
                                 std::string const& netrc_file)
198
0
{
199
0
  std::string e;
200
0
  long curl_netrc_level = CURL_NETRC_LAST;
201
0
  ::CURLcode res;
202
203
0
  if (!netrc_level.empty()) {
204
0
    if (netrc_level == "OPTIONAL") {
205
0
      curl_netrc_level = CURL_NETRC_OPTIONAL;
206
0
    } else if (netrc_level == "REQUIRED") {
207
0
      curl_netrc_level = CURL_NETRC_REQUIRED;
208
0
    } else if (netrc_level == "IGNORED") {
209
0
      curl_netrc_level = CURL_NETRC_IGNORED;
210
0
    } else {
211
0
      e = cmStrCat("NETRC accepts OPTIONAL, IGNORED or REQUIRED but got: ",
212
0
                   netrc_level);
213
0
      return e;
214
0
    }
215
0
  }
216
217
0
  if (curl_netrc_level != CURL_NETRC_LAST &&
218
0
      curl_netrc_level != CURL_NETRC_IGNORED) {
219
0
    res = ::curl_easy_setopt(curl, CURLOPT_NETRC, curl_netrc_level);
220
0
    check_curl_result(res, "Unable to set netrc level: ");
221
0
    if (!e.empty()) {
222
0
      return e;
223
0
    }
224
225
    // check to see if a .netrc file has been specified
226
0
    if (!netrc_file.empty()) {
227
0
      res = ::curl_easy_setopt(curl, CURLOPT_NETRC_FILE, netrc_file.c_str());
228
0
      check_curl_result(res, "Unable to set .netrc file path : ");
229
0
    }
230
0
  }
231
0
  return e;
232
0
}
233
234
::CURL* cm_curl_easy_init()
235
0
{
236
0
  ::CURL* curl = curl_easy_init();
237
0
  if (curl_version_info_data* cv = curl_version_info(CURLVERSION_FIRST)) {
238
    // curl 8.7.x returns incorrect HTTP/2 error codes.
239
0
    if (cv->version_num >= 0x080700 && cv->version_num < 0x080800) {
240
      curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
241
0
    }
242
0
  }
243
0
  return curl;
244
0
}