Dillo v3.2.0-170-gacf511bb
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-2026 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 int key_type, key_bits;
474 const char *type_str;
475 BIO *b;
476
477 for (i = 0; i < n; i++) {
478 cert = sk_X509_value(sk, i);
479
480 /* We are trying to find a way to get the hash function used
481 * with a certificate. This way, which is not very pleasant, puts
482 * a string such as "sha256WithRSAEncryption" in our buffer and we
483 * then trim off the "With..." part.
484 */
485 b = BIO_new(BIO_s_mem());
486 const ASN1_OBJECT *algorithm;
487 X509_ALGOR_get0(&algorithm, NULL, NULL, X509_get0_tbs_sigalg(cert));
488 rc = i2a_ASN1_OBJECT(b, algorithm);
489
490 if (rc > 0) {
491 rc = BIO_gets(b, buf, buflen);
492 }
493 if (rc <= 0) {
494 strcpy(buf, "(unknown)");
495 buf[buflen-1] = '\0';
496 } else {
497 char *s = strstr(buf, "With");
498
499 if (s) {
500 *s = '\0';
501 if (!strcmp(buf, "sha1")) {
502 if (print_chain)
503 MSG_WARN("In 2015, browsers have begun to deprecate SHA1 "
504 "certificates.\n");
505 } else if (!strncmp(buf, "md", 2) && success == TRUE) {
506 const char *msg = "A certificate in the chain uses the MD5 "
507 "signature algorithm, which is too weak "
508 "to trust.";
509 *choice = a_Dialog_choice("Dillo TLS security warning", msg,
510 "Continue", "Cancel", NULL);
511 if (*choice != 1)
512 success = FALSE;
513 }
514 }
515 }
516 BIO_free(b);
517
518 if (print_chain)
519 MSG(" %d: %s ", i, buf);
520
521 EVP_PKEY *public_key = X509_get_pubkey(cert);
522
523 /* LibreSSL returns a NULL public_key for CurveBall attacks */
524 if (public_key == NULL) {
525 if (print_chain)
526 MSG("(error)\n");
527
528 const char *err = ERR_error_string(ERR_get_error(), NULL);
529 MSG("Cannot parse public key: %s\n", err);
530
531 /* Ask the user what to do if not already failed */
532 if (success) {
533 const char *msg =
534 "The public key of a certificate in the chain cannot be decoded. "
535 "Cannot trust remote server, continue?";
536
537 *choice = a_Dialog_choice("Dillo TLS security warning", msg,
538 "Continue", "Cancel", NULL);
539
540 if (*choice != 1)
541 success = FALSE;
542 }
543 continue;
544 }
545
546#if OPENSSL_VERSION_NUMBER < 0x30000000L
547 key_type = EVP_PKEY_type(EVP_PKEY_id(public_key));
548#else
549 key_type = EVP_PKEY_type(EVP_PKEY_get_id(public_key));
550#endif
551 type_str = key_type == EVP_PKEY_RSA ? "RSA" :
552 key_type == EVP_PKEY_DSA ? "DSA" :
553 key_type == EVP_PKEY_DH ? "DH" :
554 key_type == EVP_PKEY_EC ? "EC" : "???";
555 key_bits = EVP_PKEY_bits(public_key);
556 X509_NAME_oneline(X509_get_subject_name(cert), buf, buflen);
557 buf[buflen-1] = '\0';
558 if (print_chain)
559 MSG("%d-bit %s: %s\n", key_bits, type_str, buf);
560 EVP_PKEY_free(public_key);
561
562 if (key_type == EVP_PKEY_RSA && key_bits <= 1024) {
563 if (print_chain)
564 MSG_WARN("In 2014/5, browsers have been deprecating 1024-bit "
565 "RSA keys.\n");
566 }
567 }
568
569 if (cert) {
570 X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen);
571 buf[buflen-1] = '\0';
572 if (print_chain)
573 MSG(" root: %s\n", buf);
574 }
575 }
576 return success;
577}
578
579/******************** BEGINNING OF STUFF DERIVED FROM wget-1.16.3 */
580
581#define ASTERISK_EXCLUDES_DOT /* mandated by rfc2818 */
582
583/* Return true is STRING (case-insensitively) matches PATTERN, false
584 otherwise. The recognized wildcard character is "*", which matches
585 any character in STRING except ".". Any number of the "*" wildcard
586 may be present in the pattern.
587
588 This is used to match of hosts as indicated in rfc2818: "Names may
589 contain the wildcard character * which is considered to match any
590 single domain name component or component fragment. E.g., *.a.com
591 matches foo.a.com but not bar.foo.a.com. f*.com matches foo.com but
592 not bar.com [or foo.bar.com]."
593
594 If the pattern contain no wildcards, pattern_match(a, b) is
595 equivalent to !strcasecmp(a, b). */
596
597static bool_t pattern_match (const char *pattern, const char *string)
598{
599
600 const char *p = pattern, *n = string;
601 char c;
602 for (; (c = dTolower (*p++)) != '\0'; n++)
603 if (c == '*')
604 {
605 for (c = dTolower (*p); c == '*'; c = dTolower (*++p))
606 ;
607 for (; *n != '\0'; n++)
608 if (dTolower (*n) == c && pattern_match (p, n))
609 return TRUE;
610#ifdef ASTERISK_EXCLUDES_DOT
611 else if (*n == '.')
612 return FALSE;
613#endif
614 return c == '\0';
615 }
616 else
617 {
618 if (c != dTolower (*n))
619 return FALSE;
620 }
621 return *n == '\0';
622}
623
624/*
625 * Check that the certificate corresponds to the site it's presented for.
626 *
627 * Return TRUE if the hostname matched or the user indicated acceptance.
628 * FALSE on failure.
629 */
630static bool_t Tls_check_cert_hostname(X509 *cert, const char *host,
631 int *choice)
632{
633 if (cert == NULL || host == NULL)
634 return FALSE;
635
636 char *msg;
637 GENERAL_NAMES *subjectAltNames;
638 bool_t success = TRUE, alt_name_checked = FALSE;;
639 char common_name[256];
640
641 /* Check that HOST matches the common name in the certificate.
642 #### The following remains to be done:
643
644 - When matching against common names, it should loop over all
645 common names and choose the most specific one, i.e. the last
646 one, not the first one, which the current code picks.
647
648 - Ensure that ASN1 strings from the certificate are encoded as
649 UTF-8 which can be meaningfully compared to HOST. */
650
651 subjectAltNames = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL);
652
653 if (subjectAltNames)
654 {
655 /* Test subject alternative names */
656
657 Dstr *err = dStr_new("");
658 dStr_sprintf(err, "Hostname %s does not match any of certificate's "
659 "Subject Alternative Names: ", host);
660
661 /* Do we want to check for dNSNAmes or ipAddresses (see RFC 2818)?
662 * Signal it by host_in_octet_string. */
663 ASN1_OCTET_STRING *host_in_octet_string = a2i_IPADDRESS (host);
664
665 int numaltnames = sk_GENERAL_NAME_num (subjectAltNames);
666 int i;
667 for (i=0; i < numaltnames; i++)
668 {
669 const GENERAL_NAME *name =
670 sk_GENERAL_NAME_value (subjectAltNames, i);
671 if (name)
672 {
673 if (host_in_octet_string)
674 {
675 if (name->type == GEN_IPADD)
676 {
677 /* Check for ipAddress */
678 /* TODO: Should we convert between IPv4-mapped IPv6
679 * addresses and IPv4 addresses? */
680 alt_name_checked = TRUE;
681 if (!ASN1_STRING_cmp (host_in_octet_string,
682 name->d.iPAddress))
683 break;
684 dStr_sprintfa(err, "%s ", name->d.iPAddress);
685 }
686 }
687 else if (name->type == GEN_DNS)
688 {
689 /* dNSName should be IA5String (i.e. ASCII), however who
690 * does trust CA? Convert it into UTF-8 for sure. */
691 unsigned char *name_in_utf8 = NULL;
692
693 /* Check for dNSName */
694 alt_name_checked = TRUE;
695
696 if (0 <= ASN1_STRING_to_UTF8 (&name_in_utf8, name->d.dNSName))
697 {
698 /* Compare and check for NULL attack in ASN1_STRING */
699 if (pattern_match ((char *)name_in_utf8, host) &&
700 (strlen ((char *)name_in_utf8) ==
701 (size_t)ASN1_STRING_length (name->d.dNSName)))
702 {
703 OPENSSL_free (name_in_utf8);
704 break;
705 }
706 dStr_sprintfa(err, "%s ", name_in_utf8);
707 OPENSSL_free (name_in_utf8);
708 }
709 }
710 }
711 }
712 sk_GENERAL_NAME_pop_free(subjectAltNames, GENERAL_NAME_free);
713 if (host_in_octet_string)
714 ASN1_OCTET_STRING_free(host_in_octet_string);
715
716 if (alt_name_checked == TRUE && i >= numaltnames)
717 {
718 success = FALSE;
719 *choice = a_Dialog_choice("Dillo TLS security warning",
720 err->str, "Continue", "Cancel", NULL);
721
722 switch (*choice){
723 case 1:
724 success = TRUE;
725 break;
726 case 2:
727 break;
728 default:
729 break;
730 }
731 }
732 dStr_free(err, 1);
733 }
734
735 if (alt_name_checked == FALSE)
736 {
737 /* Test commomName */
738 X509_NAME *xname = X509_get_subject_name(cert);
739 common_name[0] = '\0';
740 X509_NAME_get_text_by_NID (xname, NID_commonName, common_name,
741 sizeof (common_name));
742
743 if (!pattern_match (common_name, host))
744 {
745 success = FALSE;
746 msg = dStrconcat("Certificate common name ", common_name,
747 " doesn't match requested host name ", host, NULL);
748 *choice = a_Dialog_choice("Dillo TLS security warning",
749 msg, "Continue", "Cancel", NULL);
750 dFree(msg);
751
752 switch (*choice){
753 case 1:
754 success = TRUE;
755 break;
756 case 2:
757 break;
758 default:
759 break;
760 }
761 }
762 else
763 {
764 /* We now determine the length of the ASN1 string. If it
765 * differs from common_name's length, then there is a \0
766 * before the string terminates. This can be an instance of a
767 * null-prefix attack.
768 *
769 * https://www.blackhat.com/html/bh-usa-09/bh-usa-09-archives.html#Marlinspike
770 * */
771
772 int i = -1, j;
773 X509_NAME_ENTRY *xentry;
774 ASN1_STRING *sdata;
775
776 if (xname) {
777 for (;;)
778 {
779 j = X509_NAME_get_index_by_NID (xname, NID_commonName, i);
780 if (j == -1) break;
781 i = j;
782 }
783 }
784
785 xentry = X509_NAME_get_entry(xname,i);
786 sdata = X509_NAME_ENTRY_get_data(xentry);
787 if (strlen (common_name) != (size_t)ASN1_STRING_length (sdata))
788 {
789 success = FALSE;
790 msg = dStrconcat("Certificate common name is invalid (contains a NUL "
791 "character). This may be an indication that the "
792 "host is not who it claims to be -- that is, not "
793 "the real ", host, NULL);
794 *choice = a_Dialog_choice("Dillo TLS security warning",
795 msg, "Continue", "Cancel", NULL);
796 dFree(msg);
797
798 switch (*choice){
799 case 1:
800 success = TRUE;
801 break;
802 case 2:
803 break;
804 default:
805 break;
806 }
807 }
808 }
809 }
810 return success;
811}
812
813/******************** END OF STUFF DERIVED FROM wget-1.16.3 */
814
815/*
816 * Get the certificate at the end of the chain, or NULL on failure.
817 *
818 * Rumor has it that the stack can be NULL if a connection has been reused
819 * and that the stack can then be reconstructed if necessary, but it doesn't
820 * sound like a case we'll encounter.
821 */
822static X509 *Tls_get_end_of_chain(SSL *ssl)
823{
824 STACK_OF(X509) *sk = SSL_get_peer_cert_chain(ssl);
825
826 return sk ? sk_X509_value(sk, sk_X509_num(sk) - 1) : NULL;
827}
828
829static void Tls_get_issuer_name(X509 *cert, char *buf, uint_t buflen)
830{
831 if (cert) {
832 X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen);
833 } else {
834 strcpy(buf, "(unknown)");
835 buf[buflen-1] = '\0';
836 }
837}
838
839static void Tls_get_expiration_str(X509 *cert, char *buf, uint_t buflen)
840{
841 ASN1_TIME *exp_date = X509_get_notAfter(cert);
842 BIO *b = BIO_new(BIO_s_mem());
843 int rc = ASN1_TIME_print(b, exp_date);
844
845 if (rc > 0) {
846 rc = BIO_gets(b, buf, buflen);
847 }
848 if (rc <= 0) {
849 strcpy(buf, "(unknown)");
850 buf[buflen-1] = '\0';
851 }
852 BIO_free(b);
853}
854
855/*
856 * Examine the certificate, and, if problems are detected, ask the user what
857 * to do.
858 * Return: -1 if connection should be canceled, or 0 if it should continue.
859 */
860static int Tls_examine_certificate(SSL *ssl, Server_t *srv)
861{
862 X509 *remote_cert;
863 long st;
864 const uint_t buflen = 4096;
865 char buf[buflen], *cn, *msg;
866 int choice = -1, ret = -1;
867 char *title = dStrconcat("Dillo TLS security warning: ",srv->hostname,NULL);
868
869#if OPENSSL_VERSION_NUMBER < 0x30000000L
870 remote_cert = SSL_get_peer_certificate(ssl);
871#else
872 /* SSL_get_peer_certificate() was deprecated in 3.0.0. */
873 remote_cert = SSL_get1_peer_certificate(ssl);
874#endif
875 if (remote_cert == NULL){
876 /* Inform user that remote system cannot be trusted */
877 choice = a_Dialog_choice(title,
878 "The remote system is not presenting a certificate. "
879 "This site cannot be trusted. Sending data is not safe.",
880 "Continue", "Cancel", NULL);
881
882 /* Abort on anything but "Continue" */
883 if (choice == 1){
884 ret = 0;
885 }
886 } else if (Tls_check_cert_strength(ssl, srv, &choice) &&
887 Tls_check_cert_hostname(remote_cert, srv->hostname, &choice)) {
888 /* Figure out if (and why) the remote system can't be trusted */
889 st = SSL_get_verify_result(ssl);
890 switch (st) {
891 case X509_V_OK:
892 ret = 0;
893 break;
894 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
895 ; /* Case follows declaration */
896 /* Either self signed and untrusted */
897 /* Extract CN from certificate name information */
898 X509_NAME *subject_name = X509_get_subject_name(remote_cert);
899 char *subj = X509_NAME_oneline(subject_name, NULL, 0);
900 if ((cn = strstr(subj, "/CN=")) == NULL) {
901 strcpy(buf, "(no CN given)");
902 } else {
903 char *cn_end;
904
905 cn += 4;
906
907 if ((cn_end = strstr(cn, "/")) == NULL )
908 cn_end = cn + strlen(cn);
909
910 strncpy(buf, cn, (size_t) (cn_end - cn));
911 buf[cn_end - cn] = '\0';
912 }
913 OPENSSL_free(subj);
914 msg = dStrconcat("The remote certificate is self-signed and "
915 "untrusted. For address: ", buf, NULL);
916 choice = a_Dialog_choice(title,
917 msg, "Continue", "Cancel", "Save Certificate", NULL);
918 dFree(msg);
919
920 switch (choice){
921 case 1:
922 ret = 0;
923 break;
924 case 2:
925 break;
926 case 3:
927 /* Save certificate to a file */
928 Tls_save_certificate_home(remote_cert);
929 ret = 0;
930 break;
931 default:
932 break;
933 }
934 break;
935 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
936 case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
937 choice = a_Dialog_choice(title,
938 "The issuer for the remote certificate cannot be found. "
939 "The authenticity of the remote certificate cannot be trusted.",
940 "Continue", "Cancel", NULL);
941
942 if (choice == 1) {
943 ret = 0;
944 }
945 break;
946
947 case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
948 case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
949 case X509_V_ERR_CERT_SIGNATURE_FAILURE:
950 case X509_V_ERR_CRL_SIGNATURE_FAILURE:
951 choice = a_Dialog_choice(title,
952 "The remote certificate signature could not be read "
953 "or is invalid and should not be trusted",
954 "Continue", "Cancel", NULL);
955
956 if (choice == 1) {
957 ret = 0;
958 }
959 break;
960 case X509_V_ERR_CERT_NOT_YET_VALID:
961 case X509_V_ERR_CRL_NOT_YET_VALID:
962 choice = a_Dialog_choice(title,
963 "Part of the remote certificate is not yet valid. "
964 "Certificates usually have a range of dates over which "
965 "they are to be considered valid, and the certificate "
966 "presented has a starting validity after today's date "
967 "You should be cautious about using this site",
968 "Continue", "Cancel", NULL);
969
970 if (choice == 1) {
971 ret = 0;
972 }
973 break;
974 case X509_V_ERR_CERT_HAS_EXPIRED:
975 case X509_V_ERR_CRL_HAS_EXPIRED:
976 Tls_get_expiration_str(remote_cert, buf, buflen);
977 msg = dStrconcat("The remote certificate expired on: ", buf,
978 ". This site can no longer be trusted.", NULL);
979
980 choice = a_Dialog_choice(title, msg, "Continue", "Cancel", NULL);
981 if (choice == 1) {
982 ret = 0;
983 }
984 dFree(msg);
985 break;
986 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
987 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
988 case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
989 case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
990 choice = a_Dialog_choice(title,
991 "There was an error in the certificate presented. "
992 "Some of the certificate data was improperly formatted "
993 "making it impossible to determine if the certificate "
994 "is valid. You should not trust this certificate.",
995 "Continue", "Cancel", NULL);
996 if (choice == 1) {
997 ret = 0;
998 }
999 break;
1000 case X509_V_ERR_INVALID_CA:
1001 case X509_V_ERR_INVALID_PURPOSE:
1002 case X509_V_ERR_CERT_UNTRUSTED:
1003 case X509_V_ERR_CERT_REJECTED:
1004 case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
1005 choice = a_Dialog_choice(title,
1006 "One of the certificates in the chain is being used "
1007 "incorrectly (possibly due to configuration problems "
1008 "with the remote system. The connection should not "
1009 "be trusted",
1010 "Continue", "Cancel", NULL);
1011 if (choice == 1) {
1012 ret = 0;
1013 }
1014 break;
1015 case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
1016 case X509_V_ERR_AKID_SKID_MISMATCH:
1017 case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
1018 choice = a_Dialog_choice(title,
1019 "Some of the information presented by the remote system "
1020 "does not match other information presented. "
1021 "This may be an attempt to eavesdrop on communications",
1022 "Continue", "Cancel", NULL);
1023 if (choice == 1) {
1024 ret = 0;
1025 }
1026 break;
1027 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
1028 Tls_get_issuer_name(Tls_get_end_of_chain(ssl), buf, buflen);
1029 msg = dStrconcat("Certificate chain led to a self-signed certificate "
1030 "instead of a trusted root. Name: ", buf , NULL);
1031 choice = a_Dialog_choice(title, msg, "Continue", "Cancel", NULL);
1032 if (choice == 1) {
1033 ret = 0;
1034 }
1035 dFree(msg);
1036 break;
1037 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
1038 Tls_get_issuer_name(Tls_get_end_of_chain(ssl), buf, buflen);
1039 msg = dStrconcat("The issuer certificate of an untrusted certificate "
1040 "cannot be found. Issuer: ", buf, NULL);
1041 choice = a_Dialog_choice(title, msg, "Continue", "Cancel", NULL);
1042 if (choice == 1) {
1043 ret = 0;
1044 }
1045 dFree(msg);
1046 break;
1047 default: /* Need to add more options later */
1048 snprintf(buf, 80,
1049 "The remote certificate cannot be verified (code %ld)", st);
1050 choice = a_Dialog_choice(title,
1051 buf, "Continue", "Cancel", NULL);
1052 /* abort on anything but "Continue" */
1053 if (choice == 1){
1054 ret = 0;
1055 }
1056 }
1057 X509_free(remote_cert);
1058 remote_cert = 0;
1059 }
1060 dFree(title);
1061
1062 if (choice == -1) {
1063 srv->cert_status = CERT_STATUS_CLEAN; /* no warning popups */
1064 } else if (choice == 1) {
1065 srv->cert_status = CERT_STATUS_USER_ACCEPTED; /* clicked Continue */
1066 } else {
1067 /* 2 for Cancel, or 0 when window closed. Treating 0 as meaning 'No' is
1068 * probably not exactly correct, but adding complexity to handle this
1069 * obscure case does not seem justifiable.
1070 */
1071 srv->cert_status = CERT_STATUS_BAD;
1072 }
1073 return ret;
1074}
1075
1076/*
1077 * If the connection was closed before we got the certificate, we need to
1078 * reset state so that we'll try again.
1079 */
1081{
1082 if (servers) {
1084
1085 if (s && s->cert_status == CERT_STATUS_RECEIVING)
1086 s->cert_status = CERT_STATUS_NONE;
1087 }
1088}
1089
1090/*
1091 * Close an open TLS connection.
1092 */
1093static void Tls_close_by_key(int connkey)
1094{
1095 _MSG("Tls_close_by_key: connkey=%d\n", connkey);
1096
1097 Conn_t *c;
1098
1099 if ((c = a_Klist_get_data(conn_list, connkey))) {
1101 if (c->connecting) {
1102 a_IOwatch_remove_fd(c->fd, -1);
1103 dClose(c->fd);
1104 }
1105 if (c->do_shutdown && !SSL_in_init(c->ssl)) {
1106 /* openssl 1.0.2f does not like shutdown being called during
1107 * handshake, resulting in ssl_undefined_function in the error queue.
1108 */
1109 if (SSL_shutdown(c->ssl) < 0) {
1110 /* Drain error queue before making more calls */
1111 for (int e = ERR_get_error(); e; e = ERR_get_error()) {
1112 MSG("SSL_shutdown() failed with %s for url: %s\n",
1113 ERR_error_string(e, NULL), URL_STR(c->url));
1114 }
1115 }
1116 } else {
1117 MSG("Tls_close_by_key: Avoiding SSL shutdown for: %s\n", URL_STR(c->url));
1118 }
1119 SSL_free(c->ssl);
1120
1121 a_Url_free(c->url);
1123 a_Klist_remove(conn_list, connkey);
1124 dFree(c);
1125 }
1126}
1127
1128/*
1129 * Connect, set a callback if it's still not completed. If completed, check
1130 * the certificate and report back to http.
1131 */
1132static void Tls_connect(int fd, int connkey)
1133{
1134 int ret;
1135 bool_t ongoing = FALSE, failed = TRUE;
1136 Conn_t *conn;
1137
1138 _MSG("Tls_connect: fd=%d connkey=%d\n", fd, connkey);
1139
1140 if (!(conn = a_Klist_get_data(conn_list, connkey))) {
1141 MSG("Tls_connect: conn for fd %d not valid\n", fd);
1142 return;
1143 }
1144
1145 if (conn->in_connect) {
1146 MSG("Tls_connect: nested call to Tls_connect(), aborting\n");
1147 abort();
1148 } else {
1149 conn->in_connect = TRUE;
1150 }
1151
1152 if (ERR_peek_error()) {
1153 unsigned long err;
1154 while ((err = ERR_get_error())) {
1155 MSG("Tls_connect: queued error: %s\n",
1156 ERR_error_string(err, NULL));
1157 }
1158 abort();
1159 }
1160
1161 ret = SSL_connect(conn->ssl);
1162
1163 a_IOwatch_remove_fd(fd, -1);
1164
1165 if (ret <= 0) {
1166 int err1_ret = SSL_get_error(conn->ssl, ret);
1167 if (err1_ret == SSL_ERROR_WANT_READ ||
1168 err1_ret == SSL_ERROR_WANT_WRITE) {
1169 int want = err1_ret == SSL_ERROR_WANT_READ ? DIO_READ : DIO_WRITE;
1170
1171 _MSG("iowatching fd %d for tls -- want %s\n", fd,
1172 err1_ret == SSL_ERROR_WANT_READ ? "read" : "write");
1173 a_IOwatch_add_fd(fd, want, Tls_connect_cb, INT2VOIDP(connkey));
1174 ongoing = TRUE;
1175 failed = FALSE;
1176 } else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
1177 /* From the OpenSSL documentation: "Note that SSL_shutdown() must not
1178 * be called if a previous fatal error has occurred on a connection
1179 * i.e. if SSL_get_error() has returned SSL_ERROR_SYSCALL or
1180 * SSL_ERROR_SSL." */
1181 conn->do_shutdown = FALSE;
1182
1183 unsigned long err2_ret = ERR_get_error();
1184
1185 if (err2_ret) {
1186 do {
1187 MSG("SSL_connect() failed: %s\n",
1188 ERR_error_string(err2_ret, NULL));
1189 } while ((err2_ret = ERR_get_error()));
1190 } else {
1191 /* nothing in the error queue */
1192 if (ret == 0) {
1193 MSG("TLS connect error: \"an EOF was observed that violates "
1194 "the protocol\"\n");
1195 /*
1196 * I presume we took too long on our side and the server grew
1197 * impatient.
1198 */
1199 } else if (ret == -1) {
1200 MSG("TLS connect error: %s\n", dStrerror(errno));
1201
1202 /* If the following can happen, I'll add code to handle it, but
1203 * I don't want to add code blindly if it isn't getting used
1204 */
1205 assert(errno != EAGAIN && errno != EINTR);
1206 } else {
1207 MSG_ERR("According to the man page for SSL_get_error(), this "
1208 "was not a possibility (ret %d).\n", ret);
1209 }
1210 }
1211 } else {
1212 MSG("SSL_get_error() returned %d on a connect.\n", err1_ret);
1213 }
1214 } else {
1215 Server_t *srv = dList_find_sorted(servers, conn->url,
1217
1218 if (srv->cert_status == CERT_STATUS_RECEIVING) {
1219 /* Making first connection with the server. Show cipher used. */
1220 SSL *ssl = conn->ssl;
1221 const char *version = SSL_get_version(ssl);
1222 const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
1223
1224 MSG("%s: %s, cipher %s\n", URL_AUTHORITY(conn->url), version,
1225 SSL_CIPHER_get_name(cipher));
1226 }
1227
1228 if (srv->cert_status == CERT_STATUS_USER_ACCEPTED ||
1229 (Tls_examine_certificate(conn->ssl, srv) != -1)) {
1230 failed = FALSE;
1231 }
1232 }
1233
1234 /*
1235 * If there were problems with the certificate, the connection may have
1236 * been closed by the server if the user responded too slowly to a popup.
1237 */
1238
1239 conn = a_Klist_get_data(conn_list, connkey);
1240
1241 if (!ongoing) {
1242 if (conn) {
1243 conn->connecting = FALSE;
1244 if (failed) {
1245 conn->in_connect = FALSE;
1246 Tls_close_by_key(connkey);
1247 /* conn is freed now */
1248 conn = NULL;
1249 }
1252 } else {
1253 MSG("Connection disappeared. Too long with a popup popped up?\n");
1254 }
1255 }
1256
1257 if (conn)
1258 conn->in_connect = FALSE;
1259}
1260
1261static void Tls_connect_cb(int fd, void *vconnkey)
1262{
1263 Tls_connect(fd, VOIDP2INT(vconnkey));
1264}
1265
1266/*
1267 * Perform the TLS handshake on an open socket.
1268 */
1269void a_Tls_openssl_connect(int fd, const DilloUrl *url)
1270{
1271 _MSG("a_Tls_openssl_connect: fd=%d url=%s\n", fd, URL_STR(url));
1272
1273 SSL *ssl;
1274 bool_t success = TRUE;
1275 int connkey = -1;
1276
1277 if (!ssl_context)
1278 success = FALSE;
1279
1280 if (success && Tls_user_said_no(url)) {
1281 success = FALSE;
1282 }
1283
1284 if (ERR_peek_error()) {
1285 unsigned long err;
1286 while ((err = ERR_get_error())) {
1287 MSG("a_Tls_openssl_connect: queued error: %s\n",
1288 ERR_error_string(err, NULL));
1289 }
1290 abort();
1291 }
1292
1293 if (success && !(ssl = SSL_new(ssl_context))) {
1294 unsigned long err_ret = ERR_get_error();
1295 do {
1296 MSG("SSL_new() failed: %s\n", ERR_error_string(err_ret, NULL));
1297 } while ((err_ret = ERR_get_error()));
1298 success = FALSE;
1299 }
1300
1301 /* assign TLS connection to this file descriptor */
1302 if (success && !SSL_set_fd(ssl, fd)) {
1303 unsigned long err_ret = ERR_get_error();
1304 do {
1305 MSG("SSL_set_fd() failed: %s\n", ERR_error_string(err_ret, NULL));
1306 } while ((err_ret = ERR_get_error()));
1307 success = FALSE;
1308 }
1309
1310 if (success)
1311 connkey = Tls_conn_new(fd, url, ssl);
1312
1313#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1314 /* Server Name Indication. From the openssl changelog, it looks like this
1315 * came along in 2010.
1316 */
1317 if (success && a_Url_host_type(URL_HOST(url)) == URL_HOST_NAME)
1318 SSL_set_tlsext_host_name(ssl, URL_HOST(url));
1319#endif
1320
1321 if (!success) {
1323 a_Http_connect_done(fd, success);
1324 } else {
1325 Tls_connect(fd, connkey);
1326 }
1327}
1328
1329/*
1330 * Traduces an SSL I/O error into something understandable by IO.c
1331 *
1332 * Returns: -1 if there is an error and sets errno to the appropriate error,
1333 * otherwise return the number of bytes read/written (which may be 0).
1334 */
1335static int Tls_handle_error(Conn_t *conn, int ret, const char *where)
1336{
1337 /* Success */
1338 if (ret > 0) {
1339 errno = 0;
1340 return ret;
1341 }
1342
1343 SSL *ssl = conn->ssl;
1344 int err1_ret = SSL_get_error(ssl, ret);
1345 if (err1_ret == SSL_ERROR_NONE) {
1346 errno = 0;
1347 return ret;
1348 } else if (err1_ret == SSL_ERROR_ZERO_RETURN) {
1349 errno = 0;
1350 return 0;
1351 } else if (err1_ret == SSL_ERROR_WANT_READ || err1_ret == SSL_ERROR_WANT_WRITE) {
1352 errno = EAGAIN;
1353 return -1;
1354 } else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
1355 conn->do_shutdown = FALSE;
1356 unsigned long err2_ret = ERR_get_error();
1357 if (err2_ret) {
1358 do {
1359 MSG("%s failed: %s\n", where, ERR_error_string(err2_ret, NULL));
1360 } while ((err2_ret = ERR_get_error()));
1361 } else {
1362 /* Provide a generic message, as there is none in the queue */
1363 const char *which = err1_ret == SSL_ERROR_SYSCALL ? "SYSCALL" : "SSL";
1364 MSG("%s failed: SSL_ERROR_%s\n", where, which);
1365 }
1366 errno = EPROTO;
1367 return -1;
1368 }
1369
1370 MSG("%s failed: SSL_get_error() returned %d\n", where, err1_ret);
1371 errno = EPROTO;
1372 return -1;
1373}
1374
1375/*
1376 * Read data from an open TLS connection.
1377 */
1378int a_Tls_openssl_read(void *conn, void *buf, size_t len)
1379{
1380 Conn_t *c = (Conn_t*)conn;
1381 return Tls_handle_error(c, SSL_read(c->ssl, buf, len), "SSL_read()");
1382}
1383
1384/*
1385 * Write data to an open TLS connection.
1386 */
1387int a_Tls_openssl_write(void *conn, void *buf, size_t len)
1388{
1389 Conn_t *c = (Conn_t*)conn;
1390 return Tls_handle_error(c, SSL_write(c->ssl, buf, len), "SSL_write()");
1391}
1392
1394{
1395 FdMapEntry_t *fme = dList_find_custom(fd_map, INT2VOIDP(fd),
1397
1398 if (fme) {
1399 Tls_close_by_key(fme->connkey);
1400 }
1401}
1402
1403static void Tls_servers_freeall(void)
1404{
1405 if (servers) {
1406 Server_t *s;
1407 int i, n = dList_length(servers);
1408
1409 for (i = 0; i < n; i++) {
1410 s = (Server_t *) dList_nth_data(servers, i);
1411 dFree(s->hostname);
1412 dFree(s);
1413 }
1415 }
1416}
1417
1418static void Tls_fd_map_remove_all(void)
1419{
1420 if (fd_map) {
1421 FdMapEntry_t *fme;
1422 int i, n = dList_length(fd_map);
1423
1424 for (i = 0; i < n; i++) {
1425 fme = (FdMapEntry_t *) dList_nth_data(fd_map, i);
1426 dFree(fme);
1427 }
1429 }
1430}
1431
1432/*
1433 * Clean up the OpenSSL library
1434 */
1436{
1437 if (ssl_context)
1438 SSL_CTX_free(ssl_context);
1441}
#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:370
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