Dillo v3.1.1-119-g140d9ebd
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-2024 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 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 = tolower (*p++)) != '\0'; n++)
580 if (c == '*')
581 {
582 for (c = tolower (*p); c == '*'; c = tolower (*++p))
583 ;
584 for (; *n != '\0'; n++)
585 if (tolower (*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 != tolower (*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 Conn_t *c;
1073
1074 if ((c = a_Klist_get_data(conn_list, connkey))) {
1076 if (c->connecting) {
1077 a_IOwatch_remove_fd(c->fd, -1);
1078 dClose(c->fd);
1079 }
1080 if (c->do_shutdown && !SSL_in_init(c->ssl)) {
1081 /* openssl 1.0.2f does not like shutdown being called during
1082 * handshake, resulting in ssl_undefined_function in the error queue.
1083 */
1084 SSL_shutdown(c->ssl);
1085 } else {
1086 MSG("Tls_close_by_key: Avoiding SSL shutdown for: %s\n", URL_STR(c->url));
1087 }
1088 SSL_free(c->ssl);
1089
1090 a_Url_free(c->url);
1092 a_Klist_remove(conn_list, connkey);
1093 dFree(c);
1094 }
1095}
1096
1097/*
1098 * Connect, set a callback if it's still not completed. If completed, check
1099 * the certificate and report back to http.
1100 */
1101static void Tls_connect(int fd, int connkey)
1102{
1103 int ret;
1104 bool_t ongoing = FALSE, failed = TRUE;
1105 Conn_t *conn;
1106
1107 if (!(conn = a_Klist_get_data(conn_list, connkey))) {
1108 MSG("Tls_connect: conn for fd %d not valid\n", fd);
1109 return;
1110 }
1111
1112 if (conn->in_connect) {
1113 MSG("Tls_connect: nested call to Tls_connect(), aborting\n");
1114 abort();
1115 } else {
1116 conn->in_connect = TRUE;
1117 }
1118
1119 if (ERR_peek_error()) {
1120 unsigned long err;
1121 while ((err = ERR_get_error())) {
1122 MSG("Tls_connect: queued error: %s\n",
1123 ERR_error_string(err, NULL));
1124 }
1125 abort();
1126 }
1127
1128 ret = SSL_connect(conn->ssl);
1129
1130 a_IOwatch_remove_fd(fd, -1);
1131
1132 if (ret <= 0) {
1133 int err1_ret = SSL_get_error(conn->ssl, ret);
1134 if (err1_ret == SSL_ERROR_WANT_READ ||
1135 err1_ret == SSL_ERROR_WANT_WRITE) {
1136 int want = err1_ret == SSL_ERROR_WANT_READ ? DIO_READ : DIO_WRITE;
1137
1138 _MSG("iowatching fd %d for tls -- want %s\n", fd,
1139 err1_ret == SSL_ERROR_WANT_READ ? "read" : "write");
1140 a_IOwatch_add_fd(fd, want, Tls_connect_cb, INT2VOIDP(connkey));
1141 ongoing = TRUE;
1142 failed = FALSE;
1143 } else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
1144 /* From the OpenSSL documentation: "Note that SSL_shutdown() must not
1145 * be called if a previous fatal error has occurred on a connection
1146 * i.e. if SSL_get_error() has returned SSL_ERROR_SYSCALL or
1147 * SSL_ERROR_SSL." */
1148 conn->do_shutdown = FALSE;
1149
1150 unsigned long err2_ret = ERR_get_error();
1151
1152 if (err2_ret) {
1153 do {
1154 MSG("SSL_connect() failed: %s\n",
1155 ERR_error_string(err2_ret, NULL));
1156 } while ((err2_ret = ERR_get_error()));
1157 } else {
1158 /* nothing in the error queue */
1159 if (ret == 0) {
1160 MSG("TLS connect error: \"an EOF was observed that violates "
1161 "the protocol\"\n");
1162 /*
1163 * I presume we took too long on our side and the server grew
1164 * impatient.
1165 */
1166 } else if (ret == -1) {
1167 MSG("TLS connect error: %s\n", dStrerror(errno));
1168
1169 /* If the following can happen, I'll add code to handle it, but
1170 * I don't want to add code blindly if it isn't getting used
1171 */
1172 assert(errno != EAGAIN && errno != EINTR);
1173 } else {
1174 MSG_ERR("According to the man page for SSL_get_error(), this "
1175 "was not a possibility (ret %d).\n", ret);
1176 }
1177 }
1178 } else {
1179 MSG("SSL_get_error() returned %d on a connect.\n", err1_ret);
1180 }
1181 } else {
1182 Server_t *srv = dList_find_sorted(servers, conn->url,
1184
1185 if (srv->cert_status == CERT_STATUS_RECEIVING) {
1186 /* Making first connection with the server. Show cipher used. */
1187 SSL *ssl = conn->ssl;
1188 const char *version = SSL_get_version(ssl);
1189 const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
1190
1191 MSG("%s: %s, cipher %s\n", URL_AUTHORITY(conn->url), version,
1192 SSL_CIPHER_get_name(cipher));
1193 }
1194
1195 if (srv->cert_status == CERT_STATUS_USER_ACCEPTED ||
1196 (Tls_examine_certificate(conn->ssl, srv) != -1)) {
1197 failed = FALSE;
1198 }
1199 }
1200
1201 /*
1202 * If there were problems with the certificate, the connection may have
1203 * been closed by the server if the user responded too slowly to a popup.
1204 */
1205
1206 if (!ongoing) {
1207 if (a_Klist_get_data(conn_list, connkey)) {
1208 conn->connecting = FALSE;
1209 if (failed) {
1210 conn->in_connect = FALSE;
1211 Tls_close_by_key(connkey);
1212 /* conn is freed now */
1213 conn = NULL;
1214 }
1217 } else {
1218 MSG("Connection disappeared. Too long with a popup popped up?\n");
1219 }
1220 }
1221
1222 if (conn)
1223 conn->in_connect = FALSE;
1224}
1225
1226static void Tls_connect_cb(int fd, void *vconnkey)
1227{
1228 Tls_connect(fd, VOIDP2INT(vconnkey));
1229}
1230
1231/*
1232 * Perform the TLS handshake on an open socket.
1233 */
1234void a_Tls_openssl_connect(int fd, const DilloUrl *url)
1235{
1236 SSL *ssl;
1237 bool_t success = TRUE;
1238 int connkey = -1;
1239
1240 if (!ssl_context)
1241 success = FALSE;
1242
1243 if (success && Tls_user_said_no(url)) {
1244 success = FALSE;
1245 }
1246
1247 if (ERR_peek_error()) {
1248 unsigned long err;
1249 while ((err = ERR_get_error())) {
1250 MSG("a_Tls_openssl_connect: queued error: %s\n",
1251 ERR_error_string(err, NULL));
1252 }
1253 abort();
1254 }
1255
1256 if (success && !(ssl = SSL_new(ssl_context))) {
1257 unsigned long err_ret = ERR_get_error();
1258 do {
1259 MSG("SSL_new() failed: %s\n", ERR_error_string(err_ret, NULL));
1260 } while ((err_ret = ERR_get_error()));
1261 success = FALSE;
1262 }
1263
1264 /* assign TLS connection to this file descriptor */
1265 if (success && !SSL_set_fd(ssl, fd)) {
1266 unsigned long err_ret = ERR_get_error();
1267 do {
1268 MSG("SSL_set_fd() failed: %s\n", ERR_error_string(err_ret, NULL));
1269 } while ((err_ret = ERR_get_error()));
1270 success = FALSE;
1271 }
1272
1273 if (success)
1274 connkey = Tls_conn_new(fd, url, ssl);
1275
1276#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1277 /* Server Name Indication. From the openssl changelog, it looks like this
1278 * came along in 2010.
1279 */
1280 if (success && a_Url_host_type(URL_HOST(url)) == URL_HOST_NAME)
1281 SSL_set_tlsext_host_name(ssl, URL_HOST(url));
1282#endif
1283
1284 if (!success) {
1286 a_Http_connect_done(fd, success);
1287 } else {
1288 Tls_connect(fd, connkey);
1289 }
1290}
1291
1292/*
1293 * Traduces an SSL I/O error into something understandable by IO.c
1294 *
1295 * Returns: -1 if there is an error and sets errno to the appropriate error,
1296 * otherwise return the number of bytes read/written (which may be 0).
1297 */
1298static int Tls_handle_error(Conn_t *conn, int ret, const char *where)
1299{
1300 /* Success */
1301 if (ret > 0) {
1302 errno = 0;
1303 return ret;
1304 }
1305
1306 SSL *ssl = conn->ssl;
1307 int err1_ret = SSL_get_error(ssl, ret);
1308 if (err1_ret == SSL_ERROR_NONE) {
1309 errno = 0;
1310 return ret;
1311 } else if (err1_ret == SSL_ERROR_ZERO_RETURN) {
1312 errno = 0;
1313 return 0;
1314 } else if (err1_ret == SSL_ERROR_WANT_READ || err1_ret == SSL_ERROR_WANT_WRITE) {
1315 errno = EAGAIN;
1316 return -1;
1317 } else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
1318 conn->do_shutdown = FALSE;
1319 unsigned long err2_ret = ERR_get_error();
1320 if (err2_ret) {
1321 do {
1322 MSG("%s failed: %s\n", where, ERR_error_string(err2_ret, NULL));
1323 } while ((err2_ret = ERR_get_error()));
1324 } else {
1325 /* Provide a generic message, as there is none in the queue */
1326 const char *which = err1_ret == SSL_ERROR_SYSCALL ? "SYSCALL" : "SSL";
1327 MSG("%s failed: SSL_ERROR_%s\n", where, which);
1328 }
1329 errno = EPROTO;
1330 return -1;
1331 }
1332
1333 MSG("%s failed: SSL_get_error() returned %d\n", where, err1_ret);
1334 errno = EPROTO;
1335 return -1;
1336}
1337
1338/*
1339 * Read data from an open TLS connection.
1340 */
1341int a_Tls_openssl_read(void *conn, void *buf, size_t len)
1342{
1343 Conn_t *c = (Conn_t*)conn;
1344 return Tls_handle_error(c, SSL_read(c->ssl, buf, len), "SSL_read()");
1345}
1346
1347/*
1348 * Write data to an open TLS connection.
1349 */
1350int a_Tls_openssl_write(void *conn, void *buf, size_t len)
1351{
1352 Conn_t *c = (Conn_t*)conn;
1353 return Tls_handle_error(c, SSL_write(c->ssl, buf, len), "SSL_write()");
1354}
1355
1357{
1358 FdMapEntry_t *fme = dList_find_custom(fd_map, INT2VOIDP(fd),
1360
1361 if (fme) {
1362 Tls_close_by_key(fme->connkey);
1363 }
1364}
1365
1366static void Tls_servers_freeall(void)
1367{
1368 if (servers) {
1369 Server_t *s;
1370 int i, n = dList_length(servers);
1371
1372 for (i = 0; i < n; i++) {
1373 s = (Server_t *) dList_nth_data(servers, i);
1374 dFree(s->hostname);
1375 dFree(s);
1376 }
1378 }
1379}
1380
1381static void Tls_fd_map_remove_all(void)
1382{
1383 if (fd_map) {
1384 FdMapEntry_t *fme;
1385 int i, n = dList_length(fd_map);
1386
1387 for (i = 0; i < n; i++) {
1388 fme = (FdMapEntry_t *) dList_nth_data(fd_map, i);
1389 dFree(fme);
1390 }
1392 }
1393}
1394
1395/*
1396 * Clean up the OpenSSL library
1397 */
1399{
1400 if (ssl_context)
1401 SSL_CTX_free(ssl_context);
1404}
#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:769
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:548
int dList_length(Dlist *lp)
For completing the ADT.
Definition dlib.c:613
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:662
void dList_remove_fast(Dlist *lp, const void *data)
Remove a data item without preserving order.
Definition dlib.c:623
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:951
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:597
void * dList_find_sorted(Dlist *lp, const void *data, dCompareFunc func)
Search a sorted list.
Definition dlib.c:796
void dList_free(Dlist *lp)
Free a list (not its elements)
Definition dlib.c:564
void * dList_find_custom(Dlist *lp, const void *data, dCompareFunc func)
Search a data item using a custom function.
Definition dlib.c:704
char * dGethomedir(void)
Return the home directory in a static string (don't free)
Definition dlib.c:906
#define dStrerror
Definition dlib.h:95
#define dNew0(type, count)
Definition dlib.h:51
#define VOIDP2INT(p)
Definition dlib.h:43
#define TRUE
Definition dlib.h:23
#define FALSE
Definition dlib.h:19
#define INT2VOIDP(i)
Definition dlib.h:44
#define dNew(type, count)
Definition dlib.h:49
#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:131
Definition dlib.h:102
Dstr_char_t * str
Definition dlib.h:105
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