1 /*
2  * Copyright (c) 2019-2021 Free Software Foundation, Inc.
3  *
4  * This file is part of libwget.
5  *
6  * Libwget is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Libwget is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with libwget.  If not, see <https://www.gnu.org/licenses/>.
18  *
19  *
20  * WolfSSL integration
21  *
22  * Resources:
23  * https://github.com/wolfSSL/wolfssl-examples
24  * RFC6066 Transport Layer Security (TLS) Extensions: Extension Definitions (defines OCSP stapling)
25  * RFC6960 Online Certificate Status Protocol - OCSP
26  * RFC6961 TLS Multiple Certificate Status Request Extension
27  *
28  * Testing revocation:
29  * https://revoked.grc.com/
30  * https://test-sspev.verisign.com:2443/test-SSPEV-revoked-verisign.html
31  *
32  */
33 
34 #include <config.h>
35 
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <errno.h>
40 
41 #include <wolfssl/options.h>
42 #include <wolfssl/ssl.h>
43 
44 #include <wget.h>
45 #include "private.h"
46 #include "net.h"
47 
48 /**
49  * \file
50  * \brief Functions for establishing and managing SSL/TLS connections
51  * \defgroup libwget-ssl SSL/TLS engine
52  *
53  * @{
54  */
55 
56 static wget_tls_stats_callback
57 	*tls_stats_callback;
58 static void
59 	*tls_stats_ctx;
60 
61 static wget_ocsp_stats_callback
62 	*ocsp_stats_callback;
63 static void
64 	*ocsp_stats_ctx;
65 
66 static struct config {
67 	const char
68 		*secure_protocol,
69 		*ca_directory,
70 		*ca_file,
71 		*cert_file,
72 		*key_file,
73 		*crl_file,
74 		*ocsp_server,
75 		*alpn;
76 	wget_ocsp_db
77 		*ocsp_cert_cache,
78 		*ocsp_host_cache;
79 	wget_tls_session_db
80 		*tls_session_cache;
81 	wget_hpkp_db
82 		*hpkp_cache;
83 	char
84 		ca_type,
85 		cert_type,
86 		key_type;
87 	bool
88 		check_certificate : 1,
89 		check_hostname : 1,
90 		print_info : 1,
91 		ocsp : 1,
92 		ocsp_stapling : 1;
93 } config = {
94 	.check_certificate = 1,
95 	.check_hostname = 1,
96 	.ocsp = 1,
97 	.ocsp_stapling = 1,
98 	.ca_type = WGET_SSL_X509_FMT_PEM,
99 	.cert_type = WGET_SSL_X509_FMT_PEM,
100 	.key_type = WGET_SSL_X509_FMT_PEM,
101 	.secure_protocol = "AUTO",
102 	.ca_directory = "system",
103 #ifdef WITH_LIBNGHTTP2
104 	.alpn = "h2,http/1.1",
105 #endif
106 };
107 
108 struct session_context {
109 	const char *
110 		hostname;
111 	wget_hpkp_stats_result
112 		stats_hpkp;
113 	unsigned char
114 		ocsp_stapling : 1,
115 		valid : 1,
116 		delayed_session_data : 1;
117 };
118 
119 static WOLFSSL_CTX
120 	*ssl_ctx;
121 
122 /**
123  * \param[in] key An identifier for the config parameter (starting with `WGET_SSL_`) to set
124  * \param[in] value The value for the config parameter (a NULL-terminated string)
125  *
126  * Set a configuration parameter, as a string.
127  *
128  * The following parameters accept a string as their value (\p key can have any of those values):
129  *
130  *  - WGET_SSL_SECURE_PROTOCOL: A string describing which SSL/TLS version should be used. It can have either
131  *  an arbitrary value, or one of the following fixed values (case does not matter):
132  *      - "SSL": SSLv3 will be used. Warning: this protocol is insecure and should be avoided.
133  *      - "TLSv1": TLS 1.0 will be used.
134  *      - "TLSv1_1": TLS 1.1 will be used.
135  *      - "TLSv1_2": TLS 1.2 will be used.
136  *      - "TLSv1_3": TLS 1.3 will be used.
137  *      - "AUTO": Let the TLS library decide.
138  *      - "PFS": Let the TLS library decide, but make sure only forward-secret ciphers are used.
139  *
140  *  An arbitrary string can also be supplied (an string that's different from any of the previous ones). If that's the case
141  *  the string will be directly taken as the priority string and sent to the library. Priority strings provide the greatest flexibility,
142  *  but have a library-specific syntax. A GnuTLS priority string will not work if your libwget has been compiled with OpenSSL, for instance.
143  *  - WGET_SSL_CA_DIRECTORY: A path to the directory where the root certificates will be taken from
144  *  for server cert validation. Every file of that directory is expected to contain an X.509 certificate,
145  *  encoded in PEM format. If the string "system" is specified, the system's default directory will be used.
146  *  The default value is "system". Certificates get loaded in wget_ssl_init().
147  *  - WGET_SSL_CA_FILE: A path to a file containing a single root certificate. This will be used to validate
148  *  the server's certificate chain. This option can be used together with `WGET_SSL_CA_DIRECTORY`. The certificate
149  *  can be in either PEM or DER format. The format is specified in the `WGET_SSL_CA_TYPE` option (see
150  *  wget_ssl_set_config_int()).
151  *  - WGET_SSL_CERT_FILE: Set the client certificate. It will be used for client authentication if the server requests it.
152  *  It can be in either PEM or DER format. The format is specified in the `WGET_SSL_CERT_TYPE` option (see
153  *  wget_ssl_set_config_int()). The `WGET_SSL_KEY_FILE` option specifies the private key corresponding to the cert's
154  *  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.
155  *  - WGET_SSL_KEY_FILE: Set the private key corresponding to the client certificate specified in `WGET_SSL_CERT_FILE`.
156  *  It can be in either PEM or DER format. The format is specified in the `WGET_SSL_KEY_TYPE` option (see
157  *  wget_ssl_set_config_int()). IF `WGET_SSL_CERT_FILE` is not set, then the certificate is expected to be in the same file
158  *  as the private key.
159  *  - WGET_SSL_CRL_FILE: Sets a CRL (Certificate Revocation List) file which will be used to verify client and server certificates.
160  *  A CRL file is a black list that contains the serial numbers of the certificates that should not be treated as valid. Whenever
161  *  a client or a server presents a certificate in the TLS handshake whose serial number is contained in the CRL, the handshake
162  *  will be immediately aborted. The CRL file must be in PEM format.
163  *  - WGET_SSL_OCSP_SERVER: Set the URL of the OCSP server that will be used to validate certificates.
164  *  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
165  *  to that used by CRLs. While CRLs are black lists, OCSP takes a white list approach where a certificate can be checked for validity.
166  *  Whenever a client or server presents a certificate in a TLS handshake, the provided URL will be queried (using OCSP) to check whether
167  *  that certificate is valid or not. If the server responds the certificate is not valid, the handshake will be immediately aborted.
168  *  - WGET_SSL_ALPN: Sets the ALPN string to be sent to the remote host. ALPN is a TLS extension
169  *  ([RFC 7301](https://tools.ietf.org/html/rfc7301))
170  *  that allows both the server and the client to signal which application-layer protocols they support (HTTP/2, QUIC, etc.).
171  *  That information can then be used for the server to ultimately decide which protocol will be used on top of TLS.
172  *
173  *  An invalid value for \p key will not harm the operation of TLS, but will cause
174  *  a complain message to be printed to the error log stream.
175  */
wget_ssl_set_config_string(int key,const char * value)176 void wget_ssl_set_config_string(int key, const char *value)
177 {
178 	switch (key) {
179 	case WGET_SSL_SECURE_PROTOCOL: config.secure_protocol = value; break;
180 	case WGET_SSL_CA_DIRECTORY: config.ca_directory = value; break;
181 	case WGET_SSL_CA_FILE: config.ca_file = value; break;
182 	case WGET_SSL_CERT_FILE: config.cert_file = value; break;
183 	case WGET_SSL_KEY_FILE: config.key_file = value; break;
184 	case WGET_SSL_CRL_FILE: config.crl_file = value; break;
185 	case WGET_SSL_OCSP_SERVER: config.ocsp_server = value; break;
186 	case WGET_SSL_ALPN: config.alpn = value; break;
187 	default: error_printf(_("Unknown config key %d (or value must not be a string)\n"), key);
188 	}
189 }
190 
191 /**
192  * \param[in] key An identifier for the config parameter (starting with `WGET_SSL_`) to set
193  * \param[in] value The value for the config parameter (a pointer)
194  *
195  * Set a configuration parameter, as a libwget object.
196  *
197  * The following parameters expect an already initialized libwget object as their value.
198  *
199  * - WGET_SSL_OCSP_CACHE: This option takes a pointer to a \ref wget_ocsp_db
200  *  structure as an argument. Such a pointer is returned when initializing the OCSP cache with wget_ocsp_db_init().
201  *  The cache is used to store OCSP responses locally and avoid querying the OCSP server repeatedly for the same certificate.
202  *  - WGET_SSL_SESSION_CACHE: This option takes a pointer to a \ref wget_tls_session_db structure.
203  *  Such a pointer is returned when initializing the TLS session cache with wget_tls_session_db_init().
204  *  This option thus sets the handle to the TLS session cache that will be used to store TLS sessions.
205  *  The TLS session cache is used to support TLS session resumption. It stores the TLS session parameters derived from a previous TLS handshake
206  *  (most importantly the session identifier and the master secret) so that there's no need to run the handshake again
207  *  the next time we connect to the same host. This is useful as the handshake is an expensive process.
208  *  - WGET_SSL_HPKP_CACHE: Set the HPKP cache to be used to verify known HPKP pinned hosts. This option takes a pointer
209  *  to a \ref wget_hpkp_db structure. Such a pointer is returned when initializing the HPKP cache
210  *  with wget_hpkp_db_init(). HPKP is a HTTP-level protocol that allows the server to "pin" its present and future X.509
211  *  certificate fingerprints, to support rapid certificate change in the event that the higher level root CA
212  *  gets compromised ([RFC 7469](https://tools.ietf.org/html/rfc7469)).
213  */
214 
wget_ssl_set_config_object(int key,void * value)215 void wget_ssl_set_config_object(int key, void *value)
216 {
217 	switch (key) {
218 	case WGET_SSL_OCSP_CACHE: config.ocsp_cert_cache = (wget_ocsp_db *)value; break;
219 	case WGET_SSL_SESSION_CACHE: config.tls_session_cache = (wget_tls_session_db *)value; break;
220 	case WGET_SSL_HPKP_CACHE: config.hpkp_cache = (wget_hpkp_db *)value; break;
221 	default: error_printf(_("Unknown config key %d (or value must not be an object)\n"), key);
222 	}
223 }
224 
225 /**
226  * \param[in] key An identifier for the config parameter (starting with `WGET_SSL_`)
227  * \param[in] value The value for the config parameter
228  *
229  * Set a configuration parameter, as an integer.
230  *
231  * These are the parameters that can be set (\p key can have any of these values):
232  *
233  *  - WGET_SSL_CHECK_CERTIFICATE: whether certificates should be verified (1) or not (0)
234  *  - WGET_SSL_CHECK_HOSTNAME: whether or not to check if the certificate's subject field
235  *  matches the peer's hostname. This check is done according to the rules in [RFC 6125](https://tools.ietf.org/html/rfc6125)
236  *  and typically involves checking whether the hostname and the common name (CN) field of the subject match.
237  *  - WGET_SSL_PRINT_INFO: whether or not information should be printed about the established SSL/TLS handshake (negotiated
238  *  ciphersuites, certificates, etc.). The default is no (0).
239  *
240  * The following three options all can take either `WGET_SSL_X509_FMT_PEM` (to specify the PEM format) or `WGET_SSL_X509_FMT_DER`
241  * (for the DER format). The default in for all of them is `WGET_SSL_X509_FMT_PEM`.
242  *
243  *  - WGET_SSL_CA_TYPE: Specifies what's the format of the root CA certificate(s) supplied with either `WGET_SSL_CA_DIRECTORY`
244  *  or `WGET_SSL_CA_FILE`.
245  *  - WGET_SSL_CERT_TYPE: Specifies what's the format of the certificate file supplied with `WGET_SSL_CERT_FILE`. **The certificate
246  *  and the private key supplied must both be of the same format.**
247  *  - WGET_SSL_KEY_TYPE: Specifies what's the format of the private key file supplied with `WGET_SSL_KEY_FILE`. **The private key
248  *  and the certificate supplied must both be of the same format.**
249  *
250  * The following two options control OCSP queries. These don't affect the CRL set with `WGET_SSL_CRL_FILE`, if any.
251  * If both CRLs and OCSP are enabled, both will be used.
252  *
253  *  - WGET_SSL_OCSP: whether or not OCSP should be used. The default is yes (1).
254  *  - WGET_SSL_OCSP_STAPLING: whether or not OCSP stapling should be used. The default is yes (1).
255  */
wget_ssl_set_config_int(int key,int value)256 void wget_ssl_set_config_int(int key, int value)
257 {
258 	switch (key) {
259 	case WGET_SSL_CHECK_CERTIFICATE: config.check_certificate = (char)value; break;
260 	case WGET_SSL_CHECK_HOSTNAME: config.check_hostname = (char)value; break;
261 	case WGET_SSL_CA_TYPE: config.ca_type = (char)value; break;
262 	case WGET_SSL_CERT_TYPE: config.cert_type = (char)value; break;
263 	case WGET_SSL_KEY_TYPE: config.key_type = (char)value; break;
264 	case WGET_SSL_PRINT_INFO: config.print_info = (char)value; break;
265 	case WGET_SSL_OCSP: config.ocsp = (char)value; break;
266 	case WGET_SSL_OCSP_STAPLING: config.ocsp_stapling = (char)value; break;
267 	default: error_printf(_("Unknown config key %d (or value must not be an integer)\n"), key);
268 	}
269 }
270 
271 /* This function will verify the peer's certificate, and check
272  * if the hostname matches, as well as the activation, expiration dates.
273  */
274 /*
275 static int verify_certificate_callback(gnutls_session_t session)
276 {
277 	unsigned int status, deinit_cert = 0, deinit_issuer = 0;
278 	const gnutls_datum_t *cert_list = 0;
279 	unsigned int cert_list_size;
280 	int ret = -1, err, ocsp_ok = 0, pinning_ok = 0;
281 	gnutls_x509_crt_t cert = NULL, issuer = NULL;
282 	const char *hostname;
283 	const char *tag = config.check_certificate ? _("ERROR") : _("WARNING");
284 	unsigned nvalid = 0, nrevoked = 0, nignored = 0;
285 
286 	// read hostname
287 	struct session_context *ctx = gnutls_session_get_ptr(session);
288 	hostname = ctx->hostname;
289 
290 	// This verification function uses the trusted CAs in the credentials
291 	// structure. So you must have installed one or more CA certificates.
292 	//
293 	if (gnutls_certificate_verify_peers3(session, hostname, &status) != GNUTLS_E_SUCCESS) {
294 //		if (wget_get_logger(WGET_LOGGER_DEBUG))
295 //			print_info(session);
296 		error_printf(_("%s: Certificate verification error\n"), tag);
297 		goto out;
298 	}
299 
300 //	if (wget_get_logger(WGET_LOGGER_DEBUG))
301 //		print_info(session);
302 
303 	if (status & GNUTLS_CERT_REVOKED) {
304 		if (config.ocsp_cert_cache)
305 			wget_ocsp_db_add_host(config.ocsp_cert_cache, hostname, 0); // remove entry from cache
306 		if (ctx->ocsp_stapling) {
307 			if (gnutls_x509_crt_init(&cert) == GNUTLS_E_SUCCESS) {
308 				if ((cert_list = gnutls_certificate_get_peers(session, &cert_list_size))) {
309 					if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) == GNUTLS_E_SUCCESS) {
310 						add_cert_to_ocsp_cache(cert, 0);
311 					}
312 				}
313 				gnutls_x509_crt_deinit(cert);
314 			}
315 		}
316 	}
317 
318 	if (status) {
319 		gnutls_datum_t out;
320 
321 		if (gnutls_certificate_verification_status_print(
322 			status, gnutls_certificate_type_get(session), &out, 0) == GNUTLS_E_SUCCESS)
323 		{
324 			error_printf("%s: %s\n", tag, out.data); // no translation
325 			gnutls_free(out.data);
326 		}
327 
328 		goto out;
329 	}
330 
331 	// Up to here the process is the same for X.509 certificates and
332 	// OpenPGP keys. From now on X.509 certificates are assumed. This can
333 	// be easily extended to work with openpgp keys as well.
334 	//
335 	if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) {
336 		error_printf(_("%s: Certificate must be X.509\n"), tag);
337 		goto out;
338 	}
339 
340 	if (gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS) {
341 		error_printf(_("%s: Error initializing X.509 certificate\n"), tag);
342 		goto out;
343 	}
344 	deinit_cert = 1;
345 
346 	if (!(cert_list = gnutls_certificate_get_peers(session, &cert_list_size))) {
347 		error_printf(_("%s: No certificate was found!\n"), tag);
348 		goto out;
349 	}
350 
351 	if ((err = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)) != GNUTLS_E_SUCCESS) {
352 		error_printf(_("%s: Failed to parse certificate: %s\n"), tag, gnutls_strerror (err));
353 		goto out;
354 	}
355 
356 	if (!config.check_hostname || (config.check_hostname && hostname && gnutls_x509_crt_check_hostname(cert, hostname)))
357 		ret = 0;
358 	else
359 		goto out;
360 
361 	// At this point, the cert chain has been found valid regarding the locally available CA certificates and CRLs.
362 	// Now, we are going to check the revocation status via OCSP
363 	if (config.ocsp_stapling) {
364 		if (!ctx->valid && ctx->ocsp_stapling) {
365 			if (gnutls_ocsp_status_request_is_checked(session, 0)) {
366 				debug_printf("Server certificate is valid regarding OCSP stapling\n");
367 //				get_cert_fingerprint(cert, fingerprint, sizeof(fingerprint)); // calc hexadecimal fingerprint string
368 				add_cert_to_ocsp_cache(cert, 1);
369 				nvalid = 1;
370 			}
371 			else if (gnutls_ocsp_status_request_is_checked(session, GNUTLS_OCSP_SR_IS_AVAIL))
372 				error_printf(_("WARNING: The certificate's (stapled) OCSP status is invalid\n"));
373 			else if (!config.ocsp)
374 				error_printf(_("WARNING: The certificate's (stapled) OCSP status has not been sent\n"));
375 		} else if (ctx->valid)
376 			debug_printf("OCSP: Host '%s' is valid (from cache)\n", hostname);
377 	}
378 
379 	for (unsigned it = 0; it < cert_list_size; it++) {
380 		gnutls_x509_crt_deinit(cert);
381 		gnutls_x509_crt_init(&cert);
382 
383 		if ((err = gnutls_x509_crt_import(cert, &cert_list[it], GNUTLS_X509_FMT_DER)) != GNUTLS_E_SUCCESS) {
384 			error_printf(_("%s: Failed to parse certificate[%u]: %s\n"), tag, it, gnutls_strerror (err));
385 			continue;
386 		}
387 
388 		if (cert_verify_hpkp(cert, hostname, session) == 0)
389 			pinning_ok = 1;
390 
391 		cert_verify_hpkp(cert, hostname, session);
392 
393 		if (config.ocsp && it > nvalid) {
394 			char fingerprint[64 * 2 +1];
395 			int revoked;
396 
397 			get_cert_fingerprint(cert, fingerprint, sizeof(fingerprint)); // calc hexadecimal fingerprint string
398 
399 			if (wget_ocsp_fingerprint_in_cache(config.ocsp_cert_cache, fingerprint, &revoked)) {
400 				// found cert's fingerprint in cache
401 				if (revoked) {
402 					debug_printf("Certificate[%u] of '%s' has been revoked (cached)\n", it, hostname);
403 					nrevoked++;
404 				} else {
405 					debug_printf("Certificate[%u] of '%s' is valid (cached)\n", it, hostname);
406 					nvalid++;
407 				}
408 				continue;
409 			}
410 
411 			if (deinit_issuer) {
412 				gnutls_x509_crt_deinit(issuer);
413 				deinit_issuer = 0;
414 			}
415 			if ((err = gnutls_certificate_get_issuer(credentials, cert, &issuer, 0)) != GNUTLS_E_SUCCESS && it < cert_list_size - 1) {
416 				gnutls_x509_crt_init(&issuer);
417 				deinit_issuer = 1;
418 				if ((err = gnutls_x509_crt_import(issuer, &cert_list[it + 1], GNUTLS_X509_FMT_DER))  != GNUTLS_E_SUCCESS) {
419 					debug_printf("Decoding error: %s\n", gnutls_strerror(err));
420 					continue;
421 				}
422 			} else if (err  != GNUTLS_E_SUCCESS) {
423 				debug_printf("Cannot find issuer: %s\n", gnutls_strerror(err));
424 				continue;
425 			}
426 
427 			ocsp_ok = cert_verify_ocsp(cert, issuer);
428 			debug_printf("check_ocsp_response() returned %d\n", ocsp_ok);
429 
430 			if (ocsp_ok == 1) {
431 				debug_printf("Certificate[%u] of '%s' is valid (via OCSP)\n", it, hostname);
432 				wget_ocsp_db_add_fingerprint(config.ocsp_cert_cache, fingerprint, time(NULL) + 3600, 1); // 1h valid
433 				nvalid++;
434 			} else if (ocsp_ok == 0) {
435 				debug_printf("%s: Certificate[%u] of '%s' has been revoked (via OCSP)\n", tag, it, hostname);
436 				wget_ocsp_db_add_fingerprint(config.ocsp_cert_cache, fingerprint, time(NULL) + 3600, 0);  // cert has been revoked
437 				nrevoked++;
438 			} else {
439 				debug_printf("WARNING: OCSP response not available or ignored\n");
440 				nignored++;
441 			}
442 		}
443 	}
444 
445 	if (config.ocsp && stats_callback_ocsp) {
446 		wget_ocsp_stats_data stats;
447 		stats.hostname = hostname;
448 		stats.nvalid = nvalid;
449 		stats.nrevoked = nrevoked;
450 		stats.nignored = nignored;
451 		stats.stapling = ctx->ocsp_stapling;
452 
453 		stats_callback_ocsp(&stats);
454 	}
455 
456 	if (config.ocsp_stapling || config.ocsp) {
457 		if (nvalid == cert_list_size) {
458 			wget_ocsp_db_add_host(config.ocsp_cert_cache, hostname, time(NULL) + 3600); // 1h valid
459 		} else if (nrevoked) {
460 			wget_ocsp_db_add_host(config.ocsp_cert_cache, hostname, 0); // remove entry from cache
461 			ret = -1;
462 		}
463 	}
464 
465 	if (!pinning_ok) {
466 		error_printf(_("%s: Pubkey pinning mismatch!\n"), tag);
467 		ret = -1;
468 	}
469 
470 	// 0: continue handshake
471 	// else: stop handshake
472 out:
473 	if (deinit_cert)
474 		gnutls_x509_crt_deinit(cert);
475 	if (deinit_issuer)
476 		gnutls_x509_crt_deinit(issuer);
477 
478 	return config.check_certificate ? ret : 0;
479 }
480 */
481 
482 static int init;
483 static wget_thread_mutex mutex;
484 
tls_init(void)485 static void __attribute__ ((constructor)) tls_init(void)
486 {
487 	if (!mutex)
488 		wget_thread_mutex_init(&mutex);
489 }
490 
tls_exit(void)491 static void __attribute__ ((destructor)) tls_exit(void)
492 {
493 	if (mutex)
494 		wget_thread_mutex_destroy(&mutex);
495 }
496 
497 /*
498 static void set_credentials(gnutls_certificate_credentials_t *credentials)
499 {
500 	if (config.cert_file && !config.key_file) {
501 		// Use the private key from the cert file unless otherwise specified.
502 		config.key_file = config.cert_file;
503 		config.key_type = config.cert_type;
504 	}
505 	else if (!config.cert_file && config.key_file) {
506 		// Use the cert from the private key file unless otherwise specified.
507 		config.cert_file = config.key_file;
508 		config.cert_type = config.key_type;
509 	}
510 
511 	if (config.cert_file && config.key_file) {
512 		if (config.key_type !=config.cert_type) {
513 			// GnuTLS can't handle this
514 			error_printf(_("GnuTLS requires the key and the cert to be of the same type.\n"));
515 		}
516 
517 		if (gnutls_certificate_set_x509_key_file(*credentials,config.cert_file,config.key_file,key_type(config.key_type)) != GNUTLS_E_SUCCESS)
518 			error_printf(_("No certificates or keys were found\n"));
519 	}
520 
521 	if (config.ca_file) {
522 		if (gnutls_certificate_set_x509_trust_file(*credentials, config.ca_file, key_type(config.ca_type)) <= 0)
523 			error_printf(_("No CAs were found in '%s'\n"), config.ca_file);
524 	}
525 }
526 */
527 
528 /**
529  * Initialize the SSL/TLS engine as a client.
530  *
531  * This function assumes the caller is an SSL client connecting to a server.
532  * The functions wget_ssl_open(), wget_ssl_close() and wget_ssl_deinit() can be called
533  * after this.
534  *
535  * This is where the root certificates get loaded from the folder specified in the
536  * `WGET_SSL_CA_DIRECTORY` parameter. If any of the files in that folder cannot be loaded
537  * for whatever reason, that file will be silently skipped without harm (a message will be
538  * printed to the debug log stream).
539  *
540  * CLRs and private keys and their certificates are also loaded here.
541  *
542  * On systems with automatic library constructors/destructors, this function
543  * is thread-safe. On other systems it is not thread-safe.
544  *
545  * This function may be called several times. Only the first call really
546  * takes action.
547  */
wget_ssl_init(void)548 void wget_ssl_init(void)
549 {
550 	tls_init();
551 
552 	wget_thread_mutex_lock(mutex);
553 
554 	if (!init) {
555 		WOLFSSL_METHOD *method;
556 		int min_version = -1;
557 		const char *ciphers = NULL;
558 
559 		debug_printf("WolfSSL init\n");
560 		wolfSSL_Init();
561 
562 		if (!wget_strcasecmp_ascii(config.secure_protocol, "SSLv2")) {
563 			method = SSLv2_client_method();
564 		} else if (!wget_strcasecmp_ascii(config.secure_protocol, "SSLv3")) {
565 			method = wolfSSLv23_client_method();
566 			min_version = WOLFSSL_SSLV3;
567 		} else if (!wget_strcasecmp_ascii(config.secure_protocol, "TLSv1")) {
568 			method = wolfSSLv23_client_method();
569 			min_version = WOLFSSL_TLSV1;
570 		} else if (!wget_strcasecmp_ascii(config.secure_protocol, "TLSv1_1")) {
571 			method = wolfSSLv23_client_method();
572 			min_version = WOLFSSL_TLSV1_1;
573 		} else if (!wget_strcasecmp_ascii(config.secure_protocol, "TLSv1_2")) {
574 			method = wolfSSLv23_client_method();
575 			min_version = WOLFSSL_TLSV1_2;
576 		} else if (!wget_strcasecmp_ascii(config.secure_protocol, "TLSv1_3")) {
577 			method = wolfSSLv23_client_method();
578 			min_version = WOLFSSL_TLSV1_3;
579 		} else if (!wget_strcasecmp_ascii(config.secure_protocol, "PFS")) {
580 			method = wolfSSLv23_client_method();
581 			ciphers = "HIGH:!aNULL:!RC4:!MD5:!SRP:!PSK:!kRSA";
582 		} else if (!wget_strcasecmp_ascii(config.secure_protocol, "auto")) {
583 			method = wolfSSLv23_client_method();
584 			min_version = WOLFSSL_TLSV1_2;
585 			ciphers = "HIGH:!aNULL:!RC4:!MD5:!SRP:!PSK";
586 		} else if (*config.secure_protocol) {
587 			method = wolfSSLv23_client_method();
588 			ciphers = config.secure_protocol;
589 		} else {
590 			error_printf(_("Missing TLS method\n"));
591 			goto out;
592 		}
593 
594 		/* Create and initialize WOLFSSL_CTX */
595 		if ((ssl_ctx = wolfSSL_CTX_new(method)) == NULL) {
596 			error_printf(_("Failed to create WOLFSSL_CTX\n"));
597 			goto out;
598 		}
599 
600 		if (min_version != -1)
601 			wolfSSL_CTX_SetMinVersion(ssl_ctx, min_version);
602 
603 /*
604 		int rc;
605 		char cipher_list[8096];
606 		rc = wolfSSL_get_ciphers(cipher_list, (int) sizeof(cipher_list));
607 		debug_printf("%d ciphers found %s (len=%zu)\n", rc, cipher_list, strlen(cipher_list));
608 */
609 		if (ciphers)
610 			if (!wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers))
611 				error_printf(_("WolfSSL: Failed to set ciphers '%s'\n"), ciphers);
612 
613 		if (config.check_certificate) {
614 			if (!wget_strcmp(config.ca_directory, "system"))
615 				config.ca_directory = "/etc/ssl/certs";
616 
617 			/* Load client certificates into WOLFSSL_CTX */
618 			if (wolfSSL_CTX_load_verify_locations(ssl_ctx, config.ca_file, config.ca_directory) != SSL_SUCCESS) {
619 				error_printf(_("Failed to load %s, please check the file.\n"), config.ca_directory);
620 				goto out;
621 			}
622 		} else {
623 			wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
624 		}
625 
626 /*		if (config.crl_file) {
627 			WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(ssl_ctx);
628 			WOLFSSL_X509_LOOKUP *lookup;
629 
630 			if (!(lookup = wolfSSL_X509_STORE_add_lookup(store, wolfSSL_X509_LOOKUP_file()))
631 				|| (!X509_load_crl_file(lookup, config.crl_file, X509_FILETYPE_PEM)))
632 				return;
633 
634 			wolfSSL_X509_STORE_set_flags(store, WOLFSSL_CRL_CHECK | WOLFSSL_CRL_CHECKALL);
635 		}
636 */
637 		debug_printf("Certificates loaded\n");
638 
639 out:
640 		init++;
641 
642 		debug_printf("WolfSSL init done\n");
643 	}
644 
645 	wget_thread_mutex_unlock(mutex);
646 }
647 
648 /**
649  * Deinitialize the SSL/TLS engine, after it has been initialized
650  * with wget_ssl_init().
651  *
652  * This function unloads everything that was loaded in wget_ssl_init().
653  *
654  * On systems with automatic library constructors/destructors, this function
655  * is thread-safe. On other systems it is not thread-safe.
656  *
657  * This function may be called several times. Only the last deinit really
658  * takes action.
659  */
wget_ssl_deinit(void)660 void wget_ssl_deinit(void)
661 {
662 	wget_thread_mutex_lock(mutex);
663 
664 	if (init == 1) {
665 		wolfSSL_CTX_free(ssl_ctx); ssl_ctx = NULL;
666 		wolfSSL_Cleanup();
667 	}
668 
669 	if (init > 0) init--;
670 
671 	wget_thread_mutex_unlock(mutex);
672 }
673 
do_handshake(WOLFSSL * session,int sockfd,int timeout)674 static int do_handshake(WOLFSSL *session, int sockfd, int timeout)
675 {
676 	int ret;
677 
678 	// Wait for socket being ready before we call gnutls_handshake().
679 	// I had problems on a KVM Win7 + CygWin (gnutls 3.2.4-1).
680 	int rc = wget_ready_2_write(sockfd, timeout);
681 
682 	if (rc == 0)
683 		ret = WGET_E_TIMEOUT;
684 	else
685 		ret = WGET_E_HANDSHAKE;
686 
687 	// Perform the TLS handshake
688 	while (rc > 0) {
689 		rc = wolfSSL_connect(session);
690 
691 		if (rc == SSL_SUCCESS) {
692 			ret = WGET_E_SUCCESS;
693 			break;
694 		}
695 
696 		rc =  wolfSSL_get_error(session, rc);
697 		debug_printf("wolfSSL_connect2: (%d) (errno=%d) %s\n", rc, errno, wolfSSL_ERR_reason_error_string(rc));
698 
699 /*			if (rc == GNUTLS_E_CERTIFICATE_ERROR) {
700 				ret = WGET_E_CERTIFICATE;
701 			} else if (rc == GNUTLS_E_PUSH_ERROR && (errno == ECONNREFUSED || errno == ENOTCONN)) {
702 				// ECONNREFUSED: on Linux
703 				// ENOTCONN: MinGW (in out Gitlab CI runner)
704 				ret = WGET_E_CONNECT;
705 			} else if (rc == GNUTLS_E_PULL_ERROR && errno == 61) {
706 				// ENODATA, but not on OSX/Travis ?
707 				// We see this with older versions of GnuTLS, e.g. on TravisCI. (Tim, 11.4.2018)
708 				// It happens when trying to connect to a port without a listener
709 				ret = WGET_E_CONNECT;
710 			} else if (rc == GNUTLS_E_PREMATURE_TERMINATION && errno == EAGAIN) {
711 				// It happens when trying to connect to a closed port
712 				ret = WGET_E_CONNECT;
713 			} else if (rc == GNUTLS_E_UNEXPECTED_PACKET_LENGTH && errno == EAGAIN) {
714 				// We see this with older versions of GnuTLS, e.g. on TravisCI. (Tim, 11.4.2018)
715 				// It happens when trying to connect to a port without a listener
716 				ret = WGET_E_CONNECT;
717 			} else
718 				ret = WGET_E_HANDSHAKE;
719 */
720 		if (rc == WOLFSSL_ERROR_WANT_WRITE) {
721 			// wait for writeability
722 			rc = wget_ready_2_write(sockfd, timeout);
723 		} else if (rc == WOLFSSL_ERROR_WANT_READ) {
724 			// wait for readability
725 			rc = wget_ready_2_read(sockfd, timeout);
726 		} else {
727 			ret = WGET_E_CONNECT;
728 			break;
729 		}
730 	}
731 
732 	return ret;
733 }
734 
ShowX509(WOLFSSL_X509 * x509,const char * hdr)735 static void ShowX509(WOLFSSL_X509 *x509, const char *hdr)
736 {
737 	char *altName;
738 	char *issuer;
739 	char *subject;
740 	byte serial[32];
741 	int ret;
742 	int sz = sizeof(serial);
743 
744 	if (!x509) {
745 		debug_printf("%s No Cert\n", hdr);
746 		return;
747 	}
748 
749 	issuer = wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_issuer_name(x509), 0, 0);
750 	subject = wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(x509), 0, 0);
751 
752 	debug_printf("%s issuer : %s subject: %s", hdr, issuer, subject);
753 
754 	while ((altName = wolfSSL_X509_get_next_altname(x509)))
755 		debug_printf(" altname = %s\n", altName);
756 
757 	ret = wolfSSL_X509_get_serial_number(x509, serial, &sz);
758 	if (ret == WOLFSSL_SUCCESS) {
759 		int i;
760 		int strLen;
761 		char serialMsg[80];
762 
763 		/* testsuite has multiple threads writing to stdout, get output
764 			message ready to write once */
765 		strLen = sprintf(serialMsg, " serial number");
766 		for (i = 0; i < sz; i++)
767 			sprintf(serialMsg + strLen + (i * 3), ":%02x ", serial[i]);
768 		debug_printf("%s\n", serialMsg);
769 	}
770 
771 	XFREE(subject, 0, DYNAMIC_TYPE_OPENSSL)
772 	XFREE(issuer, 0, DYNAMIC_TYPE_OPENSSL)
773 
774 	{
775 		WOLFSSL_BIO* bio;
776 		char buf[256]; /* should be size of ASN_NAME_MAX */
777 		int textSz;
778 
779 
780 		/* print out domain component if certificate has it */
781 		textSz = wolfSSL_X509_NAME_get_text_by_NID(
782 			wolfSSL_X509_get_subject_name(x509), NID_domainComponent,
783 			buf, sizeof(buf));
784 		if (textSz > 0) {
785 			debug_printf("Domain Component = %s\n", buf);
786 		}
787 
788 		bio = wolfSSL_BIO_new(wolfSSL_BIO_s_file());
789 		if (bio) {
790 			wolfSSL_BIO_set_fp(bio, stdout, BIO_NOCLOSE);
791 			wolfSSL_X509_print(bio, x509);
792 			wolfSSL_BIO_free(bio);
793 		}
794 	}
795 }
796 
ShowX509Chain(WOLFSSL_X509_CHAIN * chain,int count,const char * hdr)797 static void ShowX509Chain(WOLFSSL_X509_CHAIN *chain, int count, const char *hdr)
798 {
799 //	int i;
800 //	int length;
801 //	unsigned char buffer[3072];
802 
803 	for (int i = 0; i < count; i++) {
804 //		wolfSSL_get_chain_cert_pem(chain, i, buffer, sizeof(buffer), &length);
805 //		buffer[length] = 0;
806 //		debug_printf("\n%s: %d has length %d data = \n%s\n", hdr, i, length, buffer);
807 
808 		WOLFSSL_X509 *chainX509 = wolfSSL_get_chain_X509(chain, i);
809 		if (chainX509)
810 			ShowX509(chainX509, hdr);
811 
812 		wolfSSL_FreeX509(chainX509);
813 	}
814 }
815 
816 /**
817  * \param[in] tcp A TCP connection (see wget_tcp_init())
818  * \return `WGET_E_SUCCESS` on success or an error code (`WGET_E_*`) on failure
819  *
820  * Run an SSL/TLS handshake.
821  *
822  * This functions establishes an SSL/TLS tunnel (performs an SSL/TLS handshake)
823  * over an active TCP connection. A pointer to the (internal) SSL/TLS session context
824  * can be found in `tcp->ssl_session` after successful execution of this function. This pointer
825  * has to be passed to wget_ssl_close() to close the SSL/TLS tunnel.
826  *
827  * If the handshake cannot be completed in the specified timeout for the provided TCP connection
828  * this function fails and returns `WGET_E_TIMEOUT`. You can set the timeout with wget_tcp_set_timeout().
829  */
wget_ssl_open(wget_tcp * tcp)830 int wget_ssl_open(wget_tcp *tcp)
831 {
832 	WOLFSSL *session;
833 	wget_tls_stats_data stats = {
834 			.alpn_protocol = NULL,
835 			.version = -1,
836 			.false_start = -1,
837 			.tfo = -1,
838 			.resumed = 0,
839 			.http_protocol = WGET_PROTOCOL_HTTP_1_1,
840 			.cert_chain_size = 0
841 	};
842 
843 	int rc, ret = WGET_E_UNKNOWN;
844 	int sockfd, connect_timeout;
845 	const char *hostname;
846 	long long before_millisecs = 0;
847 
848 	if (!tcp)
849 		return WGET_E_INVALID;
850 
851 	if (!init)
852 		wget_ssl_init();
853 
854 	hostname = tcp->ssl_hostname;
855 	sockfd= tcp->sockfd;
856 	connect_timeout = tcp->connect_timeout;
857 
858 	if ((session = wolfSSL_new(ssl_ctx)) == NULL) {
859 		error_printf(_("Failed to create WolfSSL session\n"));
860 		return -1;
861 	}
862 
863 	// RFC 6066 SNI Server Name Indication
864 	if (hostname)
865 		wolfSSL_UseSNI(session, WOLFSSL_SNI_HOST_NAME, hostname, (unsigned short) strlen(hostname));
866 
867 //	if (tcp->tls_false_start)
868 //		info_printf(_("WolfSSL doesn't support TLS False Start\n"));
869 
870 	if (config.alpn) {
871 		size_t len = strlen(config.alpn);
872 		char alpnbuf[256], *alpn;
873 
874 		// wolfSSL_UseALPN() destroys the ALPN string (bad design pattern !)
875 		alpn = wget_strmemcpy_a(alpnbuf, sizeof(alpnbuf), config.alpn, strlen(config.alpn));
876 
877 		if (wolfSSL_UseALPN(session, alpn, (int) len, WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) == WOLFSSL_SUCCESS) {
878 			debug_printf("ALPN offering %s\n", config.alpn);
879 		} else
880 			debug_printf("WolfSSL: Failed to set ALPN: %s\n", config.alpn);
881 
882 		if (alpn != alpnbuf)
883 			xfree(alpn);
884 	}
885 
886 	struct session_context *ctx = wget_calloc(1, sizeof(struct session_context));
887 	ctx->hostname = wget_strdup(hostname);
888 
889 	tcp->ssl_session = session;
890 //	gnutls_session_set_ptr(session, ctx);
891 	wolfSSL_set_fd(session, sockfd);
892 
893 	/* make wolfSSL object nonblocking */
894 	wolfSSL_set_using_nonblock(session, 1);
895 
896 	if (tls_stats_callback)
897 		before_millisecs = wget_get_timemillis();
898 
899 	ret = do_handshake(session, sockfd, connect_timeout);
900 
901 	if (tls_stats_callback) {
902 		long long after_millisecs = wget_get_timemillis();
903 		stats.tls_secs = after_millisecs - before_millisecs;
904 		stats.tls_con = 1;
905 		stats.false_start = 0; // WolfSSL doesn't support False Start (https://www.wolfssl.com/is-tls-false-start-going-to-take-off-2/)
906 	}
907 
908 	const char *name;
909 	int bits;
910 	WOLFSSL_CIPHER *cipher;
911 	WOLFSSL_X509 *peer = wolfSSL_get_peer_certificate(session);
912 	if (peer) {
913 		ShowX509(peer, "Peer's cert info");
914 		wolfSSL_FreeX509(peer);
915 	} else
916 		debug_printf("Peer has no cert!\n");
917 
918 	ShowX509(wolfSSL_get_certificate(session), "our cert info:");
919 	debug_printf("Peer verify result = %ld\n", wolfSSL_get_verify_result(session));
920 	debug_printf("SSL version %s\n", wolfSSL_get_version(session));
921 	cipher = wolfSSL_get_current_cipher(session);
922 //	printf("%s %s%s\n", words[1], (wolfSSL_isQSH(session)) ? "QSH:" : "", wolfSSL_CIPHER_get_name(cipher));
923 	debug_printf("SSL cipher suite %s\n", wolfSSL_CIPHER_get_name(cipher));
924 	if ((name = wolfSSL_get_curve_name(session)))
925 		debug_printf("SSL curve name %s\n", name);
926 	else if ((bits = wolfSSL_GetDhKey_Sz(session)) > 0)
927 		debug_printf("SSL DH size %d bits\n", bits);
928 
929 	if (config.alpn) {
930 		char *protocol;
931 		uint16_t protocol_length;
932 
933 		if (wolfSSL_ALPN_GetProtocol(session, &protocol, &protocol_length) != WOLFSSL_SUCCESS)
934 			debug_printf("WolfSSL: Failed to connect ALPN\n");
935 		else {
936 			debug_printf("WolfSSL: Server accepted ALPN protocol '%.*s'\n", (int) protocol_length, protocol);
937 			stats.alpn_protocol = wget_strmemdup(protocol, protocol_length);
938 
939 			if (protocol_length == 2 && !memcmp(protocol, "h2", 2)) {
940 				tcp->protocol = WGET_PROTOCOL_HTTP_2_0;
941 				stats.http_protocol = WGET_PROTOCOL_HTTP_2_0;
942 			}
943 		}
944 	}
945 
946 	if (ret == WGET_E_SUCCESS) {
947 		int resumed = wolfSSL_session_reused(session);
948 
949 		WOLFSSL_X509_CHAIN *chain = (WOLFSSL_X509_CHAIN *) wolfSSL_get_peer_cert_chain(session);
950 		ShowX509Chain(chain, wolfSSL_get_chain_count(chain), "Certificate chain");
951 
952 		if (tls_stats_callback) {
953 			stats.resumed = resumed;
954 			stats.cert_chain_size = wolfSSL_get_chain_count(chain);
955 
956 			const char *tlsver = wolfSSL_get_version(session);
957 			if (!strcmp(tlsver, "TLSv1.2"))
958 				stats.version = 4;
959 			else if (!strcmp(tlsver, "TLSv1.3"))
960 				stats.version = 5;
961 			else
962 				stats.version = 1; // SSLv3
963 		}
964 
965 		debug_printf("Handshake completed%s\n", resumed ? " (resumed session)" : "");
966 
967 		if (!resumed && config.tls_session_cache) {
968 /*			WOLFSSL_SESSION *session_data = wolfSSL_get_session(session);
969 
970 			if (session_data) {
971 				int session_data_size = wolfSSL_get_session_cache_memsize();
972 				char session_data_data[session_data_size];
973 				if (wolfSSL_memsave_session_cache(session_data_data, session_data_size) == SSL_SUCCESS) {
974 					wget_tls_session_db_add(config.tls_session_cache,
975 						wget_tls_session_new(ctx->hostname, 18 * 3600, session_data.data, session_data.size)); // 18h valid
976 				}
977 			}
978 */
979 /*			gnutls_datum_t session_data;
980 
981 			if ((rc = gnutls_session_get_data2(session, &session_data)) == GNUTLS_E_SUCCESS) {
982 				wget_tls_session_db_add(config.tls_session_cache,
983 					wget_tls_session_new(ctx->hostname, 18 * 3600, session_data.data, session_data.size)); // 18h valid
984 				gnutls_free(session_data.data);
985 			} else
986 				debug_printf("Failed to get session data: %s", gnutls_strerror(rc));
987 */		}
988 	}
989 
990 	if ((rc = wolfSSL_connect(session)) != WOLFSSL_SUCCESS) {
991 		rc = wolfSSL_get_error(session, rc);
992 		error_printf(_("failed to connect TLS (%d): %s\n"), rc, wolfSSL_ERR_reason_error_string(rc));
993 
994 		long res = wolfSSL_get_verify_result(session);
995 		if (res >= 13 && res <= 29)
996 			return WGET_E_CERTIFICATE;
997 		else
998 			return WGET_E_CONNECT;
999 	}
1000 
1001 	if (tls_stats_callback) {
1002 		stats.hostname = hostname;
1003 		tls_stats_callback(&stats, tls_stats_ctx);
1004 		xfree(stats.alpn_protocol);
1005 	}
1006 
1007 	tcp->hpkp = ctx->stats_hpkp;
1008 
1009 	if (ret != WGET_E_SUCCESS) {
1010 		if (ret == WGET_E_TIMEOUT)
1011 			debug_printf("Handshake timed out\n");
1012 		xfree(ctx->hostname);
1013 		xfree(ctx);
1014 		wolfSSL_free(session);
1015 		tcp->ssl_session = NULL;
1016 	}
1017 
1018 	return ret;
1019 }
1020 
1021 /**
1022  * \param[in] session The SSL/TLS session (a pointer to it), which is located at the `ssl_session` field
1023  * of the TCP connection (see wget_ssl_open()).
1024  *
1025  * Close an active SSL/TLS tunnel, which was opened with wget_ssl_open().
1026  *
1027  * The underlying TCP connection is kept open.
1028  */
wget_ssl_close(void ** session)1029 void wget_ssl_close(void **session)
1030 {
1031 	if (session && *session) {
1032 		WOLFSSL *s = *session;
1033 		int ret;
1034 
1035 		do {
1036 			ret = wolfSSL_shutdown(s);
1037 			ret = wolfSSL_get_error(s, ret);
1038 		} while (ret == WOLFSSL_SHUTDOWN_NOT_DONE);
1039 
1040 		if (ret < 0)
1041 			debug_printf("TLS shutdown failed: %s\n", wolfSSL_ERR_reason_error_string(ret));
1042 
1043 		wolfSSL_free(s);
1044 		*session = NULL;
1045 	}
1046 }
1047 
1048 /**
1049  * \param[in] session An opaque pointer to the SSL/TLS session (obtained with wget_ssl_open() or wget_ssl_server_open())
1050  * \param[in] buf Destination buffer where the read data will be placed
1051  * \param[in] count Length of the buffer \p buf
1052  * \param[in] timeout The amount of time to wait until data becomes available (in milliseconds)
1053  * \return The number of bytes read, or a negative value on error.
1054  *
1055  * Read data from the SSL/TLS tunnel.
1056  *
1057  * This function will read at most \p count bytes, which will be stored
1058  * in the buffer \p buf.
1059  *
1060  * The \p timeout parameter tells how long to wait until some data becomes
1061  * available to read. A \p timeout value of zero causes this function to return
1062  * immediately, whereas a negative value will cause it to wait indefinitely.
1063  * This function returns the number of bytes read, which may be zero if the timeout elapses
1064  * without any data having become available.
1065  *
1066  * If a rehandshake is needed, this function does it automatically and tries
1067  * to read again.
1068  */
wget_ssl_read_timeout(void * session,char * buf,size_t count,int timeout)1069 ssize_t wget_ssl_read_timeout(void *session, char *buf, size_t count, int timeout)
1070 {
1071 	int sockfd = wolfSSL_get_fd(session);
1072 	int rc;
1073 
1074 	while ((rc = wolfSSL_read(session, buf, (int) count)) < 0) {
1075 		rc =  wolfSSL_get_error(session, rc);
1076 		debug_printf("wolfSSL_read: (%d) (errno=%d) %s\n", rc, errno, wolfSSL_ERR_reason_error_string(rc));
1077 		if (rc == SSL_ERROR_WANT_READ) {
1078 			if ((rc = wget_ready_2_read(sockfd, timeout)) <= 0)
1079 				break;
1080 		} else
1081 			break;
1082 	}
1083 
1084 	return rc < 0 ? -1 : rc;
1085 
1086 /*	for (;;) {
1087 		int rc;
1088 
1089 		if (gnutls_record_check_pending(session) <= 0 && (rc = wget_ready_2_read(sockfd, timeout)) <= 0)
1090 			return rc;
1091 
1092 		nbytes = gnutls_record_recv(session, buf, count);
1093 
1094 		// If False Start + Session Resumption are enabled, we get the session data after the first read()
1095 		struct session_context *ctx = gnutls_session_get_ptr(session);
1096 		if (ctx && ctx->delayed_session_data) {
1097 			gnutls_datum_t session_data;
1098 
1099 			if ((rc = gnutls_session_get_data2(session, &session_data)) == GNUTLS_E_SUCCESS) {
1100 				debug_printf("Got delayed session data\n");
1101 				ctx->delayed_session_data = 0;
1102 				wget_tls_session_db_add(config.tls_session_cache,
1103 					wget_tls_session_new(ctx->hostname, 18 * 3600, session_data.data, session_data.size)); // 18h valid
1104 				gnutls_free(session_data.data);
1105 			} else
1106 				debug_printf("No delayed session data%s\n", gnutls_strerror(rc));
1107 		}
1108 
1109 		if (nbytes == GNUTLS_E_REHANDSHAKE) {
1110 			debug_printf("*** REHANDSHAKE while reading\n");
1111 			if ((nbytes = do_handshake(session, sockfd, timeout)) == 0)
1112 				nbytes = GNUTLS_E_AGAIN; // restart reading
1113 		}
1114 		if (nbytes >= 0 || nbytes != GNUTLS_E_AGAIN)
1115 			break;
1116 	}
1117 
1118 	return nbytes < -1 ? -1 : nbytes;
1119 */
1120 }
1121 
1122 /**
1123  * \param[in] session An opaque pointer to the SSL/TLS session (obtained with wget_ssl_open() or wget_ssl_server_open())
1124  * \param[in] buf Buffer with the data to be sent
1125  * \param[in] count Length of the buffer \p buf
1126  * \param[in] timeout The amount of time to wait until data can be sent to the wire (in milliseconds)
1127  * \return The number of bytes written, or a negative value on error.
1128  *
1129  * Send data through the SSL/TLS tunnel.
1130  *
1131  * This function will write \p count bytes from \p buf.
1132  *
1133  * The \p timeout parameter tells how long to wait until data can be finally sent
1134  * over the SSL/TLS tunnel. A \p timeout value of zero causes this function to return
1135  * immediately, whereas a negative value will cause it to wait indefinitely.
1136  * This function returns the number of bytes sent, which may be zero if the timeout elapses
1137  * before any data could be sent.
1138  *
1139  * If a rehandshake is needed, this function does it automatically and tries
1140  * to write again.
1141  */
wget_ssl_write_timeout(void * session,const char * buf,size_t count,int timeout)1142 ssize_t wget_ssl_write_timeout(void *session, const char *buf, size_t count, int timeout)
1143 {
1144 	int sockfd = wolfSSL_get_fd(session);
1145 	int rc;
1146 
1147 	while ((rc = wolfSSL_write(session, buf, (int) count)) < 0) {
1148 		rc =  wolfSSL_get_error(session, rc);
1149 		debug_printf("wolfSSL_write: (%d) (errno=%d) %s\n", rc, errno, wolfSSL_ERR_reason_error_string(rc));
1150 		if (rc == SSL_ERROR_WANT_WRITE) {
1151 			if ((rc = wget_ready_2_write(sockfd, timeout)) <= 0)
1152 				break;
1153 		} else
1154 			break;
1155 	}
1156 
1157 	return rc < 0 ? -1 : rc;
1158 /*
1159 	for (;;) {
1160 		ssize_t nbytes;
1161 		int rc;
1162 
1163 		if ((rc = wget_ready_2_write(sockfd, timeout)) <= 0)
1164 			return rc;
1165 
1166 		if ((nbytes = gnutls_record_send(session, buf, count)) >= 0)
1167 			return nbytes;
1168 
1169 		if (nbytes == GNUTLS_E_REHANDSHAKE) {
1170 			debug_printf("*** REHANDSHAKE while writing\n");
1171 			if ((nbytes = do_handshake(session, sockfd, timeout)) == 0)
1172 				continue; // restart writing
1173 		}
1174 		if (nbytes == GNUTLS_E_AGAIN)
1175 			return 0; // indicate timeout
1176 
1177 		return -1;
1178 	}
1179 */
1180 }
1181 
1182 /**
1183  * \param[in] fn A `wget_ssl_stats_callback_tls_t` callback function to receive TLS statistics data
1184  * \param[in] ctx Context data given to \p fn
1185  *
1186  * Set callback function to be called when TLS statistics are available
1187  */
wget_ssl_set_stats_callback_tls(wget_tls_stats_callback * fn,void * ctx)1188 void wget_ssl_set_stats_callback_tls(wget_tls_stats_callback *fn, void *ctx)
1189 {
1190 	tls_stats_callback = fn;
1191 	tls_stats_ctx = ctx;
1192 }
1193 
1194 /**
1195  * \param[in] fn A `wget_ssl_stats_callback_ocsp_t` callback function to receive OCSP statistics data
1196  * \param[in] ctx Context data given to \p fn
1197  *
1198  * Set callback function to be called when OCSP statistics are available
1199  */
wget_ssl_set_stats_callback_ocsp(wget_ocsp_stats_callback * fn,void * ctx)1200 void wget_ssl_set_stats_callback_ocsp(wget_ocsp_stats_callback *fn, void *ctx)
1201 {
1202 	ocsp_stats_callback = fn;
1203 	ocsp_stats_ctx = ctx;
1204 }
1205 
1206 /** @} */
1207