Dillo v3.2.0-88-g47ab7c70
Loading...
Searching...
No Matches
tls_openssl.c
Go to the documentation of this file.
1/*
2 * File: tls_openssl.c
3 *
4 * Copyright 2004 Garrett Kajmowicz <gkajmowi@tbaytel.net>
5 * (for some bits derived from the https dpi, e.g., certificate handling)
6 * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
7 * 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
8 * (for the certificate hostname checking from wget)
9 * Copyright (C) 2011 Benjamin Johnson <obeythepenguin@users.sourceforge.net>
10 * (for the https code offered from dplus browser that formed the basis...)
11 * Copyright (C) 2023-2025 Rodrigo Arias Mallo <rodarima@gmail.com>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * As a special exception, permission is granted to link Dillo with the OpenSSL
19 * or LibreSSL library, and distribute the linked executables without
20 * including the source code for OpenSSL or LibreSSL in the source
21 * distribution. You must obey the GNU General Public License, version 3, in
22 * all respects for all of the code used other than OpenSSL or LibreSSL.
23 */
24
25/* https://www.ssllabs.com/ssltest/viewMyClient.html
26 * https://github.com/lgarron/badssl.com
27 */
28
29/*
30 * Using TLS in Applications: http://datatracker.ietf.org/wg/uta/documents/
31 * TLS: http://datatracker.ietf.org/wg/tls/documents/
32 */
33
34#include "config.h"
35#include "../msg.h"
36
37#include <assert.h>
38
39#include <sys/stat.h>
40#include <sys/types.h>
41
42#include <ctype.h> /* tolower for wget stuff */
43#include <stdio.h>
44#include <errno.h>
45#include "../../dlib/dlib.h"
46#include "../dialog.hh"
47#include "../klist.h"
48#include "iowatch.hh"
49#include "tls.h"
50#include "Url.h"
51
52#include <openssl/ssl.h>
53#include <openssl/rand.h>
54#include <openssl/err.h>
55#include <openssl/x509v3.h> /* for hostname checking */
56#include <openssl/crypto.h> /* OpenSSL_version */
57
58#define CERT_STATUS_NONE 0
59#define CERT_STATUS_RECEIVING 1
60#define CERT_STATUS_CLEAN 2
61#define CERT_STATUS_BAD 3
62#define CERT_STATUS_USER_ACCEPTED 4
63
64typedef struct {
65 char *hostname;
66 int port;
67 int cert_status;
68} Server_t;
69
70typedef struct {
71 int fd;
72 int connkey;
73} FdMapEntry_t;
74
75/*
76 * Data type for TLS connection information
77 */
78typedef struct {
79 int fd;
80 DilloUrl *url;
81 SSL *ssl;
82 bool_t connecting;
83 bool_t in_connect;
84 bool_t do_shutdown;
85} Conn_t;
86
87/* List of active TLS connections */
88static Klist_t *conn_list = NULL;
89
90/*
91 * If ssl_context is still NULL, this corresponds to TLS being disabled.
92 */
93static SSL_CTX *ssl_context;
94static Dlist *servers;
95static Dlist *fd_map;
96
97static void Tls_connect_cb(int fd, void *vconnkey);
98
99/*
100 * Compare by FD.
101 */
102static int Tls_fd_map_cmp(const void *v1, const void *v2)
103{
104 int fd = VOIDP2INT(v2);
105 const FdMapEntry_t *e = v1;
106
107 return (fd != e->fd);
108}
109
110static void Tls_fd_map_add_entry(int fd, int connkey)
111{
112 FdMapEntry_t *e = dNew0(FdMapEntry_t, 1);
113 e->fd = fd;
114 e->connkey = connkey;
115
117 MSG_ERR("TLS FD ENTRY ALREADY FOUND FOR %d\n", e->fd);
118 assert(0);
119 }
120
122//MSG("ADD ENTRY %d %s\n", e->fd, URL_STR(sd->url));
123}
124
125/*
126 * Remove and free entry from fd_map.
127 */
128static void Tls_fd_map_remove_entry(int fd)
129{
131
132//MSG("REMOVE ENTRY %d\n", fd);
133 if (data) {
135 dFree(data);
136 } else {
137 MSG("TLS FD ENTRY NOT FOUND FOR %d\n", fd);
138 }
139}
140
141/*
142 * Return TLS connection information for a given file
143 * descriptor, or NULL if no TLS connection was found.
144 */
146{
147 Conn_t *conn;
148
149 if (fd_map) {
150 FdMapEntry_t *fme = dList_find_custom(fd_map, INT2VOIDP(fd),
152
153 if (fme && (conn = a_Klist_get_data(conn_list, fme->connkey)))
154 return conn;
155 }
156 return NULL;
157}
158
159/*
160 * Add a new TLS connection information node.
161 */
162static int Tls_conn_new(int fd, const DilloUrl *url, SSL *ssl)
163{
164 int key;
165
166 Conn_t *conn = dNew0(Conn_t, 1);
167 conn->fd = fd;
168 conn->url = a_Url_dup(url);
169 conn->ssl = ssl;
170 conn->connecting = TRUE;
171 conn->in_connect = FALSE;
172 conn->do_shutdown = TRUE;
173
174 key = a_Klist_insert(&conn_list, conn);
175
176 Tls_fd_map_add_entry(fd, key);
177
178 return key;
179}
180
181/*
182 * Let's monitor for TLS alerts.
183 */
184static void Tls_info_cb(const SSL *ssl, int where, int ret)
185{
186 if (where & SSL_CB_ALERT) {
187 const char *str = SSL_alert_desc_string_long(ret);
188
189 if (strcmp(str, "close notify"))
190 MSG("TLS ALERT on %s: %s\n", (where & SSL_CB_READ) ? "read" : "write",
191 str);
192 }
193}
194
195/*
196 * Load trusted certificates.
197 * This is like using SSL_CTX_load_verify_locations() but permitting more
198 * than one bundle and more than one directory. Due to the notoriously
199 * abysmal openssl documentation, this was worked out from reading discussion
200 * on the web and then reading openssl source to see what it normally does.
201 */
202static void Tls_load_certificates(void)
203{
204 /* curl-7.37.1 says that the following bundle locations are used on "Debian
205 * systems", "Redhat and Mandriva", "old(er) Redhat", "FreeBSD", and
206 * "OpenBSD", respectively -- and that the /etc/ssl/certs/ path is needed on
207 * "SUSE". No doubt it's all changed some over time, but this gives us
208 * something to work with.
209 */
210 uint_t u;
211 char *userpath;
212 static const char *const ca_files[] = {
213 "/etc/ssl/certs/ca-certificates.crt",
214 "/etc/pki/tls/certs/ca-bundle.crt",
215 "/usr/share/ssl/certs/ca-bundle.crt",
216 "/usr/local/share/certs/ca-root.crt",
217 "/etc/ssl/cert.pem",
218 "/etc/openssl/certs/ca-certificates.crt",
219 CA_CERTS_FILE
220 };
221
222 static const char *const ca_paths[] = {
223 "/etc/ssl/certs/",
224 CA_CERTS_DIR
225 };
226
227 X509_STORE *store = SSL_CTX_get_cert_store(ssl_context);
228 X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
229
230 for (u = 0; u < sizeof(ca_files) / sizeof(ca_files[0]); u++) {
231 if (*ca_files[u])
232 X509_LOOKUP_load_file(lookup, ca_files[u], X509_FILETYPE_PEM);
233 }
234
235 lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
236 for (u = 0; u < sizeof(ca_paths)/sizeof(ca_paths[0]); u++) {
237 if (*ca_paths[u])
238 X509_LOOKUP_add_dir(lookup, ca_paths[u], X509_FILETYPE_PEM);
239 }
240
241 userpath = dStrconcat(dGethomedir(), "/.dillo/certs/", NULL);
242 X509_LOOKUP_add_dir(lookup, userpath, X509_FILETYPE_PEM);
243 dFree(userpath);
244
245 /* Clear out errors in the queue (file not found, etc.) */
246 while(ERR_get_error())
247 ;
248}
249
250const char *a_Tls_openssl_version(char *buf, int n)
251{
252 /* Ugly hack to replace "OpenSSL 3.4.0 22 Oct 2024" with
253 * "OpenSSL/3.4.0". It also works for LibreSSL. */
254 const char *ver = OpenSSL_version(OPENSSL_VERSION);
255 if (snprintf(buf, n, "%s", ver) >= n)
256 return "OpenSSL/?";
257
258 char *ossl = buf;
259 char *sp1 = strchr(ossl, ' ');
260 if (sp1) {
261 *sp1 = '/';
262 char *sp2 = strchr(ossl, ' ');
263 if (sp2) {
264 *sp2 = '\0';
265 }
266 }
267
268 return buf;
269}
270
271/*
272 * Initialize the OpenSSL library.
273 */
275{
276 MSG("TLS library: %s\n", OpenSSL_version(OPENSSL_VERSION));
277 SSL_library_init();
278 SSL_load_error_strings();
279 if (RAND_status() != 1) {
280 /* The standard solution is to provide it with more entropy, but this
281 * involves knowing very well that you are doing exactly the right thing.
282 */
283 MSG_ERR("Disabling HTTPS: Insufficient entropy for openssl.\n");
284 return;
285 }
286
287 /* Create SSL context */
288 ssl_context = SSL_CTX_new(TLS_client_method());
289 if (ssl_context == NULL) {
290 MSG_ERR("Disabling HTTPS: Error creating SSL context.\n");
291 return;
292 }
293
294 SSL_CTX_set_info_callback(ssl_context, Tls_info_cb);
295
296 /* Don't want: eNULL, which has no encryption; aNULL, which has no
297 * authentication; LOW, which as of 2014 use 64 or 56-bit encryption;
298 * EXPORT40, which uses 40-bit encryption; RC4, for which methods were
299 * found in 2013 to defeat it somewhat too easily.
300 */
301 SSL_CTX_set_cipher_list(ssl_context,
302 "ALL:!aNULL:!eNULL:!LOW:!EXPORT40:!RC4");
303
304 /* SSL2 has been known to be insecure forever, disabling SSL3 is in response
305 * to POODLE, and disabling compression is in response to CRIME.
306 */
307 SSL_CTX_set_options(ssl_context,
308 SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION);
309
310 /* This lets us deal with self-signed certificates */
311 SSL_CTX_set_verify(ssl_context, SSL_VERIFY_NONE, NULL);
312
314
315 fd_map = dList_new(20);
316 servers = dList_new(8);
317}
318
319/*
320 * Save certificate with a hashed filename.
321 * Return: 0 on success, 1 on failure.
322 */
323static int Tls_save_certificate_home(X509 * cert)
324{
325 char buf[4096];
326
327 FILE * fp = NULL;
328 uint_t i = 0;
329 int ret = 1;
330
331 /* Attempt to create .dillo/certs blindly - check later */
332 snprintf(buf, 4096, "%s/.dillo/", dGethomedir());
333 mkdir(buf, 01777);
334 snprintf(buf, 4096, "%s/.dillo/certs/", dGethomedir());
335 mkdir(buf, 01777);
336
337 do {
338 snprintf(buf, 4096, "%s/.dillo/certs/%lx.%u",
339 dGethomedir(), X509_subject_name_hash(cert), i);
340
341 fp=fopen(buf, "r");
342 if (fp == NULL){
343 /* File name doesn't exist so we can use it safely */
344 fp=fopen(buf, "w");
345 if (fp == NULL){
346 MSG("Unable to open cert save file in home dir\n");
347 break;
348 } else {
349 PEM_write_X509(fp, cert);
350 fclose(fp);
351 MSG("Wrote certificate\n");
352 ret = 0;
353 break;
354 }
355 } else {
356 fclose(fp);
357 }
358 i++;
359 /* Don't loop too many times - just give up */
360 } while (i < 1024);
361
362 return ret;
363}
364
365/*
366 * Ordered comparison of servers.
367 */
368static int Tls_servers_cmp(const void *v1, const void *v2)
369{
370 const Server_t *s1 = (const Server_t *)v1, *s2 = (const Server_t *)v2;
371 int cmp = dStrAsciiCasecmp(s1->hostname, s2->hostname);
372
373 if (!cmp)
374 cmp = s1->port - s2->port;
375 return cmp;
376}
377/*
378 * Ordered comparison of server with URL.
379 */
380static int Tls_servers_by_url_cmp(const void *v1, const void *v2)
381{
382 const Server_t *s = (const Server_t *)v1;
383 const DilloUrl *url = (const DilloUrl *)v2;
384
385 int cmp = dStrAsciiCasecmp(s->hostname, URL_HOST(url));
386
387 if (!cmp)
388 cmp = s->port - URL_PORT(url);
389 return cmp;
390}
391
392/*
393 * The purpose here is to permit a single initial connection to a server.
394 * Once we have the certificate, know whether we like it -- and whether the
395 * user accepts it -- HTTP can run through queued sockets as normal.
396 *
397 * Return: TLS_CONNECT_READY or TLS_CONNECT_NOT_YET or TLS_CONNECT_NEVER.
398 */
400{
401 Server_t *s;
402 int ret = TLS_CONNECT_READY;
403
404 if (ssl_context == NULL)
405 return TLS_CONNECT_NEVER;
406
408 if (s->cert_status == CERT_STATUS_RECEIVING)
410 else if (s->cert_status == CERT_STATUS_BAD)
411 ret = TLS_CONNECT_NEVER;
412
413 if (s->cert_status == CERT_STATUS_NONE)
414 s->cert_status = CERT_STATUS_RECEIVING;
415 } else {
416 s = dNew(Server_t, 1);
417
418 s->hostname = dStrdup(URL_HOST(url));
419 s->port = URL_PORT(url);
420 s->cert_status = CERT_STATUS_RECEIVING;
422 }
423 return ret;
424}
425
426static int Tls_cert_status(const DilloUrl *url)
427{
429
430 return s ? s->cert_status : CERT_STATUS_NONE;
431}
432
433/*
434 * Did we find problems with the certificate, and did the user proceed to
435 * reject the connection?
436 */
437static int Tls_user_said_no(const DilloUrl *url)
438{
439 return Tls_cert_status(url) == CERT_STATUS_BAD;
440}
441
442/*
443 * Did everything seem proper with the certificate -- no warnings to
444 * click through?
445 */
450
451/*
452 * We are both checking whether the certificates are using a strong enough
453 * hash algorithm and key as well as printing out certificate information the
454 * first time that we see it. Mixing these two actions together is generally
455 * not good practice, but feels justified by the fact that it's so much
456 * trouble to get this information out of openssl even once.
457 *
458 * Return FALSE if MD5 (MD*) hash is found and user does not accept it,
459 * otherwise TRUE.
460 */
461static bool_t Tls_check_cert_strength(SSL *ssl, Server_t *srv, int *choice)
462{
463 /* print for first connection to server */
464 const bool_t print_chain = srv->cert_status == CERT_STATUS_RECEIVING;
465 bool_t success = TRUE;
466
467 STACK_OF(X509) *sk = SSL_get_peer_cert_chain(ssl);
468
469 if (sk) {
470 const uint_t buflen = 4096;
471 char buf[buflen];
472 int rc, i, n = sk_X509_num(sk);
473 X509 *cert = NULL;
474 EVP_PKEY *public_key;
475 int key_type, key_bits;
476 const char *type_str;
477 BIO *b;
478
479 for (i = 0; i < n; i++) {
480 cert = sk_X509_value(sk, i);
481 public_key = X509_get_pubkey(cert);
482
483 /* We are trying to find a way to get the hash function used
484 * with a certificate. This way, which is not very pleasant, puts
485 * a string such as "sha256WithRSAEncryption" in our buffer and we
486 * then trim off the "With..." part.
487 */
488 b = BIO_new(BIO_s_mem());
489 const ASN1_OBJECT *algorithm;
490 X509_ALGOR_get0(&algorithm, NULL, NULL, X509_get0_tbs_sigalg(cert));
491 rc = i2a_ASN1_OBJECT(b, algorithm);
492
493 if (rc > 0) {
494 rc = BIO_gets(b, buf, buflen);
495 }
496 if (rc <= 0) {
497 strcpy(buf, "(unknown)");
498 buf[buflen-1] = '\0';
499 } else {
500 char *s = strstr(buf, "With");
501
502 if (s) {
503 *s = '\0';
504 if (!strcmp(buf, "sha1")) {
505 if (print_chain)
506 MSG_WARN("In 2015, browsers have begun to deprecate SHA1 "
507 "certificates.\n");
508 } else if (!strncmp(buf, "md", 2) && success == TRUE) {
509 const char *msg = "A certificate in the chain uses the MD5 "
510 "signature algorithm, which is too weak "
511 "to trust.";
512 *choice = a_Dialog_choice("Dillo TLS security warning", msg,
513 "Continue", "Cancel", NULL);
514 if (*choice != 1)
515 success = FALSE;
516 }
517 }
518 }
519 BIO_free(b);
520
521 if (print_chain)
522 MSG("%s ", buf);
523
524#if OPENSSL_VERSION_NUMBER < 0x30000000L
525 key_type = EVP_PKEY_type(EVP_PKEY_id(public_key));
526#else
527 key_type = EVP_PKEY_type(EVP_PKEY_get_id(public_key));
528#endif
529 type_str = key_type == EVP_PKEY_RSA ? "RSA" :
530 key_type == EVP_PKEY_DSA ? "DSA" :
531 key_type == EVP_PKEY_DH ? "DH" :
532 key_type == EVP_PKEY_EC ? "EC" : "???";
533 key_bits = EVP_PKEY_bits(public_key);
534 X509_NAME_oneline(X509_get_subject_name(cert), buf, buflen);
535 buf[buflen-1] = '\0';
536 if (print_chain)
537 MSG("%d-bit %s: %s\n", key_bits, type_str, buf);
538 EVP_PKEY_free(public_key);
539
540 if (key_type == EVP_PKEY_RSA && key_bits <= 1024) {
541 if (print_chain)
542 MSG_WARN("In 2014/5, browsers have been deprecating 1024-bit "
543 "RSA keys.\n");
544 }
545 }
546
547 if (cert) {
548 X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen);
549 buf[buflen-1] = '\0';
550 if (print_chain)
551 MSG("root: %s\n", buf);
552 }
553 }
554 return success;
555}
556
557/******************** BEGINNING OF STUFF DERIVED FROM wget-1.16.3 */
558
559#define ASTERISK_EXCLUDES_DOT /* mandated by rfc2818 */
560
561/* Return true is STRING (case-insensitively) matches PATTERN, false
562 otherwise. The recognized wildcard character is "*", which matches
563 any character in STRING except ".". Any number of the "*" wildcard
564 may be present in the pattern.
565
566 This is used to match of hosts as indicated in rfc2818: "Names may
567 contain the wildcard character * which is considered to match any
568 single domain name component or component fragment. E.g., *.a.com
569 matches foo.a.com but not bar.foo.a.com. f*.com matches foo.com but
570 not bar.com [or foo.bar.com]."
571
572 If the pattern contain no wildcards, pattern_match(a, b) is
573 equivalent to !strcasecmp(a, b). */
574
575static bool_t pattern_match (const char *pattern, const char *string)
576{
577
578 const char *p = pattern, *n = string;
579 char c;
580 for (; (c = tolower (*p++)) != '\0'; n++)
581 if (c == '*')
582 {
583 for (c = tolower (*p); c == '*'; c = tolower (*++p))
584 ;
585 for (; *n != '\0'; n++)
586 if (tolower (*n) == c && pattern_match (p, n))
587 return TRUE;
588#ifdef ASTERISK_EXCLUDES_DOT
589 else if (*n == '.')
590 return FALSE;
591#endif
592 return c == '\0';
593 }
594 else
595 {
596 if (c != tolower (*n))
597 return FALSE;
598 }
599 return *n == '\0';
600}
601
602/*
603 * Check that the certificate corresponds to the site it's presented for.
604 *
605 * Return TRUE if the hostname matched or the user indicated acceptance.
606 * FALSE on failure.
607 */
608static bool_t Tls_check_cert_hostname(X509 *cert, const char *host,
609 int *choice)
610{
611 if (cert == NULL || host == NULL)
612 return FALSE;
613
614 char *msg;
615 GENERAL_NAMES *subjectAltNames;
616 bool_t success = TRUE, alt_name_checked = FALSE;;
617 char common_name[256];
618
619 /* Check that HOST matches the common name in the certificate.
620 #### The following remains to be done:
621
622 - When matching against common names, it should loop over all
623 common names and choose the most specific one, i.e. the last
624 one, not the first one, which the current code picks.
625
626 - Ensure that ASN1 strings from the certificate are encoded as
627 UTF-8 which can be meaningfully compared to HOST. */
628
629 subjectAltNames = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL);
630
631 if (subjectAltNames)
632 {
633 /* Test subject alternative names */
634
635 Dstr *err = dStr_new("");
636 dStr_sprintf(err, "Hostname %s does not match any of certificate's "
637 "Subject Alternative Names: ", host);
638
639 /* Do we want to check for dNSNAmes or ipAddresses (see RFC 2818)?
640 * Signal it by host_in_octet_string. */
641 ASN1_OCTET_STRING *host_in_octet_string = a2i_IPADDRESS (host);
642
643 int numaltnames = sk_GENERAL_NAME_num (subjectAltNames);
644 int i;
645 for (i=0; i < numaltnames; i++)
646 {
647 const GENERAL_NAME *name =
648 sk_GENERAL_NAME_value (subjectAltNames, i);
649 if (name)
650 {
651 if (host_in_octet_string)
652 {
653 if (name->type == GEN_IPADD)
654 {
655 /* Check for ipAddress */
656 /* TODO: Should we convert between IPv4-mapped IPv6
657 * addresses and IPv4 addresses? */
658 alt_name_checked = TRUE;
659 if (!ASN1_STRING_cmp (host_in_octet_string,
660 name->d.iPAddress))
661 break;
662 dStr_sprintfa(err, "%s ", name->d.iPAddress);
663 }
664 }
665 else if (name->type == GEN_DNS)
666 {
667 /* dNSName should be IA5String (i.e. ASCII), however who
668 * does trust CA? Convert it into UTF-8 for sure. */
669 unsigned char *name_in_utf8 = NULL;
670
671 /* Check for dNSName */
672 alt_name_checked = TRUE;
673
674 if (0 <= ASN1_STRING_to_UTF8 (&name_in_utf8, name->d.dNSName))
675 {
676 /* Compare and check for NULL attack in ASN1_STRING */
677 if (pattern_match ((char *)name_in_utf8, host) &&
678 (strlen ((char *)name_in_utf8) ==
679 (size_t)ASN1_STRING_length (name->d.dNSName)))
680 {
681 OPENSSL_free (name_in_utf8);
682 break;
683 }
684 dStr_sprintfa(err, "%s ", name_in_utf8);
685 OPENSSL_free (name_in_utf8);
686 }
687 }
688 }
689 }
690 sk_GENERAL_NAME_pop_free(subjectAltNames, GENERAL_NAME_free);
691 if (host_in_octet_string)
692 ASN1_OCTET_STRING_free(host_in_octet_string);
693
694 if (alt_name_checked == TRUE && i >= numaltnames)
695 {
696 success = FALSE;
697 *choice = a_Dialog_choice("Dillo TLS security warning",
698 err->str, "Continue", "Cancel", NULL);
699
700 switch (*choice){
701 case 1:
702 success = TRUE;
703 break;
704 case 2:
705 break;
706 default:
707 break;
708 }
709 }
710 dStr_free(err, 1);
711 }
712
713 if (alt_name_checked == FALSE)
714 {
715 /* Test commomName */
716 X509_NAME *xname = X509_get_subject_name(cert);
717 common_name[0] = '\0';
718 X509_NAME_get_text_by_NID (xname, NID_commonName, common_name,
719 sizeof (common_name));
720
721 if (!pattern_match (common_name, host))
722 {
723 success = FALSE;
724 msg = dStrconcat("Certificate common name ", common_name,
725 " doesn't match requested host name ", host, NULL);
726 *choice = a_Dialog_choice("Dillo TLS security warning",
727 msg, "Continue", "Cancel", NULL);
728 dFree(msg);
729
730 switch (*choice){
731 case 1:
732 success = TRUE;
733 break;
734 case 2:
735 break;
736 default:
737 break;
738 }
739 }
740 else
741 {
742 /* We now determine the length of the ASN1 string. If it
743 * differs from common_name's length, then there is a \0
744 * before the string terminates. This can be an instance of a
745 * null-prefix attack.
746 *
747 * https://www.blackhat.com/html/bh-usa-09/bh-usa-09-archives.html#Marlinspike
748 * */
749
750 int i = -1, j;
751 X509_NAME_ENTRY *xentry;
752 ASN1_STRING *sdata;
753
754 if (xname) {
755 for (;;)
756 {
757 j = X509_NAME_get_index_by_NID (xname, NID_commonName, i);
758 if (j == -1) break;
759 i = j;
760 }
761 }
762
763 xentry = X509_NAME_get_entry(xname,i);
764 sdata = X509_NAME_ENTRY_get_data(xentry);
765 if (strlen (common_name) != (size_t)ASN1_STRING_length (sdata))
766 {
767 success = FALSE;
768 msg = dStrconcat("Certificate common name is invalid (contains a NUL "
769 "character). This may be an indication that the "
770 "host is not who it claims to be -- that is, not "
771 "the real ", host, NULL);
772 *choice = a_Dialog_choice("Dillo TLS security warning",
773 msg, "Continue", "Cancel", NULL);
774 dFree(msg);
775
776 switch (*choice){
777 case 1:
778 success = TRUE;
779 break;
780 case 2:
781 break;
782 default:
783 break;
784 }
785 }
786 }
787 }
788 return success;
789}
790
791/******************** END OF STUFF DERIVED FROM wget-1.16.3 */
792
793/*
794 * Get the certificate at the end of the chain, or NULL on failure.
795 *
796 * Rumor has it that the stack can be NULL if a connection has been reused
797 * and that the stack can then be reconstructed if necessary, but it doesn't
798 * sound like a case we'll encounter.
799 */
800static X509 *Tls_get_end_of_chain(SSL *ssl)
801{
802 STACK_OF(X509) *sk = SSL_get_peer_cert_chain(ssl);
803
804 return sk ? sk_X509_value(sk, sk_X509_num(sk) - 1) : NULL;
805}
806
807static void Tls_get_issuer_name(X509 *cert, char *buf, uint_t buflen)
808{
809 if (cert) {
810 X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen);
811 } else {
812 strcpy(buf, "(unknown)");
813 buf[buflen-1] = '\0';
814 }
815}
816
817static void Tls_get_expiration_str(X509 *cert, char *buf, uint_t buflen)
818{
819 ASN1_TIME *exp_date = X509_get_notAfter(cert);
820 BIO *b = BIO_new(BIO_s_mem());
821 int rc = ASN1_TIME_print(b, exp_date);
822
823 if (rc > 0) {
824 rc = BIO_gets(b, buf, buflen);
825 }
826 if (rc <= 0) {
827 strcpy(buf, "(unknown)");
828 buf[buflen-1] = '\0';
829 }
830 BIO_free(b);
831}
832
833/*
834 * Examine the certificate, and, if problems are detected, ask the user what
835 * to do.
836 * Return: -1 if connection should be canceled, or 0 if it should continue.
837 */
838static int Tls_examine_certificate(SSL *ssl, Server_t *srv)
839{
840 X509 *remote_cert;
841 long st;
842 const uint_t buflen = 4096;
843 char buf[buflen], *cn, *msg;
844 int choice = -1, ret = -1;
845 char *title = dStrconcat("Dillo TLS security warning: ",srv->hostname,NULL);
846
847#if OPENSSL_VERSION_NUMBER < 0x30000000L
848 remote_cert = SSL_get_peer_certificate(ssl);
849#else
850 /* SSL_get_peer_certificate() was deprecated in 3.0.0. */
851 remote_cert = SSL_get1_peer_certificate(ssl);
852#endif
853 if (remote_cert == NULL){
854 /* Inform user that remote system cannot be trusted */
855 choice = a_Dialog_choice(title,
856 "The remote system is not presenting a certificate. "
857 "This site cannot be trusted. Sending data is not safe.",
858 "Continue", "Cancel", NULL);
859
860 /* Abort on anything but "Continue" */
861 if (choice == 1){
862 ret = 0;
863 }
864 } else if (Tls_check_cert_strength(ssl, srv, &choice) &&
865 Tls_check_cert_hostname(remote_cert, srv->hostname, &choice)) {
866 /* Figure out if (and why) the remote system can't be trusted */
867 st = SSL_get_verify_result(ssl);
868 switch (st) {
869 case X509_V_OK:
870 ret = 0;
871 break;
872 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
873 ; /* Case follows declaration */
874 /* Either self signed and untrusted */
875 /* Extract CN from certificate name information */
876 X509_NAME *subject_name = X509_get_subject_name(remote_cert);
877 char *subj = X509_NAME_oneline(subject_name, NULL, 0);
878 if ((cn = strstr(subj, "/CN=")) == NULL) {
879 strcpy(buf, "(no CN given)");
880 } else {
881 char *cn_end;
882
883 cn += 4;
884
885 if ((cn_end = strstr(cn, "/")) == NULL )
886 cn_end = cn + strlen(cn);
887
888 strncpy(buf, cn, (size_t) (cn_end - cn));
889 buf[cn_end - cn] = '\0';
890 }
891 OPENSSL_free(subj);
892 msg = dStrconcat("The remote certificate is self-signed and "
893 "untrusted. For address: ", buf, NULL);
894 choice = a_Dialog_choice(title,
895 msg, "Continue", "Cancel", "Save Certificate", NULL);
896 dFree(msg);
897
898 switch (choice){
899 case 1:
900 ret = 0;
901 break;
902 case 2:
903 break;
904 case 3:
905 /* Save certificate to a file */
906 Tls_save_certificate_home(remote_cert);
907 ret = 0;
908 break;
909 default:
910 break;
911 }
912 break;
913 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
914 case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
915 choice = a_Dialog_choice(title,
916 "The issuer for the remote certificate cannot be found. "
917 "The authenticity of the remote certificate cannot be trusted.",
918 "Continue", "Cancel", NULL);
919
920 if (choice == 1) {
921 ret = 0;
922 }
923 break;
924
925 case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
926 case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
927 case X509_V_ERR_CERT_SIGNATURE_FAILURE:
928 case X509_V_ERR_CRL_SIGNATURE_FAILURE:
929 choice = a_Dialog_choice(title,
930 "The remote certificate signature could not be read "
931 "or is invalid and should not be trusted",
932 "Continue", "Cancel", NULL);
933
934 if (choice == 1) {
935 ret = 0;
936 }
937 break;
938 case X509_V_ERR_CERT_NOT_YET_VALID:
939 case X509_V_ERR_CRL_NOT_YET_VALID:
940 choice = a_Dialog_choice(title,
941 "Part of the remote certificate is not yet valid. "
942 "Certificates usually have a range of dates over which "
943 "they are to be considered valid, and the certificate "
944 "presented has a starting validity after today's date "
945 "You should be cautious about using this site",
946 "Continue", "Cancel", NULL);
947
948 if (choice == 1) {
949 ret = 0;
950 }
951 break;
952 case X509_V_ERR_CERT_HAS_EXPIRED:
953 case X509_V_ERR_CRL_HAS_EXPIRED:
954 Tls_get_expiration_str(remote_cert, buf, buflen);
955 msg = dStrconcat("The remote certificate expired on: ", buf,
956 ". This site can no longer be trusted.", NULL);
957
958 choice = a_Dialog_choice(title, msg, "Continue", "Cancel", NULL);
959 if (choice == 1) {
960 ret = 0;
961 }
962 dFree(msg);
963 break;
964 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
965 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
966 case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
967 case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
968 choice = a_Dialog_choice(title,
969 "There was an error in the certificate presented. "
970 "Some of the certificate data was improperly formatted "
971 "making it impossible to determine if the certificate "
972 "is valid. You should not trust this certificate.",
973 "Continue", "Cancel", NULL);
974 if (choice == 1) {
975 ret = 0;
976 }
977 break;
978 case X509_V_ERR_INVALID_CA:
979 case X509_V_ERR_INVALID_PURPOSE:
980 case X509_V_ERR_CERT_UNTRUSTED:
981 case X509_V_ERR_CERT_REJECTED:
982 case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
983 choice = a_Dialog_choice(title,
984 "One of the certificates in the chain is being used "
985 "incorrectly (possibly due to configuration problems "
986 "with the remote system. The connection should not "
987 "be trusted",
988 "Continue", "Cancel", NULL);
989 if (choice == 1) {
990 ret = 0;
991 }
992 break;
993 case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
994 case X509_V_ERR_AKID_SKID_MISMATCH:
995 case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
996 choice = a_Dialog_choice(title,
997 "Some of the information presented by the remote system "
998 "does not match other information presented. "
999 "This may be an attempt to eavesdrop on communications",
1000 "Continue", "Cancel", NULL);
1001 if (choice == 1) {
1002 ret = 0;
1003 }
1004 break;
1005 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
1006 Tls_get_issuer_name(Tls_get_end_of_chain(ssl), buf, buflen);
1007 msg = dStrconcat("Certificate chain led to a self-signed certificate "
1008 "instead of a trusted root. Name: ", buf , NULL);
1009 choice = a_Dialog_choice(title, msg, "Continue", "Cancel", NULL);
1010 if (choice == 1) {
1011 ret = 0;
1012 }
1013 dFree(msg);
1014 break;
1015 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
1016 Tls_get_issuer_name(Tls_get_end_of_chain(ssl), buf, buflen);
1017 msg = dStrconcat("The issuer certificate of an untrusted certificate "
1018 "cannot be found. Issuer: ", buf, NULL);
1019 choice = a_Dialog_choice(title, msg, "Continue", "Cancel", NULL);
1020 if (choice == 1) {
1021 ret = 0;
1022 }
1023 dFree(msg);
1024 break;
1025 default: /* Need to add more options later */
1026 snprintf(buf, 80,
1027 "The remote certificate cannot be verified (code %ld)", st);
1028 choice = a_Dialog_choice(title,
1029 buf, "Continue", "Cancel", NULL);
1030 /* abort on anything but "Continue" */
1031 if (choice == 1){
1032 ret = 0;
1033 }
1034 }
1035 X509_free(remote_cert);
1036 remote_cert = 0;
1037 }
1038 dFree(title);
1039
1040 if (choice == -1) {
1041 srv->cert_status = CERT_STATUS_CLEAN; /* no warning popups */
1042 } else if (choice == 1) {
1043 srv->cert_status = CERT_STATUS_USER_ACCEPTED; /* clicked Continue */
1044 } else {
1045 /* 2 for Cancel, or 0 when window closed. Treating 0 as meaning 'No' is
1046 * probably not exactly correct, but adding complexity to handle this
1047 * obscure case does not seem justifiable.
1048 */
1049 srv->cert_status = CERT_STATUS_BAD;
1050 }
1051 return ret;
1052}
1053
1054/*
1055 * If the connection was closed before we got the certificate, we need to
1056 * reset state so that we'll try again.
1057 */
1059{
1060 if (servers) {
1062
1063 if (s && s->cert_status == CERT_STATUS_RECEIVING)
1064 s->cert_status = CERT_STATUS_NONE;
1065 }
1066}
1067
1068/*
1069 * Close an open TLS connection.
1070 */
1071static void Tls_close_by_key(int connkey)
1072{
1073 Conn_t *c;
1074
1075 if ((c = a_Klist_get_data(conn_list, connkey))) {
1077 if (c->connecting) {
1078 a_IOwatch_remove_fd(c->fd, -1);
1079 dClose(c->fd);
1080 }
1081 if (c->do_shutdown && !SSL_in_init(c->ssl)) {
1082 /* openssl 1.0.2f does not like shutdown being called during
1083 * handshake, resulting in ssl_undefined_function in the error queue.
1084 */
1085 SSL_shutdown(c->ssl);
1086 } else {
1087 MSG("Tls_close_by_key: Avoiding SSL shutdown for: %s\n", URL_STR(c->url));
1088 }
1089 SSL_free(c->ssl);
1090
1091 a_Url_free(c->url);
1093 a_Klist_remove(conn_list, connkey);
1094 dFree(c);
1095 }
1096}
1097
1098/*
1099 * Connect, set a callback if it's still not completed. If completed, check
1100 * the certificate and report back to http.
1101 */
1102static void Tls_connect(int fd, int connkey)
1103{
1104 int ret;
1105 bool_t ongoing = FALSE, failed = TRUE;
1106 Conn_t *conn;
1107
1108 if (!(conn = a_Klist_get_data(conn_list, connkey))) {
1109 MSG("Tls_connect: conn for fd %d not valid\n", fd);
1110 return;
1111 }
1112
1113 if (conn->in_connect) {
1114 MSG("Tls_connect: nested call to Tls_connect(), aborting\n");
1115 abort();
1116 } else {
1117 conn->in_connect = TRUE;
1118 }
1119
1120 if (ERR_peek_error()) {
1121 unsigned long err;
1122 while ((err = ERR_get_error())) {
1123 MSG("Tls_connect: queued error: %s\n",
1124 ERR_error_string(err, NULL));
1125 }
1126 abort();
1127 }
1128
1129 ret = SSL_connect(conn->ssl);
1130
1131 a_IOwatch_remove_fd(fd, -1);
1132
1133 if (ret <= 0) {
1134 int err1_ret = SSL_get_error(conn->ssl, ret);
1135 if (err1_ret == SSL_ERROR_WANT_READ ||
1136 err1_ret == SSL_ERROR_WANT_WRITE) {
1137 int want = err1_ret == SSL_ERROR_WANT_READ ? DIO_READ : DIO_WRITE;
1138
1139 _MSG("iowatching fd %d for tls -- want %s\n", fd,
1140 err1_ret == SSL_ERROR_WANT_READ ? "read" : "write");
1141 a_IOwatch_add_fd(fd, want, Tls_connect_cb, INT2VOIDP(connkey));
1142 ongoing = TRUE;
1143 failed = FALSE;
1144 } else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
1145 /* From the OpenSSL documentation: "Note that SSL_shutdown() must not
1146 * be called if a previous fatal error has occurred on a connection
1147 * i.e. if SSL_get_error() has returned SSL_ERROR_SYSCALL or
1148 * SSL_ERROR_SSL." */
1149 conn->do_shutdown = FALSE;
1150
1151 unsigned long err2_ret = ERR_get_error();
1152
1153 if (err2_ret) {
1154 do {
1155 MSG("SSL_connect() failed: %s\n",
1156 ERR_error_string(err2_ret, NULL));
1157 } while ((err2_ret = ERR_get_error()));
1158 } else {
1159 /* nothing in the error queue */
1160 if (ret == 0) {
1161 MSG("TLS connect error: \"an EOF was observed that violates "
1162 "the protocol\"\n");
1163 /*
1164 * I presume we took too long on our side and the server grew
1165 * impatient.
1166 */
1167 } else if (ret == -1) {
1168 MSG("TLS connect error: %s\n", dStrerror(errno));
1169
1170 /* If the following can happen, I'll add code to handle it, but
1171 * I don't want to add code blindly if it isn't getting used
1172 */
1173 assert(errno != EAGAIN && errno != EINTR);
1174 } else {
1175 MSG_ERR("According to the man page for SSL_get_error(), this "
1176 "was not a possibility (ret %d).\n", ret);
1177 }
1178 }
1179 } else {
1180 MSG("SSL_get_error() returned %d on a connect.\n", err1_ret);
1181 }
1182 } else {
1183 Server_t *srv = dList_find_sorted(servers, conn->url,
1185
1186 if (srv->cert_status == CERT_STATUS_RECEIVING) {
1187 /* Making first connection with the server. Show cipher used. */
1188 SSL *ssl = conn->ssl;
1189 const char *version = SSL_get_version(ssl);
1190 const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
1191
1192 MSG("%s: %s, cipher %s\n", URL_AUTHORITY(conn->url), version,
1193 SSL_CIPHER_get_name(cipher));
1194 }
1195
1196 if (srv->cert_status == CERT_STATUS_USER_ACCEPTED ||
1197 (Tls_examine_certificate(conn->ssl, srv) != -1)) {
1198 failed = FALSE;
1199 }
1200 }
1201
1202 /*
1203 * If there were problems with the certificate, the connection may have
1204 * been closed by the server if the user responded too slowly to a popup.
1205 */
1206
1207 if (!ongoing) {
1208 if (a_Klist_get_data(conn_list, connkey)) {
1209 conn->connecting = FALSE;
1210 if (failed) {
1211 conn->in_connect = FALSE;
1212 Tls_close_by_key(connkey);
1213 /* conn is freed now */
1214 conn = NULL;
1215 }
1218 } else {
1219 MSG("Connection disappeared. Too long with a popup popped up?\n");
1220 }
1221 }
1222
1223 if (conn)
1224 conn->in_connect = FALSE;
1225}
1226
1227static void Tls_connect_cb(int fd, void *vconnkey)
1228{
1229 Tls_connect(fd, VOIDP2INT(vconnkey));
1230}
1231
1232/*
1233 * Perform the TLS handshake on an open socket.
1234 */
1235void a_Tls_openssl_connect(int fd, const DilloUrl *url)
1236{
1237 SSL *ssl;
1238 bool_t success = TRUE;
1239 int connkey = -1;
1240
1241 if (!ssl_context)
1242 success = FALSE;
1243
1244 if (success && Tls_user_said_no(url)) {
1245 success = FALSE;
1246 }
1247
1248 if (ERR_peek_error()) {
1249 unsigned long err;
1250 while ((err = ERR_get_error())) {
1251 MSG("a_Tls_openssl_connect: queued error: %s\n",
1252 ERR_error_string(err, NULL));
1253 }
1254 abort();
1255 }
1256
1257 if (success && !(ssl = SSL_new(ssl_context))) {
1258 unsigned long err_ret = ERR_get_error();
1259 do {
1260 MSG("SSL_new() failed: %s\n", ERR_error_string(err_ret, NULL));
1261 } while ((err_ret = ERR_get_error()));
1262 success = FALSE;
1263 }
1264
1265 /* assign TLS connection to this file descriptor */
1266 if (success && !SSL_set_fd(ssl, fd)) {
1267 unsigned long err_ret = ERR_get_error();
1268 do {
1269 MSG("SSL_set_fd() failed: %s\n", ERR_error_string(err_ret, NULL));
1270 } while ((err_ret = ERR_get_error()));
1271 success = FALSE;
1272 }
1273
1274 if (success)
1275 connkey = Tls_conn_new(fd, url, ssl);
1276
1277#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1278 /* Server Name Indication. From the openssl changelog, it looks like this
1279 * came along in 2010.
1280 */
1281 if (success && a_Url_host_type(URL_HOST(url)) == URL_HOST_NAME)
1282 SSL_set_tlsext_host_name(ssl, URL_HOST(url));
1283#endif
1284
1285 if (!success) {
1287 a_Http_connect_done(fd, success);
1288 } else {
1289 Tls_connect(fd, connkey);
1290 }
1291}
1292
1293/*
1294 * Traduces an SSL I/O error into something understandable by IO.c
1295 *
1296 * Returns: -1 if there is an error and sets errno to the appropriate error,
1297 * otherwise return the number of bytes read/written (which may be 0).
1298 */
1299static int Tls_handle_error(Conn_t *conn, int ret, const char *where)
1300{
1301 /* Success */
1302 if (ret > 0) {
1303 errno = 0;
1304 return ret;
1305 }
1306
1307 SSL *ssl = conn->ssl;
1308 int err1_ret = SSL_get_error(ssl, ret);
1309 if (err1_ret == SSL_ERROR_NONE) {
1310 errno = 0;
1311 return ret;
1312 } else if (err1_ret == SSL_ERROR_ZERO_RETURN) {
1313 errno = 0;
1314 return 0;
1315 } else if (err1_ret == SSL_ERROR_WANT_READ || err1_ret == SSL_ERROR_WANT_WRITE) {
1316 errno = EAGAIN;
1317 return -1;
1318 } else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
1319 conn->do_shutdown = FALSE;
1320 unsigned long err2_ret = ERR_get_error();
1321 if (err2_ret) {
1322 do {
1323 MSG("%s failed: %s\n", where, ERR_error_string(err2_ret, NULL));
1324 } while ((err2_ret = ERR_get_error()));
1325 } else {
1326 /* Provide a generic message, as there is none in the queue */
1327 const char *which = err1_ret == SSL_ERROR_SYSCALL ? "SYSCALL" : "SSL";
1328 MSG("%s failed: SSL_ERROR_%s\n", where, which);
1329 }
1330 errno = EPROTO;
1331 return -1;
1332 }
1333
1334 MSG("%s failed: SSL_get_error() returned %d\n", where, err1_ret);
1335 errno = EPROTO;
1336 return -1;
1337}
1338
1339/*
1340 * Read data from an open TLS connection.
1341 */
1342int a_Tls_openssl_read(void *conn, void *buf, size_t len)
1343{
1344 Conn_t *c = (Conn_t*)conn;
1345 return Tls_handle_error(c, SSL_read(c->ssl, buf, len), "SSL_read()");
1346}
1347
1348/*
1349 * Write data to an open TLS connection.
1350 */
1351int a_Tls_openssl_write(void *conn, void *buf, size_t len)
1352{
1353 Conn_t *c = (Conn_t*)conn;
1354 return Tls_handle_error(c, SSL_write(c->ssl, buf, len), "SSL_write()");
1355}
1356
1358{
1359 FdMapEntry_t *fme = dList_find_custom(fd_map, INT2VOIDP(fd),
1361
1362 if (fme) {
1363 Tls_close_by_key(fme->connkey);
1364 }
1365}
1366
1367static void Tls_servers_freeall(void)
1368{
1369 if (servers) {
1370 Server_t *s;
1371 int i, n = dList_length(servers);
1372
1373 for (i = 0; i < n; i++) {
1374 s = (Server_t *) dList_nth_data(servers, i);
1375 dFree(s->hostname);
1376 dFree(s);
1377 }
1379 }
1380}
1381
1382static void Tls_fd_map_remove_all(void)
1383{
1384 if (fd_map) {
1385 FdMapEntry_t *fme;
1386 int i, n = dList_length(fd_map);
1387
1388 for (i = 0; i < n; i++) {
1389 fme = (FdMapEntry_t *) dList_nth_data(fd_map, i);
1390 dFree(fme);
1391 }
1393 }
1394}
1395
1396/*
1397 * Clean up the OpenSSL library
1398 */
1400{
1401 if (ssl_context)
1402 SSL_CTX_free(ssl_context);
1405}
#define _MSG(...)
Definition bookmarks.c:45
#define MSG(...)
Definition bookmarks.c:46
unsigned int uint_t
Definition d_size.h:20
unsigned char bool_t
Definition d_size.h:21
int a_Dialog_choice(const char *title, const char *msg,...)
Make a question-dialog with a question and alternatives.
Definition dialog.cc:342
char * dStrconcat(const char *s1,...)
Concatenate a NULL-terminated list of strings.
Definition dlib.c:102
void dList_insert_sorted(Dlist *lp, void *data, dCompareFunc func)
Insert an element into a sorted list.
Definition dlib.c:797
void dFree(void *mem)
Definition dlib.c:68
int dStrAsciiCasecmp(const char *s1, const char *s2)
Definition dlib.c:203
void dStr_sprintfa(Dstr *ds, const char *format,...)
Printf-like function that appends.
Definition dlib.c:464
char * dStrdup(const char *s)
Definition dlib.c:77
Dlist * dList_new(int size)
Create a new empty list.
Definition dlib.c:576
int dList_length(Dlist *lp)
For completing the ADT.
Definition dlib.c:641
void * dList_nth_data(Dlist *lp, int n0)
Return the nth data item, NULL when not found or 'n0' is out of range.
Definition dlib.c:690
void dList_remove_fast(Dlist *lp, const void *data)
Remove a data item without preserving order.
Definition dlib.c:651
void dStr_free(Dstr *ds, int all)
Free a dillo string.
Definition dlib.c:337
int dClose(int fd)
Close a FD handling EINTR.
Definition dlib.c:979
void dStr_sprintf(Dstr *ds, const char *format,...)
Printf-like function.
Definition dlib.c:450
Dstr * dStr_new(const char *s)
Create a new string.
Definition dlib.c:325
void dList_append(Dlist *lp, void *data)
Append a data item to the list.
Definition dlib.c:625
void * dList_find_sorted(Dlist *lp, const void *data, dCompareFunc func)
Search a sorted list.
Definition dlib.c:824
void dList_free(Dlist *lp)
Free a list (not its elements)
Definition dlib.c:592
void * dList_find_custom(Dlist *lp, const void *data, dCompareFunc func)
Search a data item using a custom function.
Definition dlib.c:732
char * dGethomedir(void)
Return the home directory in a static string (don't free)
Definition dlib.c:934
#define dStrerror
Definition dlib.h:107
#define dNew0(type, count)
Definition dlib.h:63
#define VOIDP2INT(p)
Definition dlib.h:55
#define TRUE
Definition dlib.h:35
#define FALSE
Definition dlib.h:31
#define INT2VOIDP(i)
Definition dlib.h:56
#define dNew(type, count)
Definition dlib.h:61
#define MSG_ERR(...)
Definition dpid_common.h:23
void a_Http_connect_done(int fd, bool_t success)
Definition http.c:214
void a_IOwatch_add_fd(int fd, int when, Fl_FD_Handler Callback, void *usr_data=0)
Hook a Callback for a certain activities in a FD.
Definition iowatch.cc:22
void a_IOwatch_remove_fd(int fd, int when)
Remove a Callback for a given FD (or just remove some events)
Definition iowatch.cc:32
#define DIO_READ
Definition iowatch.hh:7
#define DIO_WRITE
Definition iowatch.hh:8
void a_Klist_remove(Klist_t *Klist, int Key)
Remove data by Key.
Definition klist.c:86
void * a_Klist_get_data(Klist_t *Klist, int Key)
Return the data pointer for a given Key (or NULL if not found)
Definition klist.c:43
int a_Klist_insert(Klist_t **Klist, void *Data)
Insert a data pointer and return a key for it.
Definition klist.c:56
#define MSG_WARN(...)
Definition msg.h:26
Definition url.h:88
Definition dlib.h:144
Definition dlib.h:114
Dstr_char_t * str
Definition dlib.h:117
static uint_t failed
Definition cookies.c:54
#define TLS_CONNECT_NEVER
Definition tls.h:30
#define TLS_CONNECT_READY
Definition tls.h:32
#define TLS_CONNECT_NOT_YET
Definition tls.h:31
void a_Tls_openssl_reset_server_state(const DilloUrl *url)
static Dlist * fd_map
Definition tls_openssl.c:95
static int Tls_examine_certificate(SSL *ssl, Server_t *srv)
static void Tls_fd_map_remove_all(void)
static void Tls_fd_map_remove_entry(int fd)
static void Tls_get_issuer_name(X509 *cert, char *buf, uint_t buflen)
static void Tls_servers_freeall(void)
int a_Tls_openssl_read(void *conn, void *buf, size_t len)
static X509 * Tls_get_end_of_chain(SSL *ssl)
static int Tls_servers_cmp(const void *v1, const void *v2)
static void Tls_info_cb(const SSL *ssl, int where, int ret)
void * a_Tls_openssl_connection(int fd)
static int Tls_handle_error(Conn_t *conn, int ret, const char *where)
int a_Tls_openssl_connect_ready(const DilloUrl *url)
#define CERT_STATUS_USER_ACCEPTED
Definition tls_openssl.c:62
static void Tls_fd_map_add_entry(int fd, int connkey)
#define CERT_STATUS_BAD
Definition tls_openssl.c:61
static int Tls_fd_map_cmp(const void *v1, const void *v2)
static int Tls_user_said_no(const DilloUrl *url)
static int Tls_conn_new(int fd, const DilloUrl *url, SSL *ssl)
static void Tls_close_by_key(int connkey)
#define CERT_STATUS_RECEIVING
Definition tls_openssl.c:59
void a_Tls_openssl_init(void)
static void Tls_get_expiration_str(X509 *cert, char *buf, uint_t buflen)
#define CERT_STATUS_CLEAN
Definition tls_openssl.c:60
static SSL_CTX * ssl_context
Definition tls_openssl.c:93
static Dlist * servers
Definition tls_openssl.c:94
static int Tls_servers_by_url_cmp(const void *v1, const void *v2)
static void Tls_connect_cb(int fd, void *vconnkey)
static void Tls_connect(int fd, int connkey)
#define CERT_STATUS_NONE
Definition tls_openssl.c:58
void a_Tls_openssl_close_by_fd(int fd)
const char * a_Tls_openssl_version(char *buf, int n)
static bool_t Tls_check_cert_hostname(X509 *cert, const char *host, int *choice)
void a_Tls_openssl_connect(int fd, const DilloUrl *url)
static int Tls_save_certificate_home(X509 *cert)
static int Tls_cert_status(const DilloUrl *url)
static void Tls_load_certificates(void)
static Klist_t * conn_list
Definition tls_openssl.c:88
static bool_t Tls_check_cert_strength(SSL *ssl, Server_t *srv, int *choice)
int a_Tls_openssl_certificate_is_clean(const DilloUrl *url)
void a_Tls_openssl_freeall(void)
static bool_t pattern_match(const char *pattern, const char *string)
int a_Tls_openssl_write(void *conn, void *buf, size_t len)
void a_Url_free(DilloUrl *url)
Free a DilloUrl.
Definition url.c:208
int a_Url_host_type(const char *host)
What type of host is this?
Definition url.c:683
DilloUrl * a_Url_dup(const DilloUrl *ori)
Duplicate a Url structure.
Definition url.c:477
#define URL_HOST_NAME
Definition url.h:24
#define URL_STR(u)
Definition url.h:76
#define URL_AUTHORITY(u)
Definition url.h:71
#define URL_PORT(u)
Definition url.h:78
#define URL_HOST(u)
Definition url.h:75