Coverage Report

Created: 2026-09-01 06:58

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
#include "urldata.h"
44
#include "setopt.h"
45
#include "strcase.h"
46
#include "vtls/vtls.h"
47
#include "vtls/vtls_config.h"
48
49
50
#define CLONE_STRING(var)                    \
51
0
  do {                                       \
52
0
    if(source->var) {                        \
53
0
      dest->var = curlx_strdup(source->var); \
54
0
      if(!dest->var)                         \
55
0
        return FALSE;                        \
56
0
    }                                        \
57
0
    else                                     \
58
0
      dest->var = NULL;                      \
59
0
  } while(0)
60
61
#define CLONE_BLOB(var)                  \
62
0
  do {                                   \
63
0
    if(blobdup(&dest->var, source->var)) \
64
0
      return FALSE;                      \
65
0
  } while(0)
66
67
static CURLcode blobdup(struct curl_blob **dest, struct curl_blob *src)
68
0
{
69
0
  DEBUGASSERT(dest);
70
0
  DEBUGASSERT(!*dest);
71
0
  if(src) {
72
    /* only if there is data to dupe! */
73
0
    struct curl_blob *d;
74
0
    d = curlx_malloc(sizeof(struct curl_blob) + src->len);
75
0
    if(!d)
76
0
      return CURLE_OUT_OF_MEMORY;
77
0
    d->len = src->len;
78
    /* Always duplicate because the connection may survive longer than the
79
       handle that passed in the blob. */
80
0
    d->flags = CURL_BLOB_COPY;
81
0
    d->data = (void *)((char *)d + sizeof(struct curl_blob));
82
0
    memcpy(d->data, src->data, src->len);
83
0
    *dest = d;
84
0
  }
85
0
  return CURLE_OK;
86
0
}
87
88
/* returns TRUE if the blobs are identical */
89
static bool blobcmp(struct curl_blob *first, struct curl_blob *second)
90
0
{
91
0
  if(!first && !second) /* both are NULL */
92
0
    return TRUE;
93
0
  if(!first || !second) /* one is NULL */
94
0
    return FALSE;
95
0
  if(first->len != second->len) /* different sizes */
96
0
    return FALSE;
97
0
  return !memcmp(first->data, second->data, first->len); /* same data */
98
0
}
99
100
void Curl_ssl_config_init(struct ssl_primary_config *sslc)
101
0
{
102
  /*
103
   * libcurl 7.10 introduced SSL verification *by default*! This needs to be
104
   * switched off unless wanted.
105
   */
106
0
  sslc->verifypeer = TRUE;
107
0
  sslc->verifyhost = TRUE;
108
0
  sslc->cache_session = TRUE; /* caching by default */
109
0
}
110
111
void Curl_ssl_config_cleanup(struct ssl_primary_config *sslc)
112
0
{
113
0
  if(sslc->deep_copy) {
114
0
    curlx_safefree(sslc->CApath);
115
0
    curlx_safefree(sslc->CAfile);
116
0
    curlx_safefree(sslc->issuercert);
117
0
    curlx_safefree(sslc->clientcert);
118
0
    curlx_safefree(sslc->cipher_list);
119
0
    curlx_safefree(sslc->cipher_list13);
120
0
    curlx_safefree(sslc->pinned_key);
121
0
    curlx_safefree(sslc->cert_blob);
122
0
    curlx_safefree(sslc->ca_info_blob);
123
0
    curlx_safefree(sslc->issuercert_blob);
124
0
    curlx_safefree(sslc->key_blob);
125
0
    curlx_safefree(sslc->curves);
126
0
    curlx_safefree(sslc->signature_algorithms);
127
0
    curlx_safefree(sslc->CRLfile);
128
0
    curlx_safefree(sslc->cert_type);
129
0
    curlx_safefree(sslc->key);
130
0
    curlx_safefree(sslc->key_type);
131
0
    curlx_safefree(sslc->key_passwd);
132
0
    sslc->deep_copy = FALSE;
133
0
  }
134
0
}
135
136
static bool match_ssl_primary_config(struct Curl_easy *data,
137
                                     struct ssl_primary_config *c1,
138
                                     struct ssl_primary_config *c2)
139
0
{
140
0
  (void)data;
141
0
  if((c1->version == c2->version) &&
142
0
     (c1->version_max == c2->version_max) &&
143
0
     (c1->ssl_options == c2->ssl_options) &&
144
0
     (c1->native_ca_store == c2->native_ca_store) &&
145
0
     (c1->verifypeer == c2->verifypeer) &&
146
0
     (c1->verifyhost == c2->verifyhost) &&
147
0
     (c1->verifystatus == c2->verifystatus) &&
148
0
     blobcmp(c1->cert_blob, c2->cert_blob) &&
149
0
     blobcmp(c1->ca_info_blob, c2->ca_info_blob) &&
150
0
     blobcmp(c1->issuercert_blob, c2->issuercert_blob) &&
151
0
     blobcmp(c1->key_blob, c2->key_blob) &&
152
0
     Curl_safecmp(c1->CApath, c2->CApath) &&
153
0
     Curl_safecmp(c1->CAfile, c2->CAfile) &&
154
0
     Curl_safecmp(c1->issuercert, c2->issuercert) &&
155
0
     Curl_safecmp(c1->clientcert, c2->clientcert) &&
156
0
     curl_strequal(c1->cipher_list, c2->cipher_list) &&
157
0
     curl_strequal(c1->cipher_list13, c2->cipher_list13) &&
158
0
     curl_strequal(c1->curves, c2->curves) &&
159
0
     curl_strequal(c1->signature_algorithms, c2->signature_algorithms) &&
160
0
     Curl_safecmp(c1->CRLfile, c2->CRLfile) &&
161
0
     Curl_safecmp(c1->pinned_key, c2->pinned_key) &&
162
0
     curl_strequal(c1->cert_type, c2->cert_type) &&
163
0
     Curl_safecmp(c1->key, c2->key) &&
164
0
     curl_strequal(c1->key_type, c2->key_type) &&
165
0
     !Curl_timestrcmp(c1->key_passwd, c2->key_passwd))
166
0
    return TRUE;
167
168
0
  return FALSE;
169
0
}
170
171
bool Curl_ssl_conn_config_match(struct Curl_easy *data,
172
                                struct connectdata *candidate,
173
                                bool proxy)
174
0
{
175
0
#ifndef CURL_DISABLE_PROXY
176
0
  if(proxy)
177
0
    return match_ssl_primary_config(data, &data->set.proxy_ssl.primary,
178
0
                                    &candidate->proxy_ssl_config);
179
#else
180
  (void)proxy;
181
#endif
182
0
  return match_ssl_primary_config(data, &data->set.ssl.primary,
183
0
                                  &candidate->ssl_config);
184
0
}
185
186
static bool clone_ssl_primary_config(struct ssl_primary_config *source,
187
                                     struct ssl_primary_config *dest)
188
0
{
189
0
  DEBUGASSERT(!dest->deep_copy);
190
0
  dest->deep_copy = TRUE;
191
0
  dest->version = source->version;
192
0
  dest->version_max = source->version_max;
193
0
  dest->verifypeer = source->verifypeer;
194
0
  dest->verifyhost = source->verifyhost;
195
0
  dest->verifystatus = source->verifystatus;
196
0
  dest->native_ca_store = source->native_ca_store;
197
0
  dest->cache_session = source->cache_session;
198
0
  dest->ssl_options = source->ssl_options;
199
200
0
  CLONE_BLOB(cert_blob);
201
0
  CLONE_BLOB(ca_info_blob);
202
0
  CLONE_BLOB(issuercert_blob);
203
0
  CLONE_STRING(CApath);
204
0
  CLONE_STRING(CAfile);
205
0
  CLONE_STRING(issuercert);
206
0
  CLONE_STRING(cipher_list);
207
0
  CLONE_STRING(cipher_list13);
208
0
  CLONE_STRING(pinned_key);
209
0
  CLONE_STRING(curves);
210
0
  CLONE_STRING(signature_algorithms);
211
0
  CLONE_STRING(CRLfile);
212
  /* SSL credentials: client certificate */
213
0
  CLONE_STRING(clientcert);
214
0
  CLONE_STRING(cert_type);
215
0
  CLONE_STRING(key);
216
0
  CLONE_STRING(key_type);
217
0
  CLONE_STRING(key_passwd);
218
0
  CLONE_BLOB(key_blob);
219
0
  return TRUE;
220
0
}
221
222
static void ssl_easy_config_compl_options(struct Curl_peer *origin,
223
                                          struct Curl_peer *initial_origin,
224
                                          struct ssl_config_data *sslc)
225
0
{
226
0
  uint8_t options = sslc->primary.ssl_options;
227
  /* If set via CURLOPT_(PROXY_)SSL_OPTIONS, we definitely use it.
228
   * If not, we switch it on for supported backends if no custom
229
   * CA settings exist. */
230
0
  sslc->primary.native_ca_store = !!(options & CURLSSLOPT_NATIVE_CA);
231
0
  sslc->enable_beast = !!(options & CURLSSLOPT_ALLOW_BEAST);
232
0
  sslc->no_partialchain = !!(options & CURLSSLOPT_NO_PARTIALCHAIN);
233
0
  sslc->no_revoke = !!(options & CURLSSLOPT_NO_REVOKE);
234
0
  sslc->revoke_best_effort = !!(options & CURLSSLOPT_REVOKE_BEST_EFFORT);
235
0
  sslc->earlydata = !!(options & CURLSSLOPT_EARLYDATA);
236
237
0
  sslc->auto_client_cert = Curl_peer_equal(origin, initial_origin) &&
238
0
                           !!(options & CURLSSLOPT_AUTO_CLIENT_CERT);
239
0
}
240
241
static char *ssl_easy_steal(struct Curl_easy *data, enum dupstring id)
242
0
{
243
  /* For connection matching, we borrow string references from data
244
   * THIS IS NOT REALLY NICE. */
245
0
  return CURL_UNCONST(CURL_EASY_STR(data, id));
246
0
}
247
248
CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data,
249
                                       struct Curl_peer *origin)
250
0
{
251
0
  struct ssl_config_data *sslc = &data->set.ssl;
252
0
#if defined(CURL_CA_PATH) || defined(CURL_CA_BUNDLE)
253
0
  CURLcode result;
254
0
#endif
255
256
0
  ssl_easy_config_compl_options(origin, data->state.initial_origin, sslc);
257
258
0
  if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) {
259
#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE)
260
    if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob)
261
      sslc->primary.native_ca_store = TRUE;
262
#endif
263
0
#ifdef CURL_CA_PATH
264
0
    if(!sslc->custom_capath && !CURL_EASY_STR(data, STRING_SSL_CAPATH)) {
265
0
      result = Curl_setstropt(data, STRING_SSL_CAPATH, CURL_CA_PATH);
266
0
      if(result)
267
0
        return result;
268
0
    }
269
0
#endif
270
0
#ifdef CURL_CA_BUNDLE
271
0
    if(!sslc->custom_cafile && !CURL_EASY_STR(data, STRING_SSL_CAFILE)) {
272
0
      result = Curl_setstropt(data, STRING_SSL_CAFILE, CURL_CA_BUNDLE);
273
0
      if(result)
274
0
        return result;
275
0
    }
276
0
#endif
277
0
  }
278
0
  sslc->primary.CAfile = ssl_easy_steal(data, STRING_SSL_CAFILE);
279
0
  sslc->primary.CRLfile = ssl_easy_steal(data, STRING_SSL_CRLFILE);
280
0
  sslc->primary.CApath = ssl_easy_steal(data, STRING_SSL_CAPATH);
281
0
  sslc->primary.cipher_list = ssl_easy_steal(data, STRING_SSL_CIPHER_LIST);
282
0
  sslc->primary.cipher_list13 = ssl_easy_steal(data, STRING_SSL_CIPHER13_LIST);
283
0
  sslc->primary.signature_algorithms =
284
0
    ssl_easy_steal(data, STRING_SSL_SIGNATURE_ALGORITHMS);
285
0
  sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO];
286
0
  sslc->primary.curves = ssl_easy_steal(data, STRING_SSL_EC_CURVES);
287
  /* Maybe these should not be used for another origin. But for
288
   * backwards compatibility, keep them in. */
289
0
  sslc->primary.issuercert = ssl_easy_steal(data, STRING_SSL_ISSUERCERT);
290
0
  sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT];
291
292
0
  if(Curl_peer_equal(data->state.initial_origin, origin)) {
293
0
    sslc->primary.pinned_key =
294
0
      ssl_easy_steal(data, STRING_SSL_PINNEDPUBLICKEY);
295
0
    sslc->primary.cert_blob = data->set.blobs[BLOB_CERT];
296
0
    sslc->primary.cert_type = ssl_easy_steal(data, STRING_CERT_TYPE);
297
0
    sslc->primary.key = ssl_easy_steal(data, STRING_KEY);
298
0
    sslc->primary.key_type = ssl_easy_steal(data, STRING_KEY_TYPE);
299
0
    sslc->primary.key_passwd = ssl_easy_steal(data, STRING_KEY_PASSWD);
300
0
    sslc->primary.clientcert = ssl_easy_steal(data, STRING_CERT);
301
0
    sslc->primary.key_blob = data->set.blobs[BLOB_KEY];
302
0
  }
303
0
  else {
304
0
    sslc->primary.pinned_key = NULL;
305
0
    sslc->primary.cert_blob = NULL;
306
0
    sslc->primary.cert_type = NULL;
307
0
    sslc->primary.key = NULL;
308
0
    sslc->primary.key_type = NULL;
309
0
    sslc->primary.key_passwd = NULL;
310
0
    sslc->primary.clientcert = NULL;
311
0
    sslc->primary.key_blob = NULL;
312
0
  }
313
314
0
#ifndef CURL_DISABLE_PROXY
315
0
  sslc = &data->set.proxy_ssl;
316
  /* no initial origin for proxy, it is not changed for redirects */
317
0
  ssl_easy_config_compl_options(NULL, NULL, sslc);
318
319
0
  if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) {
320
#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE)
321
    if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob)
322
      sslc->primary.native_ca_store = TRUE;
323
#endif
324
0
#ifdef CURL_CA_PATH
325
0
    if(!sslc->custom_capath &&
326
0
       !CURL_EASY_STR(data, STRING_SSL_CAPATH_PROXY)) {
327
0
      result = Curl_setstropt(data, STRING_SSL_CAPATH_PROXY, CURL_CA_PATH);
328
0
      if(result)
329
0
        return result;
330
0
    }
331
0
#endif
332
0
#ifdef CURL_CA_BUNDLE
333
0
    if(!sslc->custom_cafile &&
334
0
       !CURL_EASY_STR(data, STRING_SSL_CAFILE_PROXY)) {
335
0
      result = Curl_setstropt(data, STRING_SSL_CAFILE_PROXY, CURL_CA_BUNDLE);
336
0
      if(result)
337
0
        return result;
338
0
    }
339
0
#endif
340
0
  }
341
0
  sslc->primary.CAfile = ssl_easy_steal(data, STRING_SSL_CAFILE_PROXY);
342
0
  sslc->primary.CApath = ssl_easy_steal(data, STRING_SSL_CAPATH_PROXY);
343
0
  sslc->primary.cipher_list =
344
0
    ssl_easy_steal(data, STRING_SSL_CIPHER_LIST_PROXY);
345
0
  sslc->primary.cipher_list13 =
346
0
    ssl_easy_steal(data, STRING_SSL_CIPHER13_LIST_PROXY);
347
0
  sslc->primary.pinned_key =
348
0
    ssl_easy_steal(data, STRING_SSL_PINNEDPUBLICKEY_PROXY);
349
0
  sslc->primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY];
350
0
  sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO_PROXY];
351
0
  sslc->primary.issuercert = ssl_easy_steal(data, STRING_SSL_ISSUERCERT_PROXY);
352
0
  sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY];
353
0
  sslc->primary.CRLfile = ssl_easy_steal(data, STRING_SSL_CRLFILE_PROXY);
354
0
  sslc->primary.cert_type = ssl_easy_steal(data, STRING_CERT_TYPE_PROXY);
355
0
  sslc->primary.key = ssl_easy_steal(data, STRING_KEY_PROXY);
356
0
  sslc->primary.key_type = ssl_easy_steal(data, STRING_KEY_TYPE_PROXY);
357
0
  sslc->primary.key_passwd = ssl_easy_steal(data, STRING_KEY_PASSWD_PROXY);
358
0
  sslc->primary.clientcert = ssl_easy_steal(data, STRING_CERT_PROXY);
359
0
  sslc->primary.key_blob = data->set.blobs[BLOB_KEY_PROXY];
360
0
#endif /* CURL_DISABLE_PROXY */
361
362
0
  return CURLE_OK;
363
0
}
364
365
CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data,
366
                                   struct connectdata *conn)
367
0
{
368
  /* Clone "primary" SSL configurations from the easy handle to
369
   * the connection. They are used for connection cache matching and
370
   * probably outlive the easy handle */
371
0
  if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config))
372
0
    return CURLE_OUT_OF_MEMORY;
373
0
#ifndef CURL_DISABLE_PROXY
374
0
  if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary,
375
0
                               &conn->proxy_ssl_config))
376
0
    return CURLE_OUT_OF_MEMORY;
377
0
#endif
378
0
  return CURLE_OK;
379
0
}
380
381
void Curl_ssl_conn_config_cleanup(struct connectdata *conn)
382
0
{
383
0
  Curl_ssl_config_cleanup(&conn->ssl_config);
384
0
#ifndef CURL_DISABLE_PROXY
385
0
  Curl_ssl_config_cleanup(&conn->proxy_ssl_config);
386
0
#endif
387
0
}
388
389
void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy)
390
0
{
391
  /* May be called on an easy that has no connection yet */
392
0
  if(data->conn) {
393
0
    struct ssl_primary_config *src, *dest;
394
0
#ifndef CURL_DISABLE_PROXY
395
0
    src = for_proxy ? &data->set.proxy_ssl.primary : &data->set.ssl.primary;
396
0
    dest = for_proxy ? &data->conn->proxy_ssl_config : &data->conn->ssl_config;
397
#else
398
    (void)for_proxy;
399
    src = &data->set.ssl.primary;
400
    dest = &data->conn->ssl_config;
401
#endif
402
0
    dest->verifyhost = src->verifyhost;
403
0
    dest->verifypeer = src->verifypeer;
404
0
    dest->verifystatus = src->verifystatus;
405
0
  }
406
0
}