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