Coverage Report

Created: 2025-11-16 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wget2/libwget/ssl_gnutls.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2012-2015 Tim Ruehsen
3
 * Copyright (c) 2015-2024 Free Software Foundation, Inc.
4
 *
5
 * This file is part of libwget.
6
 *
7
 * Libwget is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as published by
9
 * the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * Libwget is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libwget.  If not, see <https://www.gnu.org/licenses/>.
19
 *
20
 *
21
 * gnutls SSL/TLS routines
22
 * - some parts have been copied from GnuTLS example code
23
 * - OCSP code has been copied from gnutls-cli/ocsptool code
24
 *
25
 * Changelog
26
 * 03.08.2012  Tim Ruehsen  created inspired from gnutls client example
27
 * 26.08.2012               wget compatibility regarding config options
28
 * 15.01.2015               added OCSP fix from https://gitorious.org/gnutls/gnutls/commit/11eebe14b232ec198d1446a3720e6ed78d118c4b
29
 *
30
 * Resources:
31
 * RFC6066 Transport Layer Security (TLS) Extensions: Extension Definitions (defines OCSP stapling)
32
 * RFC6960 Online Certificate Status Protocol - OCSP
33
 * RFC6961 TLS Multiple Certificate Status Request Extension
34
 *
35
 * Testing revocation:
36
 * https://revoked.grc.com/
37
 * https://test-sspev.verisign.com:2443/test-SSPEV-revoked-verisign.html
38
 *
39
 */
40
41
#include <config.h>
42
43
#include <unistd.h>
44
#include <stdio.h>
45
#include <string.h>
46
#include <dirent.h>
47
#include <sys/stat.h>
48
#include <errno.h>
49
50
#include <gnutls/gnutls.h>
51
#include <gnutls/x509.h>
52
#ifdef WITH_OCSP
53
# include <gnutls/ocsp.h>
54
#endif
55
#ifdef WITH_LIBDANE
56
# include <gnutls/dane.h>
57
#endif
58
#include <gnutls/crypto.h>
59
#include <gnutls/abstract.h>
60
61
#include <wget.h>
62
#include "private.h"
63
#include "net.h"
64
65
/**
66
 * \file
67
 * \brief Functions for establishing and managing SSL/TLS connections
68
 * \defgroup libwget-ssl SSL/TLS engine
69
 *
70
 * @{
71
 */
72
73
static wget_tls_stats_callback
74
  *tls_stats_callback;
75
static void
76
  *tls_stats_ctx;
77
78
static wget_ocsp_stats_callback
79
  *ocsp_stats_callback;
80
static void
81
  *ocsp_stats_ctx;
82
83
static struct config {
84
  const char
85
    *secure_protocol,
86
    *ca_directory,
87
    *ca_file,
88
    *cert_file,
89
    *key_file,
90
    *crl_file,
91
    *ocsp_server,
92
    *alpn;
93
  wget_ocsp_db
94
    *ocsp_cert_cache,
95
    *ocsp_host_cache;
96
  wget_tls_session_db
97
    *tls_session_cache;
98
  wget_hpkp_db
99
    *hpkp_cache;
100
  char
101
    ca_type,
102
    cert_type,
103
    key_type;
104
  bool
105
    check_certificate : 1,
106
    report_invalid_cert : 1,
107
    check_hostname : 1,
108
    print_info : 1,
109
    ocsp : 1,
110
    ocsp_date : 1,
111
    ocsp_stapling : 1,
112
    ocsp_nonce : 1,
113
    dane : 1;
114
} config = {
115
  .check_certificate = 1,
116
  .report_invalid_cert = 1,
117
  .check_hostname = 1,
118
#ifdef WITH_OCSP
119
  .ocsp = 0,
120
  .ocsp_stapling = 1,
121
#endif
122
  .ca_type = WGET_SSL_X509_FMT_PEM,
123
  .cert_type = WGET_SSL_X509_FMT_PEM,
124
  .key_type = WGET_SSL_X509_FMT_PEM,
125
  .secure_protocol = "AUTO",
126
  .ca_directory = "system",
127
  .ca_file = "system",
128
#ifdef WITH_LIBNGHTTP2
129
  .alpn = "h2,http/1.1",
130
#endif
131
};
132
133
struct session_context {
134
  const char *
135
    hostname;
136
  wget_hpkp_stats_result
137
    stats_hpkp;
138
  uint16_t
139
    port;
140
  bool
141
    ocsp_stapling : 1,
142
    valid : 1,
143
    delayed_session_data : 1;
144
};
145
146
static gnutls_certificate_credentials_t
147
  credentials;
148
static gnutls_priority_t
149
  priority_cache;
150
151
0
#define error_printf_check(...) if (config.report_invalid_cert) wget_error_printf(__VA_ARGS__)
152
153
/**
154
 * \param[in] key An identifier for the config parameter (starting with `WGET_SSL_`) to set
155
 * \param[in] value The value for the config parameter (a NULL-terminated string)
156
 *
157
 * Set a configuration parameter, as a string.
158
 *
159
 * The following parameters accept a string as their value (\p key can have any of those values):
160
 *
161
 *  - WGET_SSL_SECURE_PROTOCOL: A string describing which SSL/TLS version should be used. It can have either
162
 *  an arbitrary value, or one of the following fixed values (case does not matter):
163
 *      - "SSL": SSLv3 will be used. Warning: this protocol is insecure and should be avoided.
164
 *      - "TLSv1": TLS 1.0 will be used.
165
 *      - "TLSv1_1": TLS 1.1 will be used.
166
 *      - "TLSv1_2": TLS 1.2 will be used.
167
 *      - "TLSv1_3": TLS 1.3 will be used.
168
 *      - "AUTO": Let the TLS library decide.
169
 *      - "PFS": Let the TLS library decide, but make sure only forward-secret ciphers are used.
170
 *
171
 *  An arbitrary string can also be supplied (an string that's different from any of the previous ones). If that's the case
172
 *  the string will be directly taken as the priority string and sent to the library. Priority strings provide the greatest flexibility,
173
 *  but have a library-specific syntax. A GnuTLS priority string will not work if your libwget has been compiled with OpenSSL, for instance.
174
 *  - WGET_SSL_CA_DIRECTORY: A path to the directory where the root certificates will be taken from
175
 *  for server cert validation. Every file of that directory is expected to contain an X.509 certificate,
176
 *  encoded in PEM format. If the string "system" is specified, the system's default directory will be used.
177
 *  The default value is "system". Certificates get loaded in wget_ssl_init().
178
 *  - WGET_SSL_CA_FILE: A path to a file containing a single root certificate. This will be used to validate
179
 *  the server's certificate chain. This option can be used together with `WGET_SSL_CA_DIRECTORY`. The certificate
180
 *  can be in either PEM or DER format. The format is specified in the `WGET_SSL_CA_TYPE` option (see
181
 *  wget_ssl_set_config_int()).
182
 *  - WGET_SSL_CERT_FILE: Set the client certificate. It will be used for client authentication if the server requests it.
183
 *  It can be in either PEM or DER format. The format is specified in the `WGET_SSL_CERT_TYPE` option (see
184
 *  wget_ssl_set_config_int()). The `WGET_SSL_KEY_FILE` option specifies the private key corresponding to the cert's
185
 *  public key. If `WGET_SSL_KEY_FILE` is not set, then the private key is expected to be in the same file as the certificate.
186
 *  - WGET_SSL_KEY_FILE: Set the private key corresponding to the client certificate specified in `WGET_SSL_CERT_FILE`.
187
 *  It can be in either PEM or DER format. The format is specified in the `WGET_SSL_KEY_TYPE` option (see
188
 *  wget_ssl_set_config_int()). IF `WGET_SSL_CERT_FILE` is not set, then the certificate is expected to be in the same file
189
 *  as the private key.
190
 *  - WGET_SSL_CRL_FILE: Sets a CRL (Certificate Revocation List) file which will be used to verify client and server certificates.
191
 *  A CRL file is a black list that contains the serial numbers of the certificates that should not be treated as valid. Whenever
192
 *  a client or a server presents a certificate in the TLS handshake whose serial number is contained in the CRL, the handshake
193
 *  will be immediately aborted. The CRL file must be in PEM format.
194
 *  - WGET_SSL_OCSP_SERVER: Set the URL of the OCSP server that will be used to validate certificates.
195
 *  OCSP is a protocol by which a server is queried to tell whether a given certificate is valid or not. It's an approach contrary
196
 *  to that used by CRLs. While CRLs are black lists, OCSP takes a white list approach where a certificate can be checked for validity.
197
 *  Whenever a client or server presents a certificate in a TLS handshake, the provided URL will be queried (using OCSP) to check whether
198
 *  that certificate is valid or not. If the server responds the certificate is not valid, the handshake will be immediately aborted.
199
 *  - WGET_SSL_ALPN: Sets the ALPN string to be sent to the remote host. ALPN is a TLS extension
200
 *  ([RFC 7301](https://tools.ietf.org/html/rfc7301))
201
 *  that allows both the server and the client to signal which application-layer protocols they support (HTTP/2, QUIC, etc.).
202
 *  That information can then be used for the server to ultimately decide which protocol will be used on top of TLS.
203
 *
204
 *  An invalid value for \p key will not harm the operation of TLS, but will cause
205
 *  a complain message to be printed to the error log stream.
206
 */
207
void wget_ssl_set_config_string(int key, const char *value)
208
69.2k
{
209
69.2k
  switch (key) {
210
9.89k
  case WGET_SSL_SECURE_PROTOCOL: config.secure_protocol = value; break;
211
9.89k
  case WGET_SSL_CA_DIRECTORY: config.ca_directory = value; break;
212
9.89k
  case WGET_SSL_CA_FILE: config.ca_file = value; break;
213
9.89k
  case WGET_SSL_CERT_FILE: config.cert_file = value; break;
214
9.89k
  case WGET_SSL_KEY_FILE: config.key_file = value; break;
215
9.89k
  case WGET_SSL_CRL_FILE: config.crl_file = value; break;
216
9.89k
  case WGET_SSL_OCSP_SERVER: config.ocsp_server = value; break;
217
0
  case WGET_SSL_ALPN: config.alpn = value; break;
218
0
  default: error_printf(_("Unknown config key %d (or value must not be a string)\n"), key);
219
69.2k
  }
220
69.2k
}
221
222
/**
223
 * \param[in] key An identifier for the config parameter (starting with `WGET_SSL_`) to set
224
 * \param[in] value The value for the config parameter (a pointer)
225
 *
226
 * Set a configuration parameter, as a libwget object.
227
 *
228
 * The following parameters expect an already initialized libwget object as their value.
229
 *
230
 *  - WGET_SSL_OCSP_CACHE: This option takes a pointer to a \ref wget_ocsp_db
231
 *  structure as an argument. Such a pointer is returned when initializing the OCSP cache with wget_ocsp_db_init().
232
 *  The cache is used to store OCSP responses locally and avoid querying the OCSP server repeatedly for the same certificate.
233
 *  - WGET_SSL_SESSION_CACHE: This option takes a pointer to a \ref wget_tls_session_db structure.
234
 *  Such a pointer is returned when initializing the TLS session cache with wget_tls_session_db_init().
235
 *  This option thus sets the handle to the TLS session cache that will be used to store TLS sessions.
236
 *  The TLS session cache is used to support TLS session resumption. It stores the TLS session parameters derived from a previous TLS handshake
237
 *  (most importantly the session identifier and the master secret) so that there's no need to run the handshake again
238
 *  the next time we connect to the same host. This is useful as the handshake is an expensive process.
239
 *  - WGET_SSL_HPKP_CACHE: Set the HPKP cache to be used to verify known HPKP pinned hosts. This option takes a pointer
240
 *  to a \ref wget_hpkp_db structure. Such a pointer is returned when initializing the HPKP cache
241
 *  with wget_hpkp_db_init(). HPKP is a HTTP-level protocol that allows the server to "pin" its present and future X.509
242
 *  certificate fingerprints, to support rapid certificate change in the event that the higher level root CA
243
 *  gets compromised ([RFC 7469](https://tools.ietf.org/html/rfc7469)).
244
 */
245
246
void wget_ssl_set_config_object(int key, void *value)
247
29.6k
{
248
29.6k
  switch (key) {
249
9.89k
  case WGET_SSL_OCSP_CACHE: config.ocsp_cert_cache = (wget_ocsp_db *)value; break;
250
9.89k
  case WGET_SSL_SESSION_CACHE: config.tls_session_cache = (wget_tls_session_db *)value; break;
251
9.89k
  case WGET_SSL_HPKP_CACHE: config.hpkp_cache = (wget_hpkp_db *)value; break;
252
0
  default: error_printf(_("Unknown config key %d (or value must not be an object)\n"), key);
253
29.6k
  }
254
29.6k
}
255
256
/**
257
 * \param[in] key An identifier for the config parameter (starting with `WGET_SSL_`)
258
 * \param[in] value The value for the config parameter
259
 *
260
 * Set a configuration parameter, as an integer.
261
 *
262
 * These are the parameters that can be set (\p key can have any of these values):
263
 *
264
 *  - WGET_SSL_CHECK_CERTIFICATE: whether certificates should be verified (1) or not (0)
265
 *  - WGET_SSL_REPORT_INVALID_CERT: whether to print (1) errors/warnings regarding certificate verification or not (0)
266
 *  - WGET_SSL_CHECK_HOSTNAME: whether or not to check if the certificate's subject field
267
 *  matches the peer's hostname. This check is done according to the rules in [RFC 6125](https://tools.ietf.org/html/rfc6125)
268
 *  and typically involves checking whether the hostname and the common name (CN) field of the subject match.
269
 *  - WGET_SSL_PRINT_INFO: whether or not information should be printed about the established SSL/TLS handshake (negotiated
270
 *  ciphersuites, certificates, etc.). The default is no (0).
271
 *
272
 * The following three options all can take either `WGET_SSL_X509_FMT_PEM` (to specify the PEM format) or `WGET_SSL_X509_FMT_DER`
273
 * (for the DER format). The default in for all of them is `WGET_SSL_X509_FMT_PEM`.
274
 *
275
 *  - WGET_SSL_CA_TYPE: Specifies what's the format of the root CA certificate(s) supplied with either `WGET_SSL_CA_DIRECTORY`
276
 *  or `WGET_SSL_CA_FILE`.
277
 *  - WGET_SSL_CERT_TYPE: Specifies what's the format of the certificate file supplied with `WGET_SSL_CERT_FILE`. **The certificate
278
 *  and the private key supplied must both be of the same format.**
279
 *  - WGET_SSL_KEY_TYPE: Specifies what's the format of the private key file supplied with `WGET_SSL_KEY_FILE`. **The private key
280
 *  and the certificate supplied must both be of the same format.**
281
 *
282
 * The following two options control OCSP queries. These don't affect the CRL set with `WGET_SSL_CRL_FILE`, if any.
283
 * If both CRLs and OCSP are enabled, both will be used.
284
 *
285
 *  - WGET_SSL_OCSP: whether or not OCSP should be used. The default is yes (1).
286
 *  - WGET_SSL_OCSP_STAPLING: whether or not OCSP stapling should be used. The default is yes (1).
287
 *  - WGET_SSL_OCSP_NONCE: whether or not an OCSP nonce should be sent in the request. The default is yes (1).
288
 *  If a nonce was sent in the request, the OCSP verification will fail if the response nonce doesn't match.
289
 *  However if the response does not include a nonce extension, verification will be allowed to continue.
290
 *  The OCSP nonce extension is not a critical one.
291
 *  - WGET_SSL_OCSP_DATE: Reject the OCSP response if it's older than 3 days.
292
 */
293
void wget_ssl_set_config_int(int key, int value)
294
98.9k
{
295
98.9k
  switch (key) {
296
9.89k
  case WGET_SSL_CHECK_CERTIFICATE: config.check_certificate = (char)value; break;
297
9.89k
  case WGET_SSL_REPORT_INVALID_CERT: config.report_invalid_cert = (char)value; break;
298
9.89k
  case WGET_SSL_CHECK_HOSTNAME: config.check_hostname = (char)value; break;
299
0
  case WGET_SSL_CA_TYPE: config.ca_type = (char)value; break;
300
9.89k
  case WGET_SSL_CERT_TYPE: config.cert_type = (char)value; break;
301
0
  case WGET_SSL_DANE: config.dane = (char)value; break;
302
9.89k
  case WGET_SSL_KEY_TYPE: config.key_type = (char)value; break;
303
9.89k
  case WGET_SSL_PRINT_INFO: config.print_info = (char)value; break;
304
9.89k
  case WGET_SSL_OCSP: config.ocsp = (char)value; break;
305
9.89k
  case WGET_SSL_OCSP_DATE: config.ocsp_date = (char)value; break;
306
9.89k
  case WGET_SSL_OCSP_STAPLING: config.ocsp_stapling = (char)value; break;
307
9.89k
  case WGET_SSL_OCSP_NONCE: config.ocsp_nonce = value; break;
308
0
  default: error_printf(_("Unknown config key %d (or value must not be an integer)\n"), key);
309
98.9k
  }
310
98.9k
}
311
312
static const char *safe_ctime(time_t t, char *buf, size_t size)
313
0
{
314
0
  struct tm tm;
315
316
0
#pragma GCC diagnostic push
317
0
#pragma GCC diagnostic ignored "-Wformat-y2k"
318
0
  if (localtime_r(&t, &tm) && strftime(buf, size, "%c", &tm))
319
0
    return buf;
320
0
#pragma GCC diagnostic pop
321
322
0
  return "[error]";
323
0
}
324
325
static void print_x509_certificate_info(gnutls_session_t session)
326
0
{
327
0
  const char *name;
328
0
  char dn[128], timebuf[64];
329
0
  unsigned char digest[64];
330
0
  unsigned char serial[40];
331
0
  size_t dn_size = sizeof(dn);
332
0
  size_t digest_size = sizeof (digest);
333
0
  size_t serial_size = sizeof(serial);
334
0
  time_t expired, activated;
335
0
  unsigned int bits;
336
0
  int algo;
337
0
  unsigned int cert_list_size = 0, ncert;
338
0
  const gnutls_datum_t *cert_list;
339
0
  gnutls_x509_crt_t cert;
340
0
  gnutls_certificate_type_t cert_type;
341
342
0
  cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
343
344
0
  for (ncert = 0; ncert < cert_list_size; ncert++) {
345
0
    if ((cert_type = gnutls_certificate_type_get(session)) == GNUTLS_CRT_X509) {
346
347
0
      if (gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS)
348
0
        continue;
349
350
0
      if (gnutls_x509_crt_import(cert, &cert_list[ncert], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
351
0
        gnutls_x509_crt_deinit(cert);
352
0
        continue;
353
0
      }
354
355
0
      info_printf(_("Certificate info [%u]:\n"), ncert);
356
357
0
      activated = gnutls_x509_crt_get_activation_time(cert);
358
0
      info_printf(_("  Valid since: %s"), safe_ctime(activated, timebuf, sizeof(timebuf)));
359
360
0
      expired = gnutls_x509_crt_get_expiration_time(cert);
361
0
      info_printf(_("  Expires: %s"), safe_ctime(expired, timebuf, sizeof(timebuf)));
362
363
0
      if (!gnutls_fingerprint(GNUTLS_DIG_MD5, &cert_list[ncert], digest, &digest_size)) {
364
0
        char digest_hex[sizeof(digest) * 2 + 1];
365
366
0
        wget_memtohex(digest, digest_size, digest_hex, sizeof(digest_hex));
367
368
0
        info_printf(_("  Fingerprint: %s\n"), digest_hex);
369
0
      }
370
371
0
      if (!gnutls_x509_crt_get_serial(cert, serial, &serial_size)) {
372
0
        char serial_hex[sizeof(serial) * 2 + 1];
373
374
0
        wget_memtohex(serial, serial_size, serial_hex, sizeof(serial_hex));
375
376
0
        info_printf(_("  Serial number: %s\n"), serial_hex);
377
0
      }
378
379
0
      algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits);
380
0
      name = gnutls_pk_algorithm_get_name(algo);
381
0
      info_printf(_("  Public key: %s, %s (%u bits)\n"),
382
0
        name ? name : "Unknown",
383
0
        gnutls_sec_param_get_name(gnutls_pk_bits_to_sec_param(algo, bits)),
384
0
        bits);
385
386
0
      info_printf(_("  Version: #%d\n"), gnutls_x509_crt_get_version(cert));
387
388
0
      dn_size = sizeof(dn);
389
0
      gnutls_x509_crt_get_dn(cert, dn, &dn_size);
390
0
      info_printf(_("  DN: %s\n"), dn);
391
392
0
      dn_size = sizeof(dn);
393
0
      gnutls_x509_crt_get_issuer_dn(cert, dn, &dn_size);
394
0
      info_printf(_("  Issuer's DN: %s\n"), dn);
395
396
0
      dn_size = sizeof(dn);
397
0
      gnutls_x509_crt_get_issuer_dn_oid(cert, 0, dn, &dn_size);
398
0
      info_printf(_("  Issuer's OID: %s\n"), dn);
399
400
0
      dn_size = sizeof(dn);
401
0
      gnutls_x509_crt_get_issuer_unique_id(cert, dn, &dn_size);
402
0
      info_printf(_("  Issuer's UID: %s\n"), dn);
403
/*
404
      dn_size = sizeof(dn);
405
      gnutls_x509_crt_get_subject_key_id(cert, dn, &dn_size, NULL);
406
      info_printf(_("  Certificate Subject ID: %s\n"), dn);
407
408
      dn_size = sizeof(dn);
409
      gnutls_x509_crt_get_subject_unique_id(cert, dn, &dn_size);
410
      info_printf(_("  Certificate Subject UID: %s\n"), dn);
411
*/
412
0
      gnutls_x509_crt_deinit(cert);
413
0
    } else {
414
0
      info_printf(_("  Unknown certificate type %d\n"), (int) cert_type);
415
0
    }
416
0
  }
417
0
}
418
419
static int print_info(gnutls_session_t session)
420
0
{
421
0
  const char *tmp;
422
0
  gnutls_credentials_type_t cred;
423
0
  gnutls_kx_algorithm_t kx;
424
0
  int dhe = 0;
425
0
#if GNUTLS_VERSION_MAJOR >= 3
426
0
  int ecdh = 0;
427
0
#endif
428
429
0
  kx = gnutls_kx_get(session);
430
431
0
  info_printf(_("----\n"));
432
433
  /* Check the authentication type used and switch
434
   * to the appropriate.
435
   */
436
0
  cred = gnutls_auth_get_type(session);
437
0
  switch (cred) {
438
0
  case GNUTLS_CRD_IA:
439
0
    info_printf(_("TLS/IA session\n"));
440
0
    break;
441
442
0
  case GNUTLS_CRD_SRP:
443
0
#ifdef HAVE_GNUTLS_SRP_SERVER_GET_USERNAME
444
0
    info_printf(_("SRP session with username %s\n"), gnutls_srp_server_get_username(session));
445
0
#endif
446
0
    break;
447
448
0
  case GNUTLS_CRD_PSK:
449
    /* This returns NULL in server side.
450
     */
451
0
    if (gnutls_psk_client_get_hint(session) != NULL)
452
0
      info_printf(_("PSK authentication. PSK hint '%s'\n"), gnutls_psk_client_get_hint(session));
453
454
    /* This returns NULL in client side.
455
     */
456
0
    if (gnutls_psk_server_get_username(session) != NULL)
457
0
      info_printf(_("PSK authentication. Connected as '%s'\n"), gnutls_psk_server_get_username(session));
458
459
0
    if (kx == GNUTLS_KX_DHE_PSK)
460
0
      dhe = 1;
461
0
#if GNUTLS_VERSION_MAJOR >= 3
462
0
    else if (kx == GNUTLS_KX_ECDHE_PSK)
463
0
      ecdh = 1;
464
0
#endif
465
0
    break;
466
467
0
  case GNUTLS_CRD_ANON: /* anonymous authentication */
468
469
0
    info_printf(_("Anonymous authentication.\n"));
470
0
    if (kx == GNUTLS_KX_ANON_DH)
471
0
      dhe = 1;
472
0
#if GNUTLS_VERSION_MAJOR >= 3
473
0
    else if (kx == GNUTLS_KX_ANON_ECDH)
474
0
      ecdh = 1;
475
0
#endif
476
0
    break;
477
478
0
  case GNUTLS_CRD_CERTIFICATE: /* certificate authentication */
479
480
    /* Check if we have been using ephemeral Diffie-Hellman.
481
     */
482
0
    if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS)
483
0
      dhe = 1;
484
0
#if GNUTLS_VERSION_MAJOR >= 3
485
0
    else if (kx == GNUTLS_KX_ECDHE_RSA || kx == GNUTLS_KX_ECDHE_ECDSA)
486
0
      ecdh = 1;
487
0
#endif
488
489
    /* if the certificate list is available, then
490
     * print some information about it.
491
     */
492
0
    print_x509_certificate_info(session);
493
0
    break;
494
495
0
  default:
496
0
    if ((int) cred == -1)
497
0
      info_printf(_("Transport authentication failure\n"));
498
0
    else
499
0
      info_printf(_("Unsupported credential type %d.\n"), (int) cred);
500
0
    break;
501
0
  } /* switch */
502
503
0
  info_printf(_("----\n"));
504
505
0
  if (dhe != 0)
506
0
    info_printf(_("Ephemeral DH using prime of %d bits\n"), gnutls_dh_get_prime_bits(session));
507
0
#if GNUTLS_VERSION_MAJOR >= 3
508
0
  else if (ecdh != 0)
509
0
    info_printf(_("Ephemeral ECDH using curve %s\n"), gnutls_ecc_curve_get_name(gnutls_ecc_curve_get(session)));
510
0
#endif
511
512
  /* print the key exchange's algorithm name */
513
0
  tmp = gnutls_kx_get_name(kx);
514
0
  info_printf(_("Key Exchange: %s\n"), tmp);
515
516
  /* print the protocol's name (ie TLS 1.0) */
517
0
  tmp = gnutls_protocol_get_name(gnutls_protocol_get_version(session));
518
0
  info_printf(_("Protocol: %s\n"), tmp);
519
520
  /* print the certificate type of the peer, ie X.509 */
521
0
  tmp = gnutls_certificate_type_get_name(gnutls_certificate_type_get(session));
522
0
  info_printf(_("Certificate Type: %s\n"), tmp);
523
524
  /* print the name of the cipher used, ie 3DES. */
525
0
  tmp = gnutls_cipher_get_name(gnutls_cipher_get(session));
526
0
  info_printf(_("Cipher: %s\n"), tmp);
527
528
  /* Print the MAC algorithms name, ie SHA1 */
529
0
  tmp = gnutls_mac_get_name(gnutls_mac_get(session));
530
0
  info_printf(_("MAC: %s\n"), tmp);
531
532
0
  info_printf(_("----\n"));
533
534
0
  return 0;
535
0
}
536
537
#ifdef WITH_OCSP
538
static int
539
_generate_ocsp_data(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer,
540
      gnutls_datum_t * rdata, gnutls_datum_t *nonce)
541
0
{
542
0
  gnutls_ocsp_req_t req;
543
0
  int ret = gnutls_ocsp_req_init(&req);
544
545
0
  if (ret < 0) {
546
0
    debug_printf("ocsp_req_init: %s", gnutls_strerror(ret));
547
0
    return -1;
548
0
  }
549
550
0
  ret = gnutls_ocsp_req_add_cert(req, GNUTLS_DIG_SHA1, issuer, cert);
551
0
  if (ret < 0) {
552
0
    debug_printf("ocsp_req_add_cert: %s", gnutls_strerror(ret));
553
0
    goto error;
554
0
  }
555
556
0
  if (nonce) {
557
0
    ret = gnutls_ocsp_req_set_nonce(req, 0, nonce);
558
0
    if (ret < 0) {
559
0
      debug_printf("ocsp_req_set_nonce: %s", gnutls_strerror(ret));
560
0
      goto error;
561
0
    }
562
0
  }
563
564
0
  ret = gnutls_ocsp_req_export(req, rdata);
565
0
  if (ret) {
566
0
    debug_printf("ocsp_req_export: %s", gnutls_strerror(ret));
567
0
    goto error;
568
0
  }
569
570
0
  ret = 0;
571
0
error:
572
0
  gnutls_ocsp_req_deinit(req);
573
0
  return ret;
574
0
}
575
576
/* Returns 0 on ok, and -1 on error */
577
static int send_ocsp_request(const char *server,
578
          gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer,
579
          wget_buffer **ocsp_data, gnutls_datum_t *nonce)
580
0
{
581
0
  int ret = -1;
582
0
  int server_allocated = 0;
583
0
  gnutls_datum_t body;
584
0
  wget_iri *iri;
585
0
  wget_http_request *req = NULL;
586
587
0
  if (!server) {
588
    /* try to read URL from issuer certificate */
589
0
    gnutls_datum_t data;
590
0
    unsigned i = 0;
591
0
    int rc;
592
593
0
    do {
594
0
      rc = gnutls_x509_crt_get_authority_info_access(cert, i++, GNUTLS_IA_OCSP_URI, &data, NULL);
595
0
    } while(rc < 0 && rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
596
597
0
    if (rc < 0) {
598
0
      i = 0;
599
0
      do {
600
0
        rc = gnutls_x509_crt_get_authority_info_access(issuer, i++, GNUTLS_IA_OCSP_URI, &data, NULL);
601
0
      } while(rc < 0 && rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
602
0
    }
603
604
0
    if (rc < 0) {
605
0
      debug_printf("Cannot find URL from issuer: %s\n", gnutls_strerror(rc));
606
0
      return -1;
607
0
    }
608
609
0
    server = wget_strmemdup((char *)data.data, data.size);
610
0
    server_allocated = 1;
611
612
0
    xfree(data.data);
613
0
  }
614
615
0
  iri = wget_iri_parse(server, NULL);
616
617
0
  if (server_allocated)
618
0
    xfree(server);
619
620
0
  if (!iri)
621
0
    return -1;
622
623
0
  if (_generate_ocsp_data(cert, issuer, &body, nonce))
624
0
    goto out;
625
626
0
  if (!(req = wget_http_create_request(iri, "POST")))
627
0
    goto out;
628
629
0
  wget_http_add_header(req, "Accept-Encoding", "identity");
630
0
  wget_http_add_header(req, "Accept", "*/*");
631
0
  wget_http_add_header(req, "Connection", "close");
632
633
0
  wget_http_connection *conn;
634
0
  if (wget_http_open(&conn, iri) == WGET_E_SUCCESS) {
635
0
    wget_http_request_set_body(req, "application/ocsp-request", wget_memdup(body.data, body.size), body.size);
636
0
    req->debug_skip_body = 1;
637
0
    if (wget_http_send_request(conn, req) == 0) {
638
0
      wget_http_response *resp;
639
640
0
      if ((resp = wget_http_get_response(conn))) {
641
0
        *ocsp_data = resp->body;
642
0
        resp->body = NULL;
643
0
        wget_http_free_response(&resp);
644
0
        ret = 0;
645
0
      }
646
0
    }
647
0
    wget_http_close(&conn);
648
0
  }
649
650
0
  xfree(body.data);
651
652
0
out:
653
0
  wget_http_free_request(&req);
654
0
  wget_iri_free(&iri);
655
0
  return ret;
656
0
}
657
658
static void print_ocsp_verify_res(unsigned int status)
659
0
{
660
0
  debug_printf("*** Verifying OCSP Response: ");
661
662
0
  if (status) {
663
0
    debug_printf("Failure");
664
665
0
    if (status & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND)
666
0
      debug_printf(", Signer cert not found");
667
668
0
    if (status & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR)
669
0
      debug_printf(", Signer cert keyusage error");
670
671
0
    if (status & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER)
672
0
      debug_printf(", Signer cert is not trusted");
673
674
0
    if (status & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM)
675
0
      debug_printf(", Insecure algorithm");
676
677
0
    if (status & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE)
678
0
      debug_printf(", Signature failure");
679
680
0
    if (status & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED)
681
0
      debug_printf(", Signer cert not yet activated");
682
683
0
    if (status & GNUTLS_OCSP_VERIFY_CERT_EXPIRED)
684
0
      debug_printf(", Signer cert expired");
685
686
0
    debug_printf("\n");
687
0
  } else
688
0
    debug_printf("Success\n");
689
0
}
690
691
/* three days */
692
0
#define OCSP_VALIDITY_SECS (3*60*60*24)
693
694
/* Returns:
695
 *  0: certificate is revoked
696
 *  1: certificate is ok
697
 *  -1: dunno
698
 */
699
static int check_ocsp_response(gnutls_x509_crt_t cert,
700
  gnutls_x509_crt_t issuer, wget_buffer *data,
701
  gnutls_datum_t *nonce)
702
0
{
703
0
  gnutls_ocsp_resp_t resp;
704
0
  int ret = -1, rc;
705
0
  unsigned int status, cert_status;
706
0
  time_t rtime = 0, vtime = 0, ntime = 0, now;
707
0
  char timebuf[64];
708
709
0
  now = time(NULL);
710
711
0
  if ((rc = gnutls_ocsp_resp_init(&resp)) < 0) {
712
0
    debug_printf("ocsp_resp_init: %s", gnutls_strerror(rc));
713
0
    return -1;
714
0
  }
715
716
0
  rc = gnutls_ocsp_resp_import(resp, &(gnutls_datum_t){ .data = (unsigned char *) data->data, .size = (unsigned) data->length });
717
0
  if (rc < 0) {
718
0
    debug_printf("importing response: %s", gnutls_strerror(rc));
719
0
    goto cleanup;
720
0
  }
721
722
0
#if GNUTLS_VERSION_NUMBER >= 0x030103
723
0
  if ((rc = gnutls_ocsp_resp_check_crt(resp, 0, cert)) < 0) {
724
0
    if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
725
0
      debug_printf("got OCSP response with no data (ignoring)\n");
726
0
    } else {
727
0
      debug_printf("got OCSP response on an unrelated certificate (ignoring)\n");
728
0
    }
729
0
    goto cleanup;
730
0
  }
731
0
#endif
732
733
0
  if ((rc = gnutls_ocsp_resp_verify_direct(resp, issuer, &status, 0)) < 0) {
734
0
    debug_printf("gnutls_ocsp_resp_verify_direct: %s", gnutls_strerror(rc));
735
0
    goto cleanup;
736
0
  }
737
738
0
  if (status) {
739
0
    print_ocsp_verify_res(status);
740
0
    goto cleanup;
741
0
  }
742
743
0
  rc = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, NULL,
744
0
            &cert_status, &vtime, &ntime, &rtime, NULL);
745
0
  if (rc < 0) {
746
0
    debug_printf("reading response: %s", gnutls_strerror(rc));
747
0
    goto cleanup;
748
0
  }
749
750
0
  if (cert_status == GNUTLS_OCSP_CERT_REVOKED) {
751
0
    debug_printf("*** Certificate was revoked at %s", safe_ctime(rtime, timebuf, sizeof(timebuf)));
752
0
    ret = 0;
753
0
    goto cleanup;
754
0
  }
755
756
0
  debug_printf("*** OCSP issued time: %s\n", safe_ctime(vtime, timebuf, sizeof(timebuf)));
757
0
  debug_printf("*** OCSP update time  : %s\n", safe_ctime(ntime, timebuf, sizeof(timebuf)));
758
759
0
  if (ntime == -1) {
760
0
    if (config.ocsp_date && now - vtime > OCSP_VALIDITY_SECS) {
761
0
      debug_printf("*** The OCSP response is old (was issued at: %s) ignoring", safe_ctime(vtime, timebuf, sizeof(timebuf)));
762
0
      goto cleanup;
763
0
    }
764
0
  } else {
765
    /* there is a newer OCSP answer, don't trust this one */
766
0
    if (ntime < now) {
767
0
      debug_printf("*** The OCSP response was issued at: %s", safe_ctime(vtime, timebuf, sizeof(timebuf)));
768
0
      debug_printf("    but there is a newer issue at %s", safe_ctime(ntime, timebuf, sizeof(timebuf)));
769
0
      goto cleanup;
770
0
    }
771
0
  }
772
773
0
  if (nonce) {
774
0
    gnutls_datum_t rnonce;
775
776
0
    rc = gnutls_ocsp_resp_get_nonce(resp, NULL, &rnonce);
777
0
    if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
778
0
      debug_printf("*** The OCSP reply did not include the requested nonce.\n");
779
0
      goto finish_ok;
780
0
    }
781
782
0
    if (rc < 0) {
783
0
      debug_printf("could not read response's nonce: %s\n", gnutls_strerror(rc));
784
0
      goto cleanup;
785
0
    }
786
787
0
    if (config.ocsp_nonce && (rnonce.size != nonce->size || memcmp(nonce->data, rnonce.data, nonce->size) != 0)) {
788
0
      debug_printf("nonce in the response doesn't match\n");
789
0
      xfree(rnonce.data);
790
0
      goto cleanup;
791
0
    }
792
793
0
    xfree(rnonce.data);
794
0
  }
795
796
0
 finish_ok:
797
0
  debug_printf("OCSP server flags certificate not revoked as of %s", safe_ctime(vtime, timebuf, sizeof(timebuf)));
798
0
  ret = 1;
799
800
0
cleanup:
801
0
  gnutls_ocsp_resp_deinit(resp);
802
0
  return ret;
803
0
}
804
805
/*
806
 * Calculate fingerprint from certificate
807
 */
808
static char *_get_cert_fingerprint(gnutls_x509_crt_t cert, char *fingerprint_hex, size_t length)
809
0
{
810
0
  unsigned char fingerprint[64];
811
0
  size_t fingerprint_size = sizeof(fingerprint);
812
0
  int err;
813
814
0
  if ((err = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, fingerprint, &fingerprint_size)) < 0) {
815
0
    debug_printf("Failed to get fingerprint: %s\n", gnutls_strerror(err));
816
0
    wget_strscpy(fingerprint_hex, "00", length);
817
0
  } else {
818
0
    wget_memtohex(fingerprint, fingerprint_size, fingerprint_hex, length);
819
0
  }
820
821
0
  return fingerprint_hex;
822
0
}
823
824
/*
825
 * Add cert to OCSP cache, being either valid or revoked (valid==0)
826
 */
827
static void add_cert_to_ocsp_cache(gnutls_x509_crt_t cert, bool valid)
828
0
{
829
0
  if (config.ocsp_cert_cache) {
830
0
    char fingerprint_hex[64 * 2 +1];
831
832
0
    _get_cert_fingerprint(cert, fingerprint_hex, sizeof(fingerprint_hex));
833
0
    wget_ocsp_db_add_fingerprint(config.ocsp_cert_cache, fingerprint_hex, time(NULL) + 3600, valid); // 1h valid
834
0
  }
835
0
}
836
837
/* OCSP check for the peer's certificate. Should be called
838
 * only after the certificate list verification is complete.
839
 * Returns:
840
 * 0: certificate is revoked
841
 * 1: certificate is ok
842
 * -1: dunno
843
 */
844
//static int cert_verify_ocsp(gnutls_session_t session)
845
static int cert_verify_ocsp(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer)
846
0
{
847
0
  wget_buffer *resp = NULL;
848
0
  unsigned char noncebuf[23];
849
0
  gnutls_datum_t nonce = { noncebuf, sizeof(noncebuf) };
850
0
  int ret;
851
852
0
  ret = gnutls_rnd(GNUTLS_RND_NONCE, nonce.data, nonce.size);
853
0
  if (ret < 0) {
854
0
    debug_printf("gnutls_rnd: %s", gnutls_strerror(ret));
855
0
    return -1;
856
0
  }
857
858
0
  if (send_ocsp_request(config.ocsp_server, cert, issuer, &resp, &nonce) < 0) {
859
0
    debug_printf("Cannot contact OCSP server\n");
860
0
    return -1;
861
0
  }
862
863
0
  if (!resp) {
864
0
    debug_printf("Missing response from OCSP server\n");
865
0
    return -1;
866
0
  }
867
868
  /* verify and check the response for revoked cert */
869
0
  ret = check_ocsp_response(cert, issuer, resp, &nonce);
870
0
  wget_buffer_free(&resp);
871
872
0
  return ret;
873
0
}
874
#endif // WITH_OCSP
875
876
static int cert_verify_hpkp(gnutls_x509_crt_t cert, const char *hostname, gnutls_session_t session)
877
0
{
878
0
  gnutls_pubkey_t key = NULL;
879
0
  int rc, ret = -1;
880
0
  struct session_context *ctx = gnutls_session_get_ptr(session);
881
882
0
  if (!config.hpkp_cache)
883
0
    return 0;
884
885
0
  gnutls_pubkey_init(&key);
886
887
0
  if ((rc = gnutls_pubkey_import_x509(key, cert, 0)) != GNUTLS_E_SUCCESS) {
888
0
    error_printf(_("Failed to import pubkey: %s\n"), gnutls_strerror(rc));
889
0
    return 0;
890
0
  }
891
892
0
#if GNUTLS_VERSION_NUMBER >= 0x030103
893
0
  gnutls_datum_t pubkey;
894
895
0
  if ((rc = gnutls_pubkey_export2(key, GNUTLS_X509_FMT_DER, &pubkey)) != GNUTLS_E_SUCCESS) {
896
0
    error_printf(_("Failed to export pubkey: %s\n"), gnutls_strerror(rc));
897
0
    ret = 0;
898
0
    goto out;
899
0
  }
900
901
0
  rc = wget_hpkp_db_check_pubkey(config.hpkp_cache, hostname, pubkey.data, pubkey.size);
902
0
  xfree(pubkey.data);
903
#else
904
  size_t size = 0;
905
  void *data = NULL;
906
907
  if ((rc = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, NULL, &size)) != GNUTLS_E_SHORT_MEMORY_BUFFER) {
908
    error_printf(_("Failed to export pubkey: %s\n"), gnutls_strerror(rc));
909
    ret = 0;
910
    goto out;
911
  }
912
913
  data = wget_malloc(size);
914
915
  if ((rc = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, data, &size)) == GNUTLS_E_SHORT_MEMORY_BUFFER) {
916
    error_printf(_("Failed to export pubkey: %s\n"), gnutls_strerror(rc));
917
    ret = 0;
918
    goto out;
919
  }
920
921
  rc = wget_hpkp_db_check_pubkey(config.hpkp_cache, hostname, data, size);
922
  xfree(data);
923
#endif
924
925
0
  if (rc != -2) {
926
0
    if (rc == 0) {
927
0
      debug_printf("host has no pubkey pinnings stored in hpkp db\n");
928
0
      ctx->stats_hpkp = WGET_STATS_HPKP_NO;
929
0
    } else if (rc == 1) {
930
0
      debug_printf("pubkey is matching a pinning\n");
931
0
      ctx->stats_hpkp = WGET_STATS_HPKP_MATCH;
932
0
    } else if (rc == -1) {
933
0
      debug_printf("Error while checking pubkey pinning\n");
934
0
      ctx->stats_hpkp = WGET_STATS_HPKP_ERROR;
935
0
    }
936
0
    ret = 0;
937
0
  } else
938
0
    ctx->stats_hpkp = WGET_STATS_HPKP_NOMATCH;
939
940
0
out:
941
0
  gnutls_pubkey_deinit(key);
942
0
  return ret; // Pubkey not found
943
0
}
944
945
0
static void print_verification_status(gnutls_session_t session, const char *tag, int status) {
946
0
  gnutls_datum_t out;
947
948
0
  if (gnutls_certificate_verification_status_print(
949
0
    status, gnutls_certificate_type_get(session), &out, 0) == GNUTLS_E_SUCCESS)
950
0
  {
951
0
    error_printf_check("%s: %s\n", tag, out.data); // no translation
952
0
    xfree(out.data);
953
0
  }
954
0
}
955
956
/* This function will verify the peer's certificate, and check
957
 * if the hostname matches, as well as the activation, expiration dates.
958
 */
959
static int verify_certificate_callback(gnutls_session_t session)
960
0
{
961
0
  unsigned int status, deinit_cert = 0, deinit_issuer = 0;
962
0
  const gnutls_datum_t *cert_list = NULL;
963
0
  unsigned int cert_list_size;
964
0
  int ret = -1, err, ocsp_ok = 0, pinning_ok = 0;
965
0
  gnutls_x509_crt_t cert = NULL, issuer = NULL;
966
0
  const char *tag = config.check_certificate ? _("ERROR") : _("WARNING");
967
0
#ifdef WITH_OCSP
968
0
  bool skip_server_cert_check = false;
969
0
  unsigned nvalid = 0, nrevoked = 0, nignored = 0;
970
0
#endif
971
972
  // read hostname
973
0
  struct session_context *ctx = gnutls_session_get_ptr(session);
974
0
  const char *hostname = ctx->hostname;
975
976
  /* This verification function uses the trusted CAs in the credentials
977
   * structure. So you must have installed one or more CA certificates.
978
   */
979
0
#if GNUTLS_VERSION_NUMBER >= 0x030104
980
0
  if (gnutls_certificate_verify_peers3(session, hostname, &status) != GNUTLS_E_SUCCESS) {
981
#else
982
  if (gnutls_certificate_verify_peers2(session, &status) != GNUTLS_E_SUCCESS) {
983
#endif
984
//    if (wget_get_logger(WGET_LOGGER_DEBUG))
985
//      _print_info(session);
986
0
    error_printf_check(_("%s: Certificate verification error\n"), tag);
987
0
    goto out;
988
0
  }
989
990
//  if (wget_get_logger(WGET_LOGGER_DEBUG))
991
//    _print_info(session);
992
993
0
#ifdef WITH_OCSP
994
0
  if (status & GNUTLS_CERT_REVOKED) {
995
0
    if (config.ocsp_cert_cache)
996
0
      wget_ocsp_db_add_host(config.ocsp_cert_cache, hostname, 0); // remove entry from cache
997
0
    if (ctx->ocsp_stapling) {
998
0
      if (gnutls_x509_crt_init(&cert) == GNUTLS_E_SUCCESS) {
999
0
        if ((cert_list = gnutls_certificate_get_peers(session, &cert_list_size))) {
1000
0
          if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) == GNUTLS_E_SUCCESS) {
1001
0
            add_cert_to_ocsp_cache(cert, false);
1002
0
          }
1003
0
        }
1004
0
        gnutls_x509_crt_deinit(cert);
1005
0
      }
1006
0
    }
1007
0
  }
1008
0
#endif
1009
1010
0
#if GNUTLS_VERSION_NUMBER >= 0x030104
1011
#ifdef WITH_LIBDANE
1012
  // If CA cert verification failed due to missing certificates, we try DANE verification (if requested by the user).
1013
  if (status) {
1014
    if (!config.dane) {
1015
      print_verification_status(session, tag, status);
1016
      goto out;
1017
    }
1018
    if (status != (GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND)) {
1019
      print_verification_status(session, tag, status);
1020
      goto out;
1021
    }
1022
1023
    // GNUTLS_CERT_SIGNER_NOT_FOUND indicates that no matching CA cert exists.
1024
1025
    unsigned verify = 0;
1026
1027
    int rc = dane_verify_session_crt(NULL, session, hostname, "tcp", ctx->port, 0,
1028
      DANE_VFLAG_FAIL_IF_NOT_CHECKED,
1029
      &verify);
1030
1031
    if (rc < 0) {
1032
      debug_printf("DANE verification error for %s: %s\n", hostname, dane_strerror(rc));
1033
      goto out;
1034
    } else if (verify) {
1035
      gnutls_datum_t out;
1036
      rc = dane_verification_status_print(verify, &out, 0);
1037
      if (rc < 0) {
1038
        error_printf(_("DANE verification print error for %s: %s\n"), hostname, dane_strerror(rc));
1039
      } else {
1040
        error_printf(_("DANE verification failed for %s: %s\n"), hostname, out.data);
1041
      }
1042
      gnutls_free(out.data);
1043
      goto out;
1044
    } else {
1045
      debug_printf("DANE verification: %s\n", dane_strerror(rc));
1046
    }
1047
  }
1048
#else
1049
0
  if (status) {
1050
0
    print_verification_status(session, tag, status);
1051
0
    goto out;
1052
0
  }
1053
0
#endif
1054
#else
1055
  if (status) {
1056
    if (status & GNUTLS_CERT_INVALID)
1057
      error_printf_check(_("%s: The certificate is not trusted.\n"), tag);
1058
    if (status & GNUTLS_CERT_REVOKED)
1059
      error_printf_check(_("%s: The certificate has been revoked.\n"), tag);
1060
    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
1061
      error_printf_check(_("%s: The certificate doesn't have a known issuer.\n"), tag);
1062
    if (status & GNUTLS_CERT_SIGNER_NOT_CA)
1063
      error_printf_check(_("%s: The certificate signer was not a CA.\n"), tag);
1064
    if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
1065
      error_printf_check(_("%s: The certificate was signed using an insecure algorithm.\n"), tag);
1066
    if (status & GNUTLS_CERT_NOT_ACTIVATED)
1067
      error_printf_check(_("%s: The certificate is not yet activated.\n"), tag);
1068
    if (status & GNUTLS_CERT_EXPIRED)
1069
      error_printf_check(_("%s: The certificate has expired.\n"), tag);
1070
#if GNUTLS_VERSION_NUMBER >= 0x030100
1071
    if (status & GNUTLS_CERT_SIGNATURE_FAILURE)
1072
      error_printf_check(_("%s: The certificate signature is invalid.\n"), tag);
1073
    if (status & GNUTLS_CERT_UNEXPECTED_OWNER)
1074
      error_printf_check(_("%s: The certificate's owner does not match hostname '%s'.\n"), tag, hostname);
1075
#endif
1076
1077
    // any other reason
1078
    if (status & ~(GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED|GNUTLS_CERT_SIGNER_NOT_FOUND|
1079
      GNUTLS_CERT_SIGNER_NOT_CA|GNUTLS_CERT_INSECURE_ALGORITHM|GNUTLS_CERT_NOT_ACTIVATED|
1080
      GNUTLS_CERT_EXPIRED
1081
#if GNUTLS_VERSION_NUMBER >= 0x030100
1082
      |GNUTLS_CERT_SIGNATURE_FAILURE
1083
      |GNUTLS_CERT_UNEXPECTED_OWNER
1084
#endif
1085
      ))
1086
      error_printf_check(_("%s: The certificate could not be verified (0x%X).\n"), tag, status);
1087
1088
    goto out;
1089
  }
1090
#endif
1091
1092
  /* Up to here the process is the same for X.509 certificates and
1093
   * OpenPGP keys. From now on X.509 certificates are assumed. This can
1094
   * be easily extended to work with openpgp keys as well.
1095
   */
1096
0
  if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) {
1097
0
    error_printf_check(_("%s: Certificate must be X.509\n"), tag);
1098
0
    goto out;
1099
0
  }
1100
1101
0
  if (gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS) {
1102
0
    error_printf_check(_("%s: Error initializing X.509 certificate\n"), tag);
1103
0
    goto out;
1104
0
  }
1105
0
  deinit_cert = 1;
1106
1107
0
  if (!(cert_list = gnutls_certificate_get_peers(session, &cert_list_size))) {
1108
0
    error_printf_check(_("%s: No certificate was found!\n"), tag);
1109
0
    goto out;
1110
0
  }
1111
1112
0
  if ((err = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)) != GNUTLS_E_SUCCESS) {
1113
0
    error_printf_check(_("%s: Failed to parse certificate: %s\n"), tag, gnutls_strerror (err));
1114
0
    goto out;
1115
0
  }
1116
1117
0
  if (!config.check_hostname || (config.check_hostname && hostname && gnutls_x509_crt_check_hostname(cert, hostname)))
1118
0
    ret = 0;
1119
0
  else
1120
0
    goto out;
1121
1122
  // At this point, the cert chain has been found valid regarding the locally available CA certificates and CRLs.
1123
  // Now, we are going to check the revocation status via OCSP
1124
0
#ifdef WITH_OCSP
1125
0
  if (config.ocsp_stapling) {
1126
0
    if (!ctx->valid && ctx->ocsp_stapling) {
1127
0
#if GNUTLS_VERSION_NUMBER >= 0x030103
1128
0
      if (gnutls_ocsp_status_request_is_checked(session, 0)) {
1129
0
        debug_printf("Server certificate is valid regarding OCSP stapling\n");
1130
//        _get_cert_fingerprint(cert, fingerprint, sizeof(fingerprint)); // calc hexadecimal fingerprint string
1131
0
        add_cert_to_ocsp_cache(cert, true);
1132
0
        nvalid = 1;
1133
0
        skip_server_cert_check = true;
1134
0
      }
1135
0
#if GNUTLS_VERSION_NUMBER >= 0x030400
1136
0
      else if (gnutls_ocsp_status_request_is_checked(session, GNUTLS_OCSP_SR_IS_AVAIL)) {
1137
0
        error_printf_check(_("WARNING: The certificate's (stapled) OCSP status is invalid\n"));
1138
0
        skip_server_cert_check = true;
1139
0
      }
1140
0
#endif
1141
0
      else if (!config.ocsp) {
1142
0
        debug_printf("OCSP stapling is not supported by '%s'\n", hostname);
1143
0
      } else {
1144
0
        error_printf_check(_("WARNING: OCSP stapling is not supported by '%s', but OCSP validation has been requested.\n"), hostname);
1145
0
        error_printf_check(_("WARNING: This implies a privacy leak: the client sends the certificate serial ID over HTTP to the CA.\n"));
1146
0
      }
1147
0
#endif
1148
0
    } else if (ctx->valid)
1149
0
      debug_printf("OCSP: Host '%s' is valid (from cache)\n", hostname);
1150
0
  }
1151
0
#endif
1152
1153
0
  for (unsigned it = 0; it < cert_list_size; it++) {
1154
0
    gnutls_x509_crt_deinit(cert);
1155
0
    gnutls_x509_crt_init(&cert);
1156
1157
0
    if ((err = gnutls_x509_crt_import(cert, &cert_list[it], GNUTLS_X509_FMT_DER)) != GNUTLS_E_SUCCESS) {
1158
0
      error_printf_check(_("%s: Failed to parse certificate[%u]: %s\n"), tag, it, gnutls_strerror (err));
1159
0
      continue;
1160
0
    }
1161
1162
0
    if (cert_verify_hpkp(cert, hostname, session) == 0)
1163
0
      pinning_ok = 1;
1164
1165
0
    cert_verify_hpkp(cert, hostname, session);
1166
1167
0
#ifdef WITH_OCSP
1168
0
    if (!config.ocsp || (skip_server_cert_check && it == 0))
1169
0
      continue;
1170
1171
0
    char fingerprint[64 * 2 +1];
1172
0
    _get_cert_fingerprint(cert, fingerprint, sizeof(fingerprint)); // calc hexadecimal fingerprint string
1173
1174
0
    int revoked;
1175
0
    if (wget_ocsp_fingerprint_in_cache(config.ocsp_cert_cache, fingerprint, &revoked)) {
1176
      // found cert's fingerprint in cache
1177
0
      if (revoked) {
1178
0
        debug_printf("Certificate[%u] of '%s' has been revoked (cached)\n", it, hostname);
1179
0
        nrevoked++;
1180
0
      } else {
1181
0
        debug_printf("Certificate[%u] of '%s' is valid (cached)\n", it, hostname);
1182
0
        nvalid++;
1183
0
      }
1184
0
      continue;
1185
0
    }
1186
1187
0
    if (deinit_issuer) {
1188
0
      gnutls_x509_crt_deinit(issuer);
1189
0
      deinit_issuer = 0;
1190
0
    }
1191
0
    if ((err = gnutls_certificate_get_issuer(credentials, cert, &issuer, 0)) != GNUTLS_E_SUCCESS && it < cert_list_size - 1) {
1192
0
      gnutls_x509_crt_init(&issuer);
1193
0
      deinit_issuer = 1;
1194
0
      if ((err = gnutls_x509_crt_import(issuer, &cert_list[it + 1], GNUTLS_X509_FMT_DER))  != GNUTLS_E_SUCCESS) {
1195
0
        debug_printf("Decoding error: %s\n", gnutls_strerror(err));
1196
0
        continue;
1197
0
      }
1198
0
    } else if (err  != GNUTLS_E_SUCCESS) {
1199
0
      debug_printf("Cannot find issuer: %s\n", gnutls_strerror(err));
1200
0
      continue;
1201
0
    }
1202
1203
0
    ocsp_ok = cert_verify_ocsp(cert, issuer);
1204
0
    debug_printf("check_ocsp_response() returned %d\n", ocsp_ok);
1205
1206
0
    if (ocsp_ok == 1) {
1207
0
      debug_printf("Certificate[%u] of '%s' is valid (via OCSP)\n", it, hostname);
1208
0
      wget_ocsp_db_add_fingerprint(config.ocsp_cert_cache, fingerprint, time(NULL) + 3600, true); // 1h valid
1209
0
      nvalid++;
1210
0
    } else if (ocsp_ok == 0) {
1211
0
      debug_printf("%s: Certificate[%u] of '%s' has been revoked (via OCSP)\n", tag, it, hostname);
1212
0
      wget_ocsp_db_add_fingerprint(config.ocsp_cert_cache, fingerprint, time(NULL) + 3600, false);  // cert has been revoked
1213
0
      nrevoked++;
1214
0
    } else {
1215
0
      debug_printf("WARNING: OCSP response not available or ignored\n");
1216
0
      nignored++;
1217
0
    }
1218
0
#endif
1219
0
  }
1220
1221
0
#ifdef WITH_OCSP
1222
0
  if (config.ocsp && ocsp_stats_callback) {
1223
0
    wget_ocsp_stats_data stats;
1224
0
    stats.hostname = hostname;
1225
0
    stats.nvalid = nvalid;
1226
0
    stats.nrevoked = nrevoked;
1227
0
    stats.nignored = nignored;
1228
0
    stats.stapling = ctx->ocsp_stapling;
1229
1230
0
    ocsp_stats_callback(&stats, ocsp_stats_ctx);
1231
0
  }
1232
1233
0
  if (config.ocsp_stapling || config.ocsp) {
1234
0
    if (nvalid == cert_list_size) {
1235
0
      wget_ocsp_db_add_host(config.ocsp_cert_cache, hostname, time(NULL) + 3600); // 1h valid
1236
0
    } else if (nrevoked) {
1237
0
      wget_ocsp_db_add_host(config.ocsp_cert_cache, hostname, 0); // remove entry from cache
1238
0
      ret = -1;
1239
0
    }
1240
0
  }
1241
0
#endif
1242
1243
0
  if (!pinning_ok) {
1244
0
    error_printf_check(_("%s: Pubkey pinning mismatch!\n"), tag);
1245
0
    ret = -1;
1246
0
  }
1247
1248
  // 0: continue handshake
1249
  // else: stop handshake
1250
0
out:
1251
0
  if (deinit_cert)
1252
0
    gnutls_x509_crt_deinit(cert);
1253
0
  if (deinit_issuer)
1254
0
    gnutls_x509_crt_deinit(issuer);
1255
1256
0
  return config.check_certificate ? ret : 0;
1257
0
}
1258
1259
static int init;
1260
static wget_thread_mutex mutex;
1261
1262
static void tls_exit(void)
1263
0
{
1264
0
  if (mutex)
1265
0
    wget_thread_mutex_destroy(&mutex);
1266
0
}
1267
1268
INITIALIZER(tls_init)
1269
4
{
1270
4
  if (!mutex) {
1271
4
    wget_thread_mutex_init(&mutex);
1272
1273
    // Initialize paths while in a thread-safe environment (mostly for _WIN32).
1274
4
    wget_ssl_default_cert_dir();
1275
4
    wget_ssl_default_ca_bundle_path();
1276
1277
4
    atexit(tls_exit);
1278
4
  }
1279
4
}
1280
1281
static int key_type(int type)
1282
0
{
1283
0
  if (type == WGET_SSL_X509_FMT_DER)
1284
0
    return GNUTLS_X509_FMT_DER;
1285
1286
0
  return GNUTLS_X509_FMT_PEM;
1287
0
}
1288
1289
// ssl_init() is thread safe
1290
1291
static void set_credentials(gnutls_certificate_credentials_t creds)
1292
0
{
1293
0
  if (config.cert_file && !config.key_file) {
1294
    // Use the private key from the cert file unless otherwise specified.
1295
0
    config.key_file = config.cert_file;
1296
0
    config.key_type = config.cert_type;
1297
0
  }
1298
0
  else if (!config.cert_file && config.key_file) {
1299
    // Use the cert from the private key file unless otherwise specified.
1300
0
    config.cert_file = config.key_file;
1301
0
    config.cert_type = config.key_type;
1302
0
  }
1303
1304
0
  if (config.cert_file && config.key_file) {
1305
0
    if (config.key_type != config.cert_type) {
1306
      // GnuTLS can't handle this
1307
0
      error_printf(_("GnuTLS requires the key and the cert to be of the same type.\n"));
1308
0
    }
1309
1310
0
    if (gnutls_certificate_set_x509_key_file(creds, config.cert_file, config.key_file, key_type(config.key_type)) != GNUTLS_E_SUCCESS)
1311
0
      error_printf(_("No certificates or keys were found\n"));
1312
0
  }
1313
1314
0
  if (config.ca_file && !wget_strcmp(config.ca_file, "system"))
1315
0
    config.ca_file = wget_ssl_default_ca_bundle_path();
1316
0
  if (config.ca_file) {
1317
0
    if (gnutls_certificate_set_x509_trust_file(creds, config.ca_file, key_type(config.ca_type)) <= 0)
1318
0
      error_printf(_("No CAs were found in '%s'\n"), config.ca_file);
1319
0
  }
1320
0
}
1321
1322
/**
1323
 * Initialize the SSL/TLS engine as a client.
1324
 *
1325
 * This function assumes the caller is an SSL client connecting to a server.
1326
 * The functions wget_ssl_open(), wget_ssl_close() and wget_ssl_deinit() can be called
1327
 * after this.
1328
 *
1329
 * This is where the root certificates get loaded from the folder specified in the
1330
 * `WGET_SSL_CA_DIRECTORY` parameter. If any of the files in that folder cannot be loaded
1331
 * for whatever reason, that file will be silently skipped without harm (a message will be
1332
 * printed to the debug log stream).
1333
 *
1334
 * CLRs and private keys and their certificates are also loaded here.
1335
 *
1336
 * On systems with automatic library constructors/destructors, this function
1337
 * is thread-safe. On other systems it is not thread-safe.
1338
 *
1339
 * This function may be called several times. Only the first call really
1340
 * takes action.
1341
 */
1342
void wget_ssl_init(void)
1343
0
{
1344
0
  tls_init();
1345
1346
0
  wget_thread_mutex_lock(mutex);
1347
1348
0
  if (!init) {
1349
0
    int rc, ncerts = -1;
1350
1351
0
    debug_printf("GnuTLS init\n");
1352
0
    gnutls_global_init();
1353
0
    gnutls_certificate_allocate_credentials(&credentials);
1354
0
    gnutls_certificate_set_verify_function(credentials, verify_certificate_callback);
1355
1356
0
    if (config.ca_directory && *config.ca_directory && config.check_certificate) {
1357
0
#if GNUTLS_VERSION_NUMBER >= 0x03000d
1358
0
      if (!strcmp(config.ca_directory, "system")) {
1359
0
        ncerts = gnutls_certificate_set_x509_system_trust(credentials);
1360
0
        if (ncerts < 0)
1361
0
          debug_printf("GnuTLS system certificate store error %d\n", ncerts);
1362
0
        else
1363
0
          debug_printf("GnuTLS system certificate store is empty\n");
1364
0
      }
1365
0
#endif
1366
1367
0
      if (ncerts < 0) {
1368
0
        DIR *dir;
1369
1370
0
        ncerts = 0;
1371
1372
0
        if (!strcmp(config.ca_directory, "system"))
1373
0
          config.ca_directory = wget_ssl_default_cert_dir();
1374
1375
0
        if ((dir = opendir(config.ca_directory))) {
1376
0
          struct dirent *dp;
1377
1378
0
          while ((dp = readdir(dir))) {
1379
0
            size_t len = strlen(dp->d_name);
1380
1381
0
            if (len >= 4 && !wget_strncasecmp_ascii(dp->d_name + len - 4, ".pem", 4)) {
1382
0
              char *fname = wget_aprintf("%s/%s", config.ca_directory, dp->d_name);
1383
1384
0
              if (!fname) {
1385
0
                error_printf(_("Failed to allocate file name for cert '%s/%s'\n"), config.ca_directory, dp->d_name);
1386
0
                continue;
1387
0
              }
1388
1389
0
              struct stat st;
1390
0
              if (stat(fname, &st) == 0 && S_ISREG(st.st_mode)) {
1391
0
                debug_printf("GnuTLS loading %s\n", fname);
1392
0
                if ((rc = gnutls_certificate_set_x509_trust_file(credentials, fname, GNUTLS_X509_FMT_PEM)) <= 0)
1393
0
                  debug_printf("Failed to load cert '%s': (%d)\n", fname, rc);
1394
0
                else
1395
0
                  ncerts += rc;
1396
0
              }
1397
1398
0
              xfree(fname);
1399
0
            }
1400
0
          }
1401
1402
0
          closedir(dir);
1403
0
        } else {
1404
0
          error_printf(_("Failed to opendir %s\n"), config.ca_directory);
1405
0
        }
1406
0
      }
1407
0
    }
1408
1409
0
    if (config.crl_file) {
1410
0
      if ((rc = gnutls_certificate_set_x509_crl_file(credentials, config.crl_file, GNUTLS_X509_FMT_PEM)) <= 0)
1411
0
        error_printf(_("Failed to load CRL '%s': (%d)\n"), config.crl_file, rc);
1412
0
    }
1413
1414
0
    set_credentials(credentials);
1415
1416
0
    debug_printf("Certificates loaded: %d\n", ncerts);
1417
1418
0
    if (config.secure_protocol) {
1419
0
      const char *priorities = NULL;
1420
1421
0
      if (!wget_strcasecmp_ascii(config.secure_protocol, "PFS")) {
1422
0
        priorities = "PFS:-VERS-SSL3.0";
1423
        // -RSA to force DHE/ECDHE key exchanges to have Perfect Forward Secrecy (PFS))
1424
0
        if ((rc = gnutls_priority_init(&priority_cache, priorities, NULL)) != GNUTLS_E_SUCCESS) {
1425
0
          priorities = "NORMAL:-RSA:-VERS-SSL3.0";
1426
0
          rc = gnutls_priority_init(&priority_cache, priorities, NULL);
1427
0
        }
1428
0
      } else {
1429
0
#if GNUTLS_VERSION_NUMBER >= 0x030603
1430
0
#define TLS13_PRIO ":+VERS-TLS1.3"
1431
#else
1432
#define TLS13_PRIO ""
1433
#endif
1434
0
        if (!wget_strncasecmp_ascii(config.secure_protocol, "SSL", 3))
1435
0
          priorities = "NORMAL:-VERS-TLS-ALL:+VERS-SSL3.0";
1436
0
        else if (!wget_strcasecmp_ascii(config.secure_protocol, "TLSv1"))
1437
0
          priorities = "NORMAL:-VERS-SSL3.0" TLS13_PRIO;
1438
0
        else if (!wget_strcasecmp_ascii(config.secure_protocol, "TLSv1_1"))
1439
0
          priorities = "NORMAL:-VERS-SSL3.0:-VERS-TLS1.0" TLS13_PRIO;
1440
0
        else if (!wget_strcasecmp_ascii(config.secure_protocol, "TLSv1_2"))
1441
0
          priorities = "NORMAL:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1" TLS13_PRIO;
1442
0
        else if (!wget_strcasecmp_ascii(config.secure_protocol, "TLSv1_3"))
1443
0
          priorities = "NORMAL:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2" TLS13_PRIO;
1444
0
        else if (!wget_strcasecmp_ascii(config.secure_protocol, "auto")) {
1445
          /* use system default, priorities = NULL */
1446
0
        } else if (*config.secure_protocol)
1447
0
          priorities = config.secure_protocol;
1448
1449
0
        rc = gnutls_priority_init(&priority_cache, priorities, NULL);
1450
0
      }
1451
1452
0
      if (rc != GNUTLS_E_SUCCESS)
1453
0
        error_printf(_("GnuTLS: Unsupported priority string '%s': %s\n"), priorities ? priorities : "(null)", gnutls_strerror(rc));
1454
0
    } else {
1455
      // use GnuTLS defaults, which might hold insecure ciphers
1456
0
      if ((rc = gnutls_priority_init(&priority_cache, NULL, NULL)))
1457
0
        error_printf(_("GnuTLS: Unsupported default priority 'NULL': %s\n"), gnutls_strerror(rc));
1458
0
    }
1459
1460
0
    init++;
1461
1462
0
    debug_printf("GnuTLS init done\n");
1463
0
  }
1464
1465
0
  wget_thread_mutex_unlock(mutex);
1466
0
}
1467
1468
/**
1469
 * Deinitialize the SSL/TLS engine, after it has been initialized
1470
 * with wget_ssl_init().
1471
 *
1472
 * This function unloads everything that was loaded in wget_ssl_init().
1473
 *
1474
 * On systems with automatic library constructors/destructors, this function
1475
 * is thread-safe. On other systems it is not thread-safe.
1476
 *
1477
 * This function may be called several times. Only the last deinit really
1478
 * takes action.
1479
 */
1480
void wget_ssl_deinit(void)
1481
0
{
1482
0
  wget_thread_mutex_lock(mutex);
1483
1484
0
  if (init == 1) {
1485
0
    gnutls_certificate_free_credentials(credentials);
1486
0
    gnutls_priority_deinit(priority_cache);
1487
0
    gnutls_global_deinit();
1488
0
  }
1489
1490
0
  if (init > 0) init--;
1491
1492
0
  wget_thread_mutex_unlock(mutex);
1493
0
}
1494
1495
static int do_handshake(gnutls_session_t session, int sockfd, int timeout)
1496
0
{
1497
0
  int ret;
1498
1499
  // Wait for socket being ready before we call gnutls_handshake().
1500
  // I had problems on a KVM Win7 + CygWin (gnutls 3.2.4-1).
1501
0
  int rc = wget_ready_2_write(sockfd, timeout);
1502
1503
0
  if (rc == 0)
1504
0
    ret = WGET_E_TIMEOUT;
1505
0
  else
1506
0
    ret = WGET_E_HANDSHAKE;
1507
1508
  // Perform the TLS handshake
1509
0
  while (rc > 0) {
1510
0
    rc = gnutls_handshake(session);
1511
1512
0
    if (rc == GNUTLS_E_SUCCESS) {
1513
0
      ret = WGET_E_SUCCESS;
1514
0
      break;
1515
0
    }
1516
1517
0
    if (gnutls_error_is_fatal(rc)) {
1518
0
      debug_printf("gnutls_handshake: (%d) %s (errno=%d)\n", rc, gnutls_strerror(rc),errno);
1519
1520
0
      if (rc == GNUTLS_E_CERTIFICATE_ERROR) {
1521
0
        ret = WGET_E_CERTIFICATE;
1522
0
      } else if (rc == GNUTLS_E_PUSH_ERROR && (errno == ECONNREFUSED || errno == ENOTCONN)) {
1523
        /*
1524
         * ECONNREFUSED: on Linux
1525
         * ENOTCONN: MinGW (in out Gitlab CI runner)
1526
         */
1527
0
        ret = WGET_E_CONNECT;
1528
0
      } else if (rc == GNUTLS_E_PULL_ERROR && errno == 61 /* ENODATA, but not on OSX/Travis ? */) {
1529
        // We see this with older versions of GnuTLS, e.g. on TravisCI. (Tim, 11.4.2018)
1530
        // It happens when trying to connect to a port without a listener
1531
0
        ret = WGET_E_CONNECT;
1532
0
#ifdef GNUTLS_E_PREMATURE_TERMINATION
1533
0
      } else if (rc == GNUTLS_E_PREMATURE_TERMINATION && errno == EAGAIN) {
1534
        // It happens when trying to connect to a closed port
1535
0
        ret = WGET_E_CONNECT;
1536
0
#endif
1537
0
      } else if (rc == GNUTLS_E_UNEXPECTED_PACKET_LENGTH && errno == EAGAIN) {
1538
        // We see this with older versions of GnuTLS, e.g. on TravisCI. (Tim, 11.4.2018)
1539
        // It happens when trying to connect to a port without a listener
1540
0
        ret = WGET_E_CONNECT;
1541
0
      } else
1542
0
        ret = WGET_E_HANDSHAKE;
1543
1544
0
      break;
1545
0
    }
1546
1547
0
    if (gnutls_record_get_direction(session)) {
1548
      // wait for writeability
1549
0
      rc = wget_ready_2_write(sockfd, timeout);
1550
0
    } else {
1551
      // wait for readability
1552
0
      rc = wget_ready_2_read(sockfd, timeout);
1553
0
    }
1554
0
  }
1555
1556
0
#if GNUTLS_VERSION_NUMBER >= 0x030500
1557
0
  if (ret == WGET_E_SUCCESS)
1558
0
    debug_printf("TLS False Start: %s\n",
1559
0
      (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_FALSE_START) ? "on" : "off");
1560
0
#endif
1561
1562
0
  return ret;
1563
0
}
1564
1565
#ifdef MSG_FASTOPEN
1566
#include <sys/socket.h>
1567
#include <sys/uio.h> // writev
1568
#include <netdb.h>
1569
#include <errno.h>
1570
static ssize_t ssl_writev(gnutls_transport_ptr_t *p, const giovec_t *iov, int iovcnt)
1571
0
{
1572
0
  wget_tcp *tcp = (wget_tcp *) p;
1573
0
  ssize_t ret;
1574
1575
  // info_printf("%s: %d %zu\n", __func__, iovcnt, iov[0].iov_len);
1576
0
  if (tcp->first_send) {
1577
0
    struct msghdr hdr = {
1578
0
      .msg_name = tcp->connect_addrinfo->ai_addr,
1579
0
      .msg_namelen = tcp->connect_addrinfo->ai_addrlen,
1580
0
      .msg_iov = (struct iovec *) iov,
1581
0
      .msg_iovlen = iovcnt,
1582
0
    };
1583
1584
//    ret = sendto(tcp->sockfd, iov[0].iov_base, iov[0].iov_len, MSG_FASTOPEN,
1585
//        tcp->connect_addrinfo->ai_addr, tcp->connect_addrinfo->ai_addrlen);
1586
0
    ret = sendmsg(tcp->sockfd, &hdr, MSG_FASTOPEN);
1587
0
    if (ret < 0) {
1588
0
      if (errno == EINPROGRESS) {
1589
0
        errno = EAGAIN; // GnuTLS does not handle EINPROGRESS
1590
0
      } else if (errno == EOPNOTSUPP) {
1591
        // fallback from fastopen, e.g. when fastopen is disabled in system
1592
0
        debug_printf("Fallback from TCP Fast Open... TFO is disabled at system level\n");
1593
0
        tcp->tcp_fastopen = 0;
1594
0
        ret = connect(tcp->sockfd, tcp->connect_addrinfo->ai_addr, tcp->connect_addrinfo->ai_addrlen);
1595
0
        if (errno == ENOTCONN || errno == EINPROGRESS)
1596
0
          errno = EAGAIN;
1597
0
      }
1598
0
    }
1599
1600
0
    tcp->first_send = 0;
1601
0
  } else {
1602
0
    ret = writev(tcp->sockfd, (struct iovec *) iov, iovcnt);
1603
0
  }
1604
  // info_printf("errno=%d ret=%d\n", errno, ret);
1605
1606
  // after the first write we set back the transport push function and the transport pointer to standard functions
1607
#ifdef HAVE_GNUTLS_TRANSPORT_GET_INT
1608
  // since GnuTLS 3.1.9, avoid warnings about illegal pointer conversion
1609
  gnutls_transport_set_int(tcp->ssl_session, tcp->sockfd);
1610
#else
1611
0
  gnutls_transport_set_ptr(tcp->ssl_session, (gnutls_transport_ptr_t)(ptrdiff_t)tcp->sockfd);
1612
0
#endif
1613
1614
0
#if defined __clang__
1615
0
  #pragma clang diagnostic push
1616
0
  #pragma clang diagnostic ignored "-Wcast-function-type"
1617
0
#endif
1618
0
  gnutls_transport_set_vec_push_function(tcp->ssl_session, (ssize_t (*) (gnutls_transport_ptr_t, const giovec_t * iov, int iovcnt)) writev);
1619
0
#if defined __clang__
1620
0
  #pragma clang diagnostic pop
1621
0
#endif
1622
1623
0
  return ret;
1624
0
}
1625
#endif
1626
1627
#ifdef _WIN32
1628
static ssize_t win32_send(gnutls_transport_ptr_t p, const void *buf, size_t size)
1629
{
1630
  int sockfd = (int) (ptrdiff_t) p;
1631
1632
  return send(sockfd, buf, size, 0);
1633
}
1634
static ssize_t win32_recv(gnutls_transport_ptr_t p, void *buf, size_t size)
1635
{
1636
  int sockfd = (int) (ptrdiff_t) p;
1637
1638
  return recv(sockfd, buf, size, 0);
1639
}
1640
#endif
1641
1642
/**
1643
 * \param[in] tcp A TCP connection (see wget_tcp_init())
1644
 * \return `WGET_E_SUCCESS` on success or an error code (`WGET_E_*`) on failure
1645
 *
1646
 * Run an SSL/TLS handshake.
1647
 *
1648
 * This functions establishes an SSL/TLS tunnel (performs an SSL/TLS handshake)
1649
 * over an active TCP connection. A pointer to the (internal) SSL/TLS session context
1650
 * can be found in `tcp->ssl_session` after successful execution of this function. This pointer
1651
 * has to be passed to wget_ssl_close() to close the SSL/TLS tunnel.
1652
 *
1653
 * If the handshake cannot be completed in the specified timeout for the provided TCP connection
1654
 * this function fails and returns `WGET_E_TIMEOUT`. You can set the timeout with wget_tcp_set_timeout().
1655
 */
1656
int wget_ssl_open(wget_tcp *tcp)
1657
0
{
1658
0
  gnutls_session_t session;
1659
0
  wget_tls_stats_data stats = {
1660
0
      .alpn_protocol = NULL,
1661
0
      .version = -1,
1662
0
      .false_start = -1,
1663
0
      .tfo = -1,
1664
0
      .resumed = 0,
1665
0
      .http_protocol = WGET_PROTOCOL_HTTP_1_1,
1666
0
      .cert_chain_size = 0
1667
0
  };
1668
1669
0
  int ret = WGET_E_UNKNOWN;
1670
0
  int rc, sockfd, connect_timeout;
1671
0
  const char *hostname;
1672
0
  long long before_millisecs = 0;
1673
1674
0
  if (!tcp)
1675
0
    return WGET_E_INVALID;
1676
1677
0
  struct session_context *ctx = wget_calloc(1, sizeof(struct session_context));
1678
0
  if (!ctx)
1679
0
    return WGET_E_MEMORY;
1680
1681
0
  if (!init)
1682
0
    wget_ssl_init();
1683
1684
0
  hostname = tcp->ssl_hostname;
1685
0
  sockfd= tcp->sockfd;
1686
0
  connect_timeout = tcp->connect_timeout;
1687
1688
0
  unsigned int flags = GNUTLS_CLIENT;
1689
1690
0
#if GNUTLS_VERSION_NUMBER >= 0x030500
1691
0
#if GNUTLS_VERSION_NUMBER >= 0x030605
1692
0
  flags |= GNUTLS_AUTO_REAUTH | GNUTLS_POST_HANDSHAKE_AUTH;
1693
0
#endif
1694
1695
0
  if (tcp->tls_false_start) {
1696
0
    debug_printf("TLS False Start requested\n");
1697
1698
0
    flags |= GNUTLS_NONBLOCK | GNUTLS_ENABLE_FALSE_START;
1699
1700
0
    gnutls_init(&session, flags);
1701
0
  } else {
1702
0
    flags |= GNUTLS_NONBLOCK;
1703
1704
0
    gnutls_init(&session, flags);
1705
0
  }
1706
#elif defined GNUTLS_NONBLOCK
1707
  if (tcp->tls_false_start)
1708
    error_printf(_("TLS False Start requested but libwget built with insufficient GnuTLS version\n"));
1709
  flags |= GNUTLS_NONBLOCK;
1710
  gnutls_init(&session, flags);
1711
#else
1712
  // very old gnutls version, likely to not work.
1713
  if (tcp->tls_false_start)
1714
    error_printf(_("TLS False Start requested but libwget built with insufficient GnuTLS version\n"));
1715
  gnutls_init(&session, flags);
1716
#endif
1717
1718
0
  if ((rc = gnutls_priority_set(session, priority_cache)) != GNUTLS_E_SUCCESS)
1719
0
    error_printf(_("GnuTLS: Failed to set priorities: %s\n"), gnutls_strerror(rc));
1720
1721
0
  if (!wget_strcasecmp_ascii(config.secure_protocol, "auto"))
1722
0
    gnutls_session_enable_compatibility_mode(session);
1723
1724
  // RFC 6066 SNI Server Name Indication
1725
0
  if (hostname) {
1726
0
    gnutls_server_name_set(session, GNUTLS_NAME_DNS, hostname, strlen(hostname));
1727
0
    debug_printf("SNI %s\n", hostname);
1728
0
  }
1729
0
  gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, credentials);
1730
1731
0
  ctx->hostname = wget_strdup(hostname);
1732
0
  ctx->port = tcp->remote_port;
1733
1734
0
#ifdef WITH_OCSP
1735
  // If we know the cert chain for the hostname being valid at the moment,
1736
  // we don't ask for OCSP stapling to avoid unneeded IP traffic.
1737
  // In the unlikely case that the server's certificate chain changed right now,
1738
  // we fallback to OCSP responder request later (if enabled).
1739
0
  if (hostname) {
1740
0
    if (!(ctx->valid = wget_ocsp_hostname_is_valid(config.ocsp_host_cache, hostname))) {
1741
0
#if GNUTLS_VERSION_NUMBER >= 0x030103
1742
0
      if ((rc = gnutls_ocsp_status_request_enable_client(session, NULL, 0, NULL)) == GNUTLS_E_SUCCESS) {
1743
0
        debug_printf("OCSP stapling requested for %s\n", hostname);
1744
0
        ctx->ocsp_stapling = 1;
1745
0
      } else
1746
0
        error_printf("GnuTLS: %s\n", gnutls_strerror(rc)); // no translation
1747
0
#endif
1748
0
    }
1749
0
  }
1750
#else
1751
  if (config.ocsp || config.ocsp_stapling)
1752
    error_printf(_("WARNING: OCSP is not available in this version of GnuTLS.\n"));
1753
#endif
1754
1755
0
#if GNUTLS_VERSION_NUMBER >= 0x030200
1756
0
  if (config.alpn) {
1757
0
    unsigned nprot;
1758
0
    const char *e, *s;
1759
1760
0
    for (nprot = 0, s = e = config.alpn; *e; s = e + 1)
1761
0
      if ((e = strchrnul(s, ',')) != s)
1762
0
        nprot++;
1763
1764
0
    if (nprot) {
1765
0
      gnutls_datum_t data[16];
1766
1767
0
      for (nprot = 0, s = e = config.alpn; *e && nprot < countof(data); s = e + 1) {
1768
0
        if ((e = strchrnul(s, ',')) != s) {
1769
0
          data[nprot].data = (unsigned char *) s;
1770
0
          data[nprot].size = (unsigned) (e - s);
1771
0
          debug_printf("ALPN offering %.*s\n", (int) data[nprot].size, data[nprot].data);
1772
0
          nprot++;
1773
0
        }
1774
0
      }
1775
1776
0
      if ((rc = gnutls_alpn_set_protocols(session, data, nprot, 0)))
1777
0
        debug_printf("GnuTLS: Set ALPN: %s\n", gnutls_strerror(rc));
1778
0
    }
1779
0
  }
1780
0
#endif
1781
1782
0
  tcp->ssl_session = session;
1783
0
  gnutls_session_set_ptr(session, ctx);
1784
1785
0
#ifdef MSG_FASTOPEN
1786
0
  if ((rc = wget_tcp_get_tcp_fastopen(tcp))) {
1787
0
    if (tls_stats_callback)
1788
0
      stats.tfo = (char)rc;
1789
1790
    // prepare for TCP FASTOPEN... sendmsg() instead of connect/write on first write
1791
0
    gnutls_transport_set_vec_push_function(session, (ssize_t (*)(gnutls_transport_ptr_t, const giovec_t *iov, int iovcnt)) ssl_writev);
1792
0
    gnutls_transport_set_ptr(session, tcp);
1793
0
  } else {
1794
0
#endif
1795
1796
#ifdef _WIN32
1797
  gnutls_transport_set_push_function(session, (gnutls_push_func) win32_send);
1798
  gnutls_transport_set_pull_function(session, (gnutls_pull_func) win32_recv);
1799
#endif
1800
1801
#ifdef HAVE_GNUTLS_TRANSPORT_GET_INT
1802
  // since GnuTLS 3.1.9, avoid warnings about illegal pointer conversion
1803
  gnutls_transport_set_int(session, sockfd);
1804
#else
1805
0
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(ptrdiff_t)sockfd);
1806
0
#endif
1807
1808
0
#ifdef MSG_FASTOPEN
1809
0
  }
1810
0
#endif
1811
1812
0
  {
1813
0
    void *data;
1814
0
    size_t size;
1815
1816
0
    if (wget_tls_session_get(config.tls_session_cache, ctx->hostname, &data, &size) == 0) {
1817
0
      debug_printf("found cached session data for %s\n", ctx->hostname);
1818
0
      if ((rc = gnutls_session_set_data(session, data, size)) != GNUTLS_E_SUCCESS)
1819
0
        error_printf(_("GnuTLS: Failed to set session data: %s\n"), gnutls_strerror(rc));
1820
0
      xfree(data);
1821
0
    }
1822
0
  }
1823
1824
0
  if (tls_stats_callback)
1825
0
    before_millisecs = wget_get_timemillis();
1826
1827
0
  ret = do_handshake(session, sockfd, connect_timeout);
1828
1829
0
  if (tls_stats_callback) {
1830
0
    long long after_millisecs = wget_get_timemillis();
1831
0
    stats.tls_secs = after_millisecs - before_millisecs;
1832
0
    stats.tls_con = 1;
1833
0
#if GNUTLS_VERSION_NUMBER >= 0x030500
1834
0
    stats.false_start = (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_FALSE_START) != 0;
1835
0
#endif
1836
0
  }
1837
1838
0
#if GNUTLS_VERSION_NUMBER >= 0x030200
1839
0
  if (config.alpn) {
1840
0
    gnutls_datum_t protocol;
1841
0
    if ((rc = gnutls_alpn_get_selected_protocol(session, &protocol))) {
1842
0
      debug_printf("GnuTLS: Get ALPN: %s\n", gnutls_strerror(rc));
1843
0
      if (!strstr(config.alpn,"http/1.1"))
1844
0
        ret = WGET_E_CONNECT;
1845
0
    } else {
1846
0
      debug_printf("ALPN: Server accepted protocol '%.*s'\n", (int) protocol.size, protocol.data);
1847
0
      if (tls_stats_callback)
1848
0
        stats.alpn_protocol = wget_strmemdup(protocol.data, protocol.size);
1849
1850
0
      if (!memcmp(protocol.data, "h2", 2)) {
1851
0
        tcp->protocol = WGET_PROTOCOL_HTTP_2_0;
1852
0
        if (tls_stats_callback)
1853
0
          stats.http_protocol = WGET_PROTOCOL_HTTP_2_0;
1854
0
      }
1855
0
    }
1856
0
  }
1857
0
#endif
1858
1859
0
  if (config.print_info)
1860
0
    print_info(session);
1861
1862
0
  if (ret == WGET_E_SUCCESS) {
1863
0
    int resumed = gnutls_session_is_resumed(session);
1864
1865
0
    if (tls_stats_callback) {
1866
0
      stats.resumed = resumed;
1867
0
      stats.version = gnutls_protocol_get_version(session);
1868
0
      gnutls_certificate_get_peers(session, (unsigned int *)&(stats.cert_chain_size));
1869
0
    }
1870
1871
0
    debug_printf("Handshake completed%s\n", resumed ? " (resumed session)" : "");
1872
1873
0
    if (!resumed && config.tls_session_cache) {
1874
0
      if (tcp->tls_false_start) {
1875
0
        ctx->delayed_session_data = 1;
1876
0
      } else {
1877
0
        gnutls_datum_t session_data;
1878
1879
0
        if ((rc = gnutls_session_get_data2(session, &session_data)) == GNUTLS_E_SUCCESS) {
1880
0
          wget_tls_session_db_add(config.tls_session_cache,
1881
0
            wget_tls_session_new(ctx->hostname, 18 * 3600, session_data.data, session_data.size)); // 18h valid
1882
0
          xfree(session_data.data);
1883
0
        } else
1884
0
          debug_printf("Failed to get session data: %s", gnutls_strerror(rc));
1885
0
      }
1886
0
    }
1887
0
  }
1888
1889
0
  if (tls_stats_callback) {
1890
0
    stats.hostname = hostname;
1891
0
    tls_stats_callback(&stats, tls_stats_ctx);
1892
0
    xfree(stats.alpn_protocol);
1893
0
  }
1894
1895
0
  tcp->hpkp = ctx->stats_hpkp;
1896
1897
0
  if (ret != WGET_E_SUCCESS) {
1898
0
    if (ret == WGET_E_TIMEOUT)
1899
0
      debug_printf("Handshake timed out\n");
1900
0
    xfree(ctx->hostname);
1901
0
    xfree(ctx);
1902
0
    gnutls_deinit(session);
1903
0
    tcp->ssl_session = NULL;
1904
0
  }
1905
1906
0
  return ret;
1907
0
}
1908
1909
/**
1910
 * \param[in] session The SSL/TLS session (a pointer to it), which is located at the `ssl_session` field
1911
 * of the TCP connection (see wget_ssl_open()).
1912
 *
1913
 * Close an active SSL/TLS tunnel, which was opened with wget_ssl_open().
1914
 *
1915
 * The underlying TCP connection is kept open.
1916
 */
1917
void wget_ssl_close(void **session)
1918
3.66k
{
1919
3.66k
  if (session && *session) {
1920
0
    gnutls_session_t s = *session;
1921
0
    struct session_context *ctx = gnutls_session_get_ptr(s);
1922
0
    int ret;
1923
1924
0
    do
1925
0
      ret = gnutls_bye(s, GNUTLS_SHUT_WR);
1926
0
    while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
1927
1928
0
    if (ret < 0)
1929
0
      debug_printf("TLS shutdown failed: %s\n", gnutls_strerror(ret));
1930
1931
0
    gnutls_deinit(s);
1932
0
    *session = NULL;
1933
1934
0
    xfree(ctx->hostname);
1935
0
    xfree(ctx);
1936
0
  }
1937
3.66k
}
1938
1939
/**
1940
 * \param[in] session An opaque pointer to the SSL/TLS session (obtained with wget_ssl_open() or wget_ssl_server_open())
1941
 * \param[in] buf Destination buffer where the read data will be placed
1942
 * \param[in] count Length of the buffer \p buf
1943
 * \param[in] timeout The amount of time to wait until data becomes available (in milliseconds)
1944
 * \return The number of bytes read, or a negative value on error.
1945
 *
1946
 * Read data from the SSL/TLS tunnel.
1947
 *
1948
 * This function will read at most \p count bytes, which will be stored
1949
 * in the buffer \p buf.
1950
 *
1951
 * The \p timeout parameter tells how long to wait until some data becomes
1952
 * available to read. A \p timeout value of zero causes this function to return
1953
 * immediately, whereas a negative value will cause it to wait indefinitely.
1954
 * This function returns the number of bytes read, which may be zero if the timeout elapses
1955
 * without any data having become available.
1956
 *
1957
 * If a rehandshake is needed, this function does it automatically and tries
1958
 * to read again.
1959
 */
1960
ssize_t wget_ssl_read_timeout(void *session, char *buf, size_t count, int timeout)
1961
0
{
1962
#ifdef HAVE_GNUTLS_TRANSPORT_GET_INT
1963
  // since GnuTLS 3.1.9, avoid warnings about illegal pointer conversion
1964
  int sockfd = gnutls_transport_get_int(session);
1965
#else
1966
0
  int sockfd = (int)(ptrdiff_t)gnutls_transport_get_ptr(session);
1967
0
#endif
1968
1969
// #if GNUTLS_VERSION_NUMBER >= 0x030107
1970
#if 0
1971
  // GnuTLS <= 3.4.5 becomes slow with large timeouts (see loop in gnutls_system_recv_timeout()).
1972
  // A fix is proposed for 3.5.x, as well as a value for indefinite timeouts (-1).
1973
  ssize_t nbytes;
1974
1975
  gnutls_record_set_timeout(session, timeout);
1976
1977
  for (;;) {
1978
    if ((nbytes = gnutls_record_recv(session, buf, count)) >= 0)
1979
      return nbytes;
1980
1981
    if (nbytes == GNUTLS_E_REHANDSHAKE) {
1982
      debug_printf("*** REHANDSHAKE while reading\n");
1983
      if ((nbytes = do_handshake(session, sockfd, timeout)) == 0)
1984
        continue; /* restart reading */
1985
    }
1986
1987
    if (nbytes == GNUTLS_E_AGAIN)
1988
      return 0; // indicate timeout
1989
1990
    return -1;
1991
  }
1992
1993
  return -1;
1994
#else
1995
0
  ssize_t nbytes;
1996
1997
0
  for (;;) {
1998
0
    int rc;
1999
2000
0
    if (gnutls_record_check_pending(session) <= 0 && (rc = wget_ready_2_read(sockfd, timeout)) <= 0)
2001
0
      return rc;
2002
2003
0
    nbytes = gnutls_record_recv(session, buf, count);
2004
2005
    // If False Start + Session Resumption are enabled, we get the session data after the first read()
2006
0
    struct session_context *ctx = gnutls_session_get_ptr(session);
2007
0
    if (ctx && ctx->delayed_session_data) {
2008
0
      gnutls_datum_t session_data;
2009
2010
0
      if ((rc = gnutls_session_get_data2(session, &session_data)) == GNUTLS_E_SUCCESS) {
2011
0
        debug_printf("Got delayed session data\n");
2012
0
        ctx->delayed_session_data = 0;
2013
0
        wget_tls_session_db_add(config.tls_session_cache,
2014
0
          wget_tls_session_new(ctx->hostname, 18 * 3600, session_data.data, session_data.size)); // 18h valid
2015
0
        xfree(session_data.data);
2016
0
      } else
2017
0
        debug_printf("No delayed session data%s\n", gnutls_strerror(rc));
2018
0
    }
2019
2020
0
    if (nbytes == GNUTLS_E_REHANDSHAKE) {
2021
0
      debug_printf("*** REHANDSHAKE while reading\n");
2022
0
      if ((nbytes = do_handshake(session, sockfd, timeout)) == 0)
2023
0
        nbytes = GNUTLS_E_AGAIN; /* restart reading */
2024
0
    }
2025
0
    if (nbytes >= 0 || nbytes != GNUTLS_E_AGAIN)
2026
0
      break;
2027
0
  }
2028
2029
0
  return nbytes < -1 ? -1 : nbytes;
2030
0
#endif
2031
0
}
2032
2033
/**
2034
 * \param[in] session An opaque pointer to the SSL/TLS session (obtained with wget_ssl_open() or wget_ssl_server_open())
2035
 * \param[in] buf Buffer with the data to be sent
2036
 * \param[in] count Length of the buffer \p buf
2037
 * \param[in] timeout The amount of time to wait until data can be sent to the wire (in milliseconds)
2038
 * \return The number of bytes written, or a negative value on error.
2039
 *
2040
 * Send data through the SSL/TLS tunnel.
2041
 *
2042
 * This function will write \p count bytes from \p buf.
2043
 *
2044
 * The \p timeout parameter tells how long to wait until data can be finally sent
2045
 * over the SSL/TLS tunnel. A \p timeout value of zero causes this function to return
2046
 * immediately, whereas a negative value will cause it to wait indefinitely.
2047
 * This function returns the number of bytes sent, which may be zero if the timeout elapses
2048
 * before any data could be sent.
2049
 *
2050
 * If a rehandshake is needed, this function does it automatically and tries
2051
 * to write again.
2052
 */
2053
ssize_t wget_ssl_write_timeout(void *session, const char *buf, size_t count, int timeout)
2054
0
{
2055
#ifdef HAVE_GNUTLS_TRANSPORT_GET_INT
2056
  // since GnuTLS 3.1.9, avoid warnings about illegal pointer conversion
2057
  int sockfd = gnutls_transport_get_int(session);
2058
#else
2059
0
  int sockfd = (int)(ptrdiff_t)gnutls_transport_get_ptr(session);
2060
0
#endif
2061
2062
0
  for (;;) {
2063
0
    ssize_t nbytes;
2064
0
    int rc;
2065
2066
0
    if ((rc = wget_ready_2_write(sockfd, timeout)) <= 0)
2067
0
      return rc;
2068
2069
0
    if ((nbytes = gnutls_record_send(session, buf, count)) >= 0)
2070
0
      return nbytes;
2071
2072
0
    if (nbytes == GNUTLS_E_REHANDSHAKE) {
2073
0
      debug_printf("*** REHANDSHAKE while writing\n");
2074
0
      if ((nbytes = do_handshake(session, sockfd, timeout)) == 0)
2075
0
        continue; /* restart writing */
2076
0
    }
2077
0
    if (nbytes == GNUTLS_E_AGAIN)
2078
0
      return 0; // indicate timeout
2079
2080
0
    return -1;
2081
0
  }
2082
0
}
2083
2084
/**
2085
 * \param[in] fn A `wget_ssl_stats_callback_tls_t` callback function to receive TLS statistics data
2086
 * \param[in] ctx Context data given to \p fn
2087
 *
2088
 * Set callback function to be called when TLS statistics are available
2089
 */
2090
void wget_ssl_set_stats_callback_tls(wget_tls_stats_callback *fn, void *ctx)
2091
25
{
2092
25
  tls_stats_callback = fn;
2093
25
  tls_stats_ctx = ctx;
2094
25
}
2095
2096
/**
2097
 * \param[in] fn A `wget_ssl_stats_callback_ocsp_t` callback function to receive OCSP statistics data
2098
 * \param[in] ctx Context data given to \p fn
2099
 *
2100
 * Set callback function to be called when OCSP statistics are available
2101
 */
2102
void wget_ssl_set_stats_callback_ocsp(wget_ocsp_stats_callback *fn, void *ctx)
2103
7
{
2104
7
  ocsp_stats_callback = fn;
2105
7
  ocsp_stats_ctx = ctx;
2106
7
}
2107
2108
/** @} */