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