Coverage Report

Created: 2026-07-16 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/vtls/vtls_config.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
25
/* This file is for implementing all "generic" SSL functions that all libcurl
26
   internals should use. It is then responsible for calling the proper
27
   "backend" function.
28
29
   SSL-functions in libcurl should call functions in this source file, and not
30
   to any specific SSL-layer.
31
32
   Curl_ssl_ - prefix for generic ones
33
34
   Note that this source code uses the functions of the configured SSL
35
   backend via the global Curl_ssl instance.
36
37
   "SSL/TLS Strong Encryption: An Introduction"
38
   https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html
39
*/
40
41
#include "curl_setup.h"
42
43
#ifdef HAVE_SYS_TYPES_H
44
#include <sys/types.h>
45
#endif
46
47
#include "urldata.h"
48
#include "setopt.h"
49
#include "strcase.h"
50
#include "vtls/vtls.h"
51
#include "vtls/vtls_config.h"
52
53
54
#define CLONE_STRING(var)                    \
55
0
  do {                                       \
56
0
    if(source->var) {                        \
57
0
      dest->var = curlx_strdup(source->var); \
58
0
      if(!dest->var)                         \
59
0
        return FALSE;                        \
60
0
    }                                        \
61
0
    else                                     \
62
0
      dest->var = NULL;                      \
63
0
  } while(0)
64
65
#define CLONE_BLOB(var)                  \
66
0
  do {                                   \
67
0
    if(blobdup(&dest->var, source->var)) \
68
0
      return FALSE;                      \
69
0
  } while(0)
70
71
static CURLcode blobdup(struct curl_blob **dest, struct curl_blob *src)
72
0
{
73
0
  DEBUGASSERT(dest);
74
0
  DEBUGASSERT(!*dest);
75
0
  if(src) {
76
    /* only if there is data to dupe! */
77
0
    struct curl_blob *d;
78
0
    d = curlx_malloc(sizeof(struct curl_blob) + src->len);
79
0
    if(!d)
80
0
      return CURLE_OUT_OF_MEMORY;
81
0
    d->len = src->len;
82
    /* Always duplicate because the connection may survive longer than the
83
       handle that passed in the blob. */
84
0
    d->flags = CURL_BLOB_COPY;
85
0
    d->data = (void *)((char *)d + sizeof(struct curl_blob));
86
0
    memcpy(d->data, src->data, src->len);
87
0
    *dest = d;
88
0
  }
89
0
  return CURLE_OK;
90
0
}
91
92
/* returns TRUE if the blobs are identical */
93
static bool blobcmp(struct curl_blob *first, struct curl_blob *second)
94
0
{
95
0
  if(!first && !second) /* both are NULL */
96
0
    return TRUE;
97
0
  if(!first || !second) /* one is NULL */
98
0
    return FALSE;
99
0
  if(first->len != second->len) /* different sizes */
100
0
    return FALSE;
101
0
  return !memcmp(first->data, second->data, first->len); /* same data */
102
0
}
103
104
void Curl_ssl_config_init(struct ssl_primary_config *sslc)
105
13.6k
{
106
  /*
107
   * libcurl 7.10 introduced SSL verification *by default*! This needs to be
108
   * switched off unless wanted.
109
   */
110
13.6k
  sslc->verifypeer = TRUE;
111
13.6k
  sslc->verifyhost = TRUE;
112
13.6k
  sslc->cache_session = TRUE; /* caching by default */
113
13.6k
}
114
115
void Curl_ssl_config_cleanup(struct ssl_primary_config *sslc)
116
13.6k
{
117
13.6k
  if(sslc->deep_copy) {
118
0
    curlx_safefree(sslc->CApath);
119
0
    curlx_safefree(sslc->CAfile);
120
0
    curlx_safefree(sslc->issuercert);
121
0
    curlx_safefree(sslc->clientcert);
122
0
    curlx_safefree(sslc->cipher_list);
123
0
    curlx_safefree(sslc->cipher_list13);
124
0
    curlx_safefree(sslc->pinned_key);
125
0
    curlx_safefree(sslc->cert_blob);
126
0
    curlx_safefree(sslc->ca_info_blob);
127
0
    curlx_safefree(sslc->issuercert_blob);
128
0
    curlx_safefree(sslc->key_blob);
129
0
    curlx_safefree(sslc->curves);
130
0
    curlx_safefree(sslc->signature_algorithms);
131
0
    curlx_safefree(sslc->CRLfile);
132
0
    curlx_safefree(sslc->cert_type);
133
0
    curlx_safefree(sslc->key);
134
0
    curlx_safefree(sslc->key_type);
135
0
    curlx_safefree(sslc->key_passwd);
136
0
    sslc->deep_copy = FALSE;
137
0
  }
138
13.6k
}
139
140
static bool match_ssl_primary_config(struct Curl_easy *data,
141
                                     struct ssl_primary_config *c1,
142
                                     struct ssl_primary_config *c2)
143
0
{
144
0
  (void)data;
145
0
  if((c1->version == c2->version) &&
146
0
     (c1->version_max == c2->version_max) &&
147
0
     (c1->ssl_options == c2->ssl_options) &&
148
0
     (c1->verifypeer == c2->verifypeer) &&
149
0
     (c1->verifyhost == c2->verifyhost) &&
150
0
     (c1->verifystatus == c2->verifystatus) &&
151
0
     blobcmp(c1->cert_blob, c2->cert_blob) &&
152
0
     blobcmp(c1->ca_info_blob, c2->ca_info_blob) &&
153
0
     blobcmp(c1->issuercert_blob, c2->issuercert_blob) &&
154
0
     blobcmp(c1->key_blob, c2->key_blob) &&
155
0
     Curl_safecmp(c1->CApath, c2->CApath) &&
156
0
     Curl_safecmp(c1->CAfile, c2->CAfile) &&
157
0
     Curl_safecmp(c1->issuercert, c2->issuercert) &&
158
0
     Curl_safecmp(c1->clientcert, c2->clientcert) &&
159
0
     curl_strequal(c1->cipher_list, c2->cipher_list) &&
160
0
     curl_strequal(c1->cipher_list13, c2->cipher_list13) &&
161
0
     curl_strequal(c1->curves, c2->curves) &&
162
0
     curl_strequal(c1->signature_algorithms, c2->signature_algorithms) &&
163
0
     Curl_safecmp(c1->CRLfile, c2->CRLfile) &&
164
0
     Curl_safecmp(c1->pinned_key, c2->pinned_key) &&
165
0
     curl_strequal(c1->cert_type, c2->cert_type) &&
166
0
     Curl_safecmp(c1->key, c2->key) &&
167
0
     curl_strequal(c1->key_type, c2->key_type) &&
168
0
     !Curl_timestrcmp(c1->key_passwd, c2->key_passwd))
169
0
    return TRUE;
170
171
0
  return FALSE;
172
0
}
173
174
bool Curl_ssl_conn_config_match(struct Curl_easy *data,
175
                                struct connectdata *candidate,
176
                                bool proxy)
177
0
{
178
0
#ifndef CURL_DISABLE_PROXY
179
0
  if(proxy)
180
0
    return match_ssl_primary_config(data, &data->set.proxy_ssl.primary,
181
0
                                    &candidate->proxy_ssl_config);
182
#else
183
  (void)proxy;
184
#endif
185
0
  return match_ssl_primary_config(data, &data->set.ssl.primary,
186
0
                                  &candidate->ssl_config);
187
0
}
188
189
static bool clone_ssl_primary_config(struct ssl_primary_config *source,
190
                                     struct ssl_primary_config *dest)
191
0
{
192
0
  DEBUGASSERT(!dest->deep_copy);
193
0
  dest->deep_copy = TRUE;
194
0
  dest->version = source->version;
195
0
  dest->version_max = source->version_max;
196
0
  dest->verifypeer = source->verifypeer;
197
0
  dest->verifyhost = source->verifyhost;
198
0
  dest->verifystatus = source->verifystatus;
199
0
  dest->cache_session = source->cache_session;
200
0
  dest->ssl_options = source->ssl_options;
201
202
0
  CLONE_BLOB(cert_blob);
203
0
  CLONE_BLOB(ca_info_blob);
204
0
  CLONE_BLOB(issuercert_blob);
205
0
  CLONE_STRING(CApath);
206
0
  CLONE_STRING(CAfile);
207
0
  CLONE_STRING(issuercert);
208
0
  CLONE_STRING(cipher_list);
209
0
  CLONE_STRING(cipher_list13);
210
0
  CLONE_STRING(pinned_key);
211
0
  CLONE_STRING(curves);
212
0
  CLONE_STRING(signature_algorithms);
213
0
  CLONE_STRING(CRLfile);
214
  /* SSL credentials: client certificate */
215
0
  CLONE_STRING(clientcert);
216
0
  CLONE_STRING(cert_type);
217
0
  CLONE_STRING(key);
218
0
  CLONE_STRING(key_type);
219
0
  CLONE_STRING(key_passwd);
220
0
  CLONE_BLOB(key_blob);
221
0
  return TRUE;
222
0
}
223
224
static void ssl_easy_config_compl_options(struct Curl_peer *origin,
225
                                          struct Curl_peer *initial_origin,
226
                                          struct ssl_config_data *sslc)
227
0
{
228
0
  uint8_t options = sslc->primary.ssl_options;
229
  /* If set via CURLOPT_(PROXY_)SSL_OPTIONS, we definitely use it.
230
   * If not, we switch it on for supported backends if no custom
231
   * CA settings exist. */
232
0
  sslc->native_ca_store = !!(options & CURLSSLOPT_NATIVE_CA);
233
0
  sslc->enable_beast = !!(options & CURLSSLOPT_ALLOW_BEAST);
234
0
  sslc->no_partialchain = !!(options & CURLSSLOPT_NO_PARTIALCHAIN);
235
0
  sslc->no_revoke = !!(options & CURLSSLOPT_NO_REVOKE);
236
0
  sslc->revoke_best_effort = !!(options & CURLSSLOPT_REVOKE_BEST_EFFORT);
237
0
  sslc->earlydata = !!(options & CURLSSLOPT_EARLYDATA);
238
239
0
  sslc->auto_client_cert = Curl_peer_equal(origin, initial_origin) &&
240
0
                           !!(options & CURLSSLOPT_AUTO_CLIENT_CERT);
241
0
}
242
243
CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data,
244
                                       struct Curl_peer *origin)
245
0
{
246
0
  struct ssl_config_data *sslc = &data->set.ssl;
247
0
#if defined(CURL_CA_PATH) || defined(CURL_CA_BUNDLE)
248
0
  struct UserDefined *set = &data->set;
249
0
  CURLcode result;
250
0
#endif
251
252
0
  ssl_easy_config_compl_options(origin, data->state.initial_origin, sslc);
253
254
0
  if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) {
255
#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE)
256
    if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob)
257
      sslc->native_ca_store = TRUE;
258
#endif
259
0
#ifdef CURL_CA_PATH
260
0
    if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH]) {
261
0
      result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH);
262
0
      if(result)
263
0
        return result;
264
0
    }
265
0
#endif
266
0
#ifdef CURL_CA_BUNDLE
267
0
    if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE]) {
268
0
      result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE);
269
0
      if(result)
270
0
        return result;
271
0
    }
272
0
#endif
273
0
  }
274
0
  sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE];
275
0
  sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE];
276
0
  sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH];
277
0
  sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST];
278
0
  sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST];
279
0
  sslc->primary.signature_algorithms =
280
0
    data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS];
281
0
  sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO];
282
0
  sslc->primary.curves = data->set.str[STRING_SSL_EC_CURVES];
283
  /* Maybe these should not be used for another origin. But for
284
   * backwards compatibility, keep them in. */
285
0
  sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
286
0
  sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT];
287
288
0
  if(Curl_peer_equal(data->state.initial_origin, origin)) {
289
0
    sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
290
0
    sslc->primary.cert_blob = data->set.blobs[BLOB_CERT];
291
0
    sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE];
292
0
    sslc->primary.key = data->set.str[STRING_KEY];
293
0
    sslc->primary.key_type = data->set.str[STRING_KEY_TYPE];
294
0
    sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD];
295
0
    sslc->primary.clientcert = data->set.str[STRING_CERT];
296
0
    sslc->primary.key_blob = data->set.blobs[BLOB_KEY];
297
0
  }
298
0
  else {
299
0
    sslc->primary.pinned_key = NULL;
300
0
    sslc->primary.cert_blob = NULL;
301
0
    sslc->primary.cert_type = NULL;
302
0
    sslc->primary.key = NULL;
303
0
    sslc->primary.key_type = NULL;
304
0
    sslc->primary.key_passwd = NULL;
305
0
    sslc->primary.clientcert = NULL;
306
0
    sslc->primary.key_blob = NULL;
307
0
  }
308
309
0
#ifndef CURL_DISABLE_PROXY
310
0
  sslc = &data->set.proxy_ssl;
311
  /* no initial origin for proxy, it is not changed for redirects */
312
0
  ssl_easy_config_compl_options(NULL, NULL, sslc);
313
314
0
  if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) {
315
#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE)
316
    if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob)
317
      sslc->native_ca_store = TRUE;
318
#endif
319
0
#ifdef CURL_CA_PATH
320
0
    if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH_PROXY]) {
321
0
      result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY],
322
0
                              CURL_CA_PATH);
323
0
      if(result)
324
0
        return result;
325
0
    }
326
0
#endif
327
0
#ifdef CURL_CA_BUNDLE
328
0
    if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE_PROXY]) {
329
0
      result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY],
330
0
                              CURL_CA_BUNDLE);
331
0
      if(result)
332
0
        return result;
333
0
    }
334
0
#endif
335
0
  }
336
0
  sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY];
337
0
  sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY];
338
0
  sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST_PROXY];
339
0
  sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST_PROXY];
340
0
  sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY];
341
0
  sslc->primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY];
342
0
  sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO_PROXY];
343
0
  sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY];
344
0
  sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY];
345
0
  sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY];
346
0
  sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE_PROXY];
347
0
  sslc->primary.key = data->set.str[STRING_KEY_PROXY];
348
0
  sslc->primary.key_type = data->set.str[STRING_KEY_TYPE_PROXY];
349
0
  sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY];
350
0
  sslc->primary.clientcert = data->set.str[STRING_CERT_PROXY];
351
0
  sslc->primary.key_blob = data->set.blobs[BLOB_KEY_PROXY];
352
0
#endif /* CURL_DISABLE_PROXY */
353
354
0
  return CURLE_OK;
355
0
}
356
357
CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data,
358
                                   struct connectdata *conn)
359
0
{
360
  /* Clone "primary" SSL configurations from the easy handle to
361
   * the connection. They are used for connection cache matching and
362
   * probably outlive the easy handle */
363
0
  if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config))
364
0
    return CURLE_OUT_OF_MEMORY;
365
0
#ifndef CURL_DISABLE_PROXY
366
0
  if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary,
367
0
                               &conn->proxy_ssl_config))
368
0
    return CURLE_OUT_OF_MEMORY;
369
0
#endif
370
0
  return CURLE_OK;
371
0
}
372
373
void Curl_ssl_conn_config_cleanup(struct connectdata *conn)
374
0
{
375
0
  Curl_ssl_config_cleanup(&conn->ssl_config);
376
0
#ifndef CURL_DISABLE_PROXY
377
0
  Curl_ssl_config_cleanup(&conn->proxy_ssl_config);
378
0
#endif
379
0
}
380
381
void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy)
382
44
{
383
  /* May be called on an easy that has no connection yet */
384
44
  if(data->conn) {
385
0
    struct ssl_primary_config *src, *dest;
386
0
#ifndef CURL_DISABLE_PROXY
387
0
    src = for_proxy ? &data->set.proxy_ssl.primary : &data->set.ssl.primary;
388
0
    dest = for_proxy ? &data->conn->proxy_ssl_config : &data->conn->ssl_config;
389
#else
390
    (void)for_proxy;
391
    src = &data->set.ssl.primary;
392
    dest = &data->conn->ssl_config;
393
#endif
394
0
    dest->verifyhost = src->verifyhost;
395
0
    dest->verifypeer = src->verifypeer;
396
0
    dest->verifystatus = src->verifystatus;
397
0
  }
398
44
}