xref: /third_party/curl/lib/vquic/vquic-tls.c (revision 13498266)
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#include "curl_setup.h"
26
27#if defined(ENABLE_QUIC) && \
28  (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
29
30#ifdef USE_OPENSSL
31#include <openssl/err.h>
32#include "vtls/openssl.h"
33#elif defined(USE_GNUTLS)
34#include <gnutls/abstract.h>
35#include <gnutls/gnutls.h>
36#include <gnutls/x509.h>
37#include <gnutls/crypto.h>
38#include <nettle/sha2.h>
39#include "vtls/gtls.h"
40#elif defined(USE_WOLFSSL)
41#include <wolfssl/options.h>
42#include <wolfssl/ssl.h>
43#include <wolfssl/quic.h>
44#include "vtls/wolfssl.h"
45#endif
46
47#include "urldata.h"
48#include "curl_trc.h"
49#include "cfilters.h"
50#include "multiif.h"
51#include "vtls/keylog.h"
52#include "vtls/vtls.h"
53#include "vquic-tls.h"
54
55/* The last 3 #include files should be in this order */
56#include "curl_printf.h"
57#include "curl_memory.h"
58#include "memdebug.h"
59
60#ifndef ARRAYSIZE
61#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
62#endif
63
64#ifdef USE_OPENSSL
65#define QUIC_CIPHERS                                                          \
66  "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_"               \
67  "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
68#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
69#elif defined(USE_GNUTLS)
70#define QUIC_PRIORITY \
71  "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
72  "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
73  "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
74  "%DISABLE_TLS13_COMPAT_MODE"
75#elif defined(USE_WOLFSSL)
76#define QUIC_CIPHERS                                                          \
77  "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_"               \
78  "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
79#define QUIC_GROUPS "P-256:P-384:P-521"
80#endif
81
82
83#ifdef USE_OPENSSL
84
85static void keylog_callback(const SSL *ssl, const char *line)
86{
87  (void)ssl;
88  Curl_tls_keylog_write_line(line);
89}
90
91static CURLcode curl_ossl_init_ctx(struct quic_tls_ctx *ctx,
92                                   struct Curl_cfilter *cf,
93                                   struct Curl_easy *data,
94                                   Curl_vquic_tls_ctx_setup *ctx_setup)
95{
96  struct ssl_primary_config *conn_config;
97  CURLcode result = CURLE_FAILED_INIT;
98
99  DEBUGASSERT(!ctx->ssl_ctx);
100#ifdef USE_OPENSSL_QUIC
101  ctx->ssl_ctx = SSL_CTX_new(OSSL_QUIC_client_method());
102#else
103  ctx->ssl_ctx = SSL_CTX_new(TLS_method());
104#endif
105  if(!ctx->ssl_ctx) {
106    result = CURLE_OUT_OF_MEMORY;
107    goto out;
108  }
109  conn_config = Curl_ssl_cf_get_primary_config(cf);
110  if(!conn_config) {
111    result = CURLE_FAILED_INIT;
112    goto out;
113  }
114
115  if(ctx_setup) {
116    result = ctx_setup(ctx, cf, data);
117    if(result)
118      goto out;
119  }
120
121  SSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
122
123  {
124    const char *curves = conn_config->curves ?
125      conn_config->curves : QUIC_GROUPS;
126    if(!SSL_CTX_set1_curves_list(ctx->ssl_ctx, curves)) {
127      failf(data, "failed setting curves list for QUIC: '%s'", curves);
128      return CURLE_SSL_CIPHER;
129    }
130  }
131
132#ifndef OPENSSL_IS_BORINGSSL
133  {
134    const char *ciphers13 = conn_config->cipher_list13 ?
135      conn_config->cipher_list13 : QUIC_CIPHERS;
136    if(SSL_CTX_set_ciphersuites(ctx->ssl_ctx, ciphers13) != 1) {
137      failf(data, "failed setting QUIC cipher suite: %s", ciphers13);
138      return CURLE_SSL_CIPHER;
139    }
140    infof(data, "QUIC cipher selection: %s", ciphers13);
141  }
142#endif
143
144  /* Open the file if a TLS or QUIC backend has not done this before. */
145  Curl_tls_keylog_open();
146  if(Curl_tls_keylog_enabled()) {
147    SSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback);
148  }
149
150  /* OpenSSL always tries to verify the peer, this only says whether it should
151   * fail to connect if the verification fails, or if it should continue
152   * anyway. In the latter case the result of the verification is checked with
153   * SSL_get_verify_result() below. */
154  SSL_CTX_set_verify(ctx->ssl_ctx, conn_config->verifypeer ?
155                     SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
156
157  /* give application a chance to interfere with SSL set up. */
158  if(data->set.ssl.fsslctx) {
159    /* When a user callback is installed to modify the SSL_CTX,
160     * we need to do the full initialization before calling it.
161     * See: #11800 */
162    if(!ctx->x509_store_setup) {
163      result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx);
164      if(result)
165        goto out;
166      ctx->x509_store_setup = TRUE;
167    }
168    Curl_set_in_callback(data, true);
169    result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx,
170                                      data->set.ssl.fsslctxp);
171    Curl_set_in_callback(data, false);
172    if(result) {
173      failf(data, "error signaled by ssl ctx callback");
174      goto out;
175    }
176  }
177  result = CURLE_OK;
178
179out:
180  if(result && ctx->ssl_ctx) {
181    SSL_CTX_free(ctx->ssl_ctx);
182    ctx->ssl_ctx = NULL;
183  }
184  return result;
185}
186
187static CURLcode curl_ossl_set_client_cert(struct quic_tls_ctx *ctx,
188                                     struct Curl_cfilter *cf,
189                                     struct Curl_easy *data)
190{
191  SSL_CTX *ssl_ctx = ctx->ssl_ctx;
192  const struct ssl_config_data *ssl_config;
193
194  ssl_config = Curl_ssl_cf_get_config(cf, data);
195  DEBUGASSERT(ssl_config);
196
197  if(ssl_config->primary.clientcert ||
198     ssl_config->primary.cert_blob ||
199     ssl_config->cert_type) {
200    return Curl_ossl_set_client_cert(
201        data, ssl_ctx, ssl_config->primary.clientcert,
202        ssl_config->primary.cert_blob, ssl_config->cert_type,
203        ssl_config->key, ssl_config->key_blob,
204        ssl_config->key_type, ssl_config->key_passwd);
205  }
206
207  return CURLE_OK;
208}
209
210/** SSL callbacks ***/
211
212static CURLcode curl_ossl_init_ssl(struct quic_tls_ctx *ctx,
213                                   struct Curl_easy *data,
214                                   struct ssl_peer *peer,
215                                   const char *alpn, size_t alpn_len,
216                                   void *user_data)
217{
218  DEBUGASSERT(!ctx->ssl);
219  ctx->ssl = SSL_new(ctx->ssl_ctx);
220
221  SSL_set_app_data(ctx->ssl, user_data);
222  SSL_set_connect_state(ctx->ssl);
223#ifndef USE_OPENSSL_QUIC
224  SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
225#endif
226
227  if(alpn)
228    SSL_set_alpn_protos(ctx->ssl, (const uint8_t *)alpn, (int)alpn_len);
229
230  if(peer->sni) {
231    if(!SSL_set_tlsext_host_name(ctx->ssl, peer->sni)) {
232      failf(data, "Failed set SNI");
233      SSL_free(ctx->ssl);
234      ctx->ssl = NULL;
235      return CURLE_QUIC_CONNECT_ERROR;
236    }
237  }
238  return CURLE_OK;
239}
240
241#elif defined(USE_GNUTLS)
242static int keylog_callback(gnutls_session_t session, const char *label,
243                    const gnutls_datum_t *secret)
244{
245  gnutls_datum_t crandom;
246  gnutls_datum_t srandom;
247
248  gnutls_session_get_random(session, &crandom, &srandom);
249  if(crandom.size != 32) {
250    return -1;
251  }
252
253  Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
254  return 0;
255}
256
257static CURLcode curl_gtls_init_ctx(struct quic_tls_ctx *ctx,
258                                   struct Curl_cfilter *cf,
259                                   struct Curl_easy *data,
260                                   struct ssl_peer *peer,
261                                   const char *alpn, size_t alpn_len,
262                                   Curl_vquic_tls_ctx_setup *ctx_setup,
263                                   void *user_data)
264{
265  struct ssl_primary_config *conn_config;
266  CURLcode result;
267  gnutls_datum_t alpns[5];
268  /* this will need some attention when HTTPS proxy over QUIC get fixed */
269  long * const pverifyresult = &data->set.ssl.certverifyresult;
270  int rc;
271
272  conn_config = Curl_ssl_cf_get_primary_config(cf);
273  if(!conn_config)
274    return CURLE_FAILED_INIT;
275
276  DEBUGASSERT(ctx->gtls == NULL);
277  ctx->gtls = calloc(1, sizeof(*(ctx->gtls)));
278  if(!ctx->gtls)
279    return CURLE_OUT_OF_MEMORY;
280
281  result = gtls_client_init(data, conn_config, &data->set.ssl,
282                            peer, ctx->gtls, pverifyresult);
283  if(result)
284    return result;
285
286  gnutls_session_set_ptr(ctx->gtls->session, user_data);
287
288  if(ctx_setup) {
289    result = ctx_setup(ctx, cf, data);
290    if(result)
291      return result;
292  }
293
294  rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL);
295  if(rc < 0) {
296    CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
297                gnutls_strerror(rc));
298    return CURLE_QUIC_CONNECT_ERROR;
299  }
300
301  /* Open the file if a TLS or QUIC backend has not done this before. */
302  Curl_tls_keylog_open();
303  if(Curl_tls_keylog_enabled()) {
304    gnutls_session_set_keylog_function(ctx->gtls->session, keylog_callback);
305  }
306
307  /* convert the ALPN string from our arguments to a list of strings
308   * that gnutls wants and will convert internally back to this very
309   * string for sending to the server. nice. */
310  if(alpn) {
311    size_t i, alen = alpn_len;
312    unsigned char *s = (unsigned char *)alpn;
313    unsigned char slen;
314    for(i = 0; (i < ARRAYSIZE(alpns)) && alen; ++i) {
315      slen = s[0];
316      if(slen >= alen)
317        return CURLE_FAILED_INIT;
318      alpns[i].data = s + 1;
319      alpns[i].size = slen;
320      s += slen + 1;
321      alen -= (size_t)slen + 1;
322    }
323    if(alen) /* not all alpn chars used, wrong format or too many */
324        return CURLE_FAILED_INIT;
325    if(i) {
326      gnutls_alpn_set_protocols(ctx->gtls->session,
327                                alpns, (unsigned int)i,
328                                GNUTLS_ALPN_MANDATORY);
329    }
330  }
331
332  return CURLE_OK;
333}
334#elif defined(USE_WOLFSSL)
335
336#if defined(HAVE_SECRET_CALLBACK)
337static void keylog_callback(const WOLFSSL *ssl, const char *line)
338{
339  (void)ssl;
340  Curl_tls_keylog_write_line(line);
341}
342#endif
343
344static CURLcode curl_wssl_init_ctx(struct quic_tls_ctx *ctx,
345                                   struct Curl_cfilter *cf,
346                                   struct Curl_easy *data,
347                                   Curl_vquic_tls_ctx_setup *ctx_setup)
348{
349  struct ssl_primary_config *conn_config;
350  CURLcode result = CURLE_FAILED_INIT;
351
352  conn_config = Curl_ssl_cf_get_primary_config(cf);
353  if(!conn_config) {
354    result = CURLE_FAILED_INIT;
355    goto out;
356  }
357
358  ctx->ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
359  if(!ctx->ssl_ctx) {
360    result = CURLE_OUT_OF_MEMORY;
361    goto out;
362  }
363
364  if(ctx_setup) {
365    result = ctx_setup(ctx, cf, data);
366    if(result)
367      goto out;
368  }
369
370  wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
371
372  if(wolfSSL_CTX_set_cipher_list(ctx->ssl_ctx, conn_config->cipher_list13 ?
373                                 conn_config->cipher_list13 :
374                                 QUIC_CIPHERS) != 1) {
375    char error_buffer[256];
376    ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
377    failf(data, "wolfSSL failed to set ciphers: %s", error_buffer);
378    result = CURLE_BAD_FUNCTION_ARGUMENT;
379    goto out;
380  }
381
382  if(wolfSSL_CTX_set1_groups_list(ctx->ssl_ctx, conn_config->curves ?
383                                  conn_config->curves :
384                                  (char *)QUIC_GROUPS) != 1) {
385    failf(data, "wolfSSL failed to set curves");
386    result = CURLE_BAD_FUNCTION_ARGUMENT;
387    goto out;
388  }
389
390  /* Open the file if a TLS or QUIC backend has not done this before. */
391  Curl_tls_keylog_open();
392  if(Curl_tls_keylog_enabled()) {
393#if defined(HAVE_SECRET_CALLBACK)
394    wolfSSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback);
395#else
396    failf(data, "wolfSSL was built without keylog callback");
397    result = CURLE_NOT_BUILT_IN;
398    goto out;
399#endif
400  }
401
402  if(conn_config->verifypeer) {
403    const char * const ssl_cafile = conn_config->CAfile;
404    const char * const ssl_capath = conn_config->CApath;
405
406    wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
407    if(ssl_cafile || ssl_capath) {
408      /* tell wolfSSL where to find CA certificates that are used to verify
409         the server's certificate. */
410      int rc =
411        wolfSSL_CTX_load_verify_locations_ex(ctx->ssl_ctx, ssl_cafile,
412                                             ssl_capath,
413                                             WOLFSSL_LOAD_FLAG_IGNORE_ERR);
414      if(SSL_SUCCESS != rc) {
415        /* Fail if we insist on successfully verifying the server. */
416        failf(data, "error setting certificate verify locations:"
417              "  CAfile: %s CApath: %s",
418              ssl_cafile ? ssl_cafile : "none",
419              ssl_capath ? ssl_capath : "none");
420        result = CURLE_SSL_CACERT;
421        goto out;
422      }
423      infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
424      infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
425    }
426#ifdef CURL_CA_FALLBACK
427    else {
428      /* verifying the peer without any CA certificates won't work so
429         use wolfssl's built-in default as fallback */
430      wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
431    }
432#endif
433  }
434  else {
435    wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
436  }
437
438  /* give application a chance to interfere with SSL set up. */
439  if(data->set.ssl.fsslctx) {
440    Curl_set_in_callback(data, true);
441    result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx,
442                                      data->set.ssl.fsslctxp);
443    Curl_set_in_callback(data, false);
444    if(result) {
445      failf(data, "error signaled by ssl ctx callback");
446      goto out;
447    }
448  }
449  result = CURLE_OK;
450
451out:
452  if(result && ctx->ssl_ctx) {
453    SSL_CTX_free(ctx->ssl_ctx);
454    ctx->ssl_ctx = NULL;
455  }
456  return result;
457}
458
459/** SSL callbacks ***/
460
461static CURLcode curl_wssl_init_ssl(struct quic_tls_ctx *ctx,
462                                   struct Curl_easy *data,
463                                   struct ssl_peer *peer,
464                                   const char *alpn, size_t alpn_len,
465                                   void *user_data)
466{
467  (void)data;
468  DEBUGASSERT(!ctx->ssl);
469  DEBUGASSERT(ctx->ssl_ctx);
470  ctx->ssl = wolfSSL_new(ctx->ssl_ctx);
471
472  wolfSSL_set_app_data(ctx->ssl, user_data);
473  wolfSSL_set_connect_state(ctx->ssl);
474  wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
475
476  if(alpn)
477    wolfSSL_set_alpn_protos(ctx->ssl, (const unsigned char *)alpn,
478                            (int)alpn_len);
479
480  if(peer->sni) {
481    wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME,
482                   peer->sni, (unsigned short)strlen(peer->sni));
483  }
484
485  return CURLE_OK;
486}
487#endif /* defined(USE_WOLFSSL) */
488
489CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx,
490                             struct Curl_cfilter *cf,
491                             struct Curl_easy *data,
492                             struct ssl_peer *peer,
493                             const char *alpn, size_t alpn_len,
494                             Curl_vquic_tls_ctx_setup *ctx_setup,
495                             void *user_data)
496{
497  CURLcode result;
498
499#ifdef USE_OPENSSL
500  result = curl_ossl_init_ctx(ctx, cf, data, ctx_setup);
501  if(result)
502    return result;
503
504  result = curl_ossl_set_client_cert(ctx, cf, data);
505  if(result)
506    return result;
507
508  return curl_ossl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data);
509#elif defined(USE_GNUTLS)
510  (void)result;
511  return curl_gtls_init_ctx(ctx, cf, data, peer, alpn, alpn_len,
512                            ctx_setup, user_data);
513#elif defined(USE_WOLFSSL)
514  result = curl_wssl_init_ctx(ctx, cf, data, ctx_setup);
515  if(result)
516    return result;
517
518  return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data);
519#else
520#error "no TLS lib in used, should not happen"
521  return CURLE_FAILED_INIT;
522#endif
523}
524
525void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx)
526{
527#ifdef USE_OPENSSL
528  if(ctx->ssl)
529    SSL_free(ctx->ssl);
530  if(ctx->ssl_ctx)
531    SSL_CTX_free(ctx->ssl_ctx);
532#elif defined(USE_GNUTLS)
533  if(ctx->gtls) {
534    if(ctx->gtls->cred)
535      gnutls_certificate_free_credentials(ctx->gtls->cred);
536    if(ctx->gtls->session)
537      gnutls_deinit(ctx->gtls->session);
538    free(ctx->gtls);
539  }
540#elif defined(USE_WOLFSSL)
541  if(ctx->ssl)
542    wolfSSL_free(ctx->ssl);
543  if(ctx->ssl_ctx)
544    wolfSSL_CTX_free(ctx->ssl_ctx);
545#endif
546  memset(ctx, 0, sizeof(*ctx));
547}
548
549CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx,
550                                    struct Curl_cfilter *cf,
551                                    struct Curl_easy *data)
552{
553#ifdef USE_OPENSSL
554  if(!ctx->x509_store_setup) {
555    CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx);
556    if(result)
557      return result;
558    ctx->x509_store_setup = TRUE;
559  }
560#else
561  (void)ctx; (void)cf; (void)data;
562#endif
563  return CURLE_OK;
564}
565
566CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx,
567                                    struct Curl_cfilter *cf,
568                                    struct Curl_easy *data,
569                                    struct ssl_peer *peer)
570{
571  struct ssl_primary_config *conn_config;
572  CURLcode result = CURLE_OK;
573
574  conn_config = Curl_ssl_cf_get_primary_config(cf);
575  if(!conn_config)
576    return CURLE_FAILED_INIT;
577
578  if(conn_config->verifyhost) {
579#ifdef USE_OPENSSL
580    X509 *server_cert;
581    server_cert = SSL_get1_peer_certificate(ctx->ssl);
582    if(!server_cert) {
583      return CURLE_PEER_FAILED_VERIFICATION;
584    }
585    result = Curl_ossl_verifyhost(data, cf->conn, peer, server_cert);
586    X509_free(server_cert);
587    if(result)
588      return result;
589#elif defined(USE_GNUTLS)
590    result = Curl_gtls_verifyserver(data, ctx->gtls->session,
591                                    conn_config, &data->set.ssl, peer,
592                                    data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
593    if(result)
594      return result;
595#elif defined(USE_WOLFSSL)
596    if(!peer->sni ||
597       wolfSSL_check_domain_name(ctx->ssl, peer->sni) == SSL_FAILURE)
598      return CURLE_PEER_FAILED_VERIFICATION;
599#endif
600    infof(data, "Verified certificate just fine");
601  }
602  else
603    infof(data, "Skipped certificate verification");
604#ifdef USE_OPENSSL
605  if(data->set.ssl.certinfo)
606    /* asked to gather certificate info */
607    (void)Curl_ossl_certchain(data, ctx->ssl);
608#endif
609  return result;
610}
611
612
613#endif /* !ENABLE_QUIC && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */
614