summaryrefslogtreecommitdiff
path: root/src/IO/tls.c
diff options
context:
space:
mode:
authorRodrigo Arias Mallo <rodarima@gmail.com>2023-12-21 21:23:50 +0100
committerRodrigo Arias Mallo <rodarima@gmail.com>2023-12-30 01:37:14 +0100
commit6ef3b839729f7e8ccd0a8c88374567b497088512 (patch)
tree02ea0686040af67431bb70d605ed068cd73a1a4c /src/IO/tls.c
parentb43e4309e694061c6a14b6e2629b870e9302c287 (diff)
Move mbedTLS implementation to tls_mbedtls.c
Diffstat (limited to 'src/IO/tls.c')
-rw-r--r--src/IO/tls.c1217
1 files changed, 0 insertions, 1217 deletions
diff --git a/src/IO/tls.c b/src/IO/tls.c
deleted file mode 100644
index c6b04b8f..00000000
--- a/src/IO/tls.c
+++ /dev/null
@@ -1,1217 +0,0 @@
-/*
- * File: tls.c
- *
- * Copyright (C) 2011 Benjamin Johnson <obeythepenguin@users.sourceforge.net>
- * (for the https code offered from dplus browser that formed the basis...)
- * Copyright 2016 corvid
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- */
-
-/*
- * https://www.ssllabs.com/ssltest/viewMyClient.html
- * https://badssl.com
- *
- * Using TLS in Applications: https://datatracker.ietf.org/wg/uta/documents/
- * TLS: https://datatracker.ietf.org/wg/tls/documents/
- */
-
-#include "config.h"
-#include "../msg.h"
-
-#ifndef ENABLE_SSL
-
-void a_Tls_init()
-{
- MSG("TLS: Disabled at compilation time.\n");
-}
-
-#else
-
-#include <assert.h>
-#include <errno.h>
-
-#include "../../dlib/dlib.h"
-#include "../dialog.hh"
-#include "../klist.h"
-#include "iowatch.hh"
-#include "tls.h"
-#include "Url.h"
-
-#include <mbedtls/platform.h> /* WORKAROUND: mbed TLS 2.3.0 ssl.h needs it */
-#include <mbedtls/ssl.h>
-#include <mbedtls/ctr_drbg.h> /* random number generator */
-#include <mbedtls/entropy.h>
-#include <mbedtls/error.h>
-#include <mbedtls/oid.h>
-#include <mbedtls/x509.h>
-#include <mbedtls/net.h> /* net_send, net_recv */
-
-#define CERT_STATUS_NONE 0
-#define CERT_STATUS_RECEIVING 1
-#define CERT_STATUS_CLEAN 2
-#define CERT_STATUS_BAD 3
-#define CERT_STATUS_USER_ACCEPTED 4
-
-typedef struct {
- char *hostname;
- int port;
- int cert_status;
-} Server_t;
-
-typedef struct {
- char *name;
- Dlist *servers;
-} CertAuth_t;
-
-typedef struct {
- int fd;
- int connkey;
-} FdMapEntry_t;
-
-/*
- * Data type for TLS connection information
- */
-typedef struct {
- int fd;
- DilloUrl *url;
- mbedtls_ssl_context *ssl;
- bool_t connecting;
-} Conn_t;
-
-/* List of active TLS connections */
-static Klist_t *conn_list = NULL;
-
-static bool_t ssl_enabled = TRUE;
-static mbedtls_ssl_config ssl_conf;
-static mbedtls_x509_crt cacerts;
-static mbedtls_ctr_drbg_context ctr_drbg;
-static mbedtls_entropy_context entropy;
-
-static Dlist *servers;
-static Dlist *cert_authorities;
-static Dlist *fd_map;
-
-static void Tls_handshake_cb(int fd, void *vconnkey);
-
-/*
- * Compare by FD.
- */
-static int Tls_fd_map_cmp(const void *v1, const void *v2)
-{
- int fd = VOIDP2INT(v2);
- const FdMapEntry_t *e = v1;
-
- return (fd != e->fd);
-}
-
-static void Tls_fd_map_add_entry(int fd, int connkey)
-{
- FdMapEntry_t *e = dNew0(FdMapEntry_t, 1);
- e->fd = fd;
- e->connkey = connkey;
-
- if (dList_find_custom(fd_map, INT2VOIDP(e->fd), Tls_fd_map_cmp)) {
- MSG_ERR("TLS FD ENTRY ALREADY FOUND FOR %d\n", e->fd);
- assert(0);
- }
-
- dList_append(fd_map, e);
-//MSG("ADD ENTRY %d %s\n", e->fd, URL_STR(sd->url));
-}
-
-/*
- * Remove and free entry from fd_map.
- */
-static void Tls_fd_map_remove_entry(int fd)
-{
- void *data = dList_find_custom(fd_map, INT2VOIDP(fd), Tls_fd_map_cmp);
-
-//MSG("REMOVE ENTRY %d\n", fd);
- if (data) {
- dList_remove_fast(fd_map, data);
- dFree(data);
- } else {
- MSG("TLS FD ENTRY NOT FOUND FOR %d\n", fd);
- }
-}
-
-/*
- * Return TLS connection information for a given file
- * descriptor, or NULL if no TLS connection was found.
- */
-void *a_Tls_connection(int fd)
-{
- Conn_t *conn;
-
- if (fd_map) {
- FdMapEntry_t *fme = dList_find_custom(fd_map, INT2VOIDP(fd),
- Tls_fd_map_cmp);
-
- if (fme && (conn = a_Klist_get_data(conn_list, fme->connkey)))
- return conn;
- }
- return NULL;
-}
-
-/*
- * Add a new TLS connection information node.
- */
-static Conn_t *Tls_conn_new(int fd, const DilloUrl *url,
- mbedtls_ssl_context *ssl)
-{
- Conn_t *conn = dNew0(Conn_t, 1);
- conn->fd = fd;
- conn->url = a_Url_dup(url);
- conn->ssl = ssl;
- conn->connecting = TRUE;
- return conn;
-}
-
-static int Tls_make_conn_key(Conn_t *conn)
-{
- int key = a_Klist_insert(&conn_list, conn);
-
- Tls_fd_map_add_entry(conn->fd, key);
-
- return key;
-}
-
-/*
- * Load certificates from a given filename.
- */
-static void Tls_load_certificates_from_file(const char *const filename)
-{
- int ret = mbedtls_x509_crt_parse_file(&cacerts, filename);
-
- if (ret < 0) {
- if (ret == MBEDTLS_ERR_PK_FILE_IO_ERROR) {
- /* can't read from file */
- } else {
- MSG("Failed to parse certificates from %s (returned -0x%04x)\n",
- filename, -ret);
- }
- }
-}
-
-/*
- * Load certificates from a given pathname.
- */
-static void Tls_load_certificates_from_path(const char *const pathname)
-{
- int ret = mbedtls_x509_crt_parse_path(&cacerts, pathname);
-
- if (ret < 0) {
- if (ret == MBEDTLS_ERR_X509_FILE_IO_ERROR) {
- /* can't read from path */
- } else {
- MSG("Failed to parse certificates from %s (returned -0x%04x)\n",
- pathname, -ret);
- }
- }
-}
-
-/*
- * Remove duplicate certificates.
- */
-static void Tls_remove_duplicate_certificates()
-{
- mbedtls_x509_crt *cp, *curr = &cacerts;
-
- while (curr) {
- cp = curr;
- while (cp->next) {
- if (curr->serial.len == cp->next->serial.len &&
- !memcmp(curr->serial.p, cp->next->serial.p, curr->serial.len) &&
- curr->subject_raw.len == cp->next->subject_raw.len &&
- !memcmp(curr->subject_raw.p, cp->next->subject_raw.p,
- curr->subject_raw.len)) {
- mbedtls_x509_crt *duplicate = cp->next;
-
- cp->next = duplicate->next;
-
- /* clearing the next field prevents it from freeing the whole
- * chain of certificates
- */
- duplicate->next = NULL;
- mbedtls_x509_crt_free(duplicate);
- dFree(duplicate);
- } else {
- cp = cp->next;
- }
- }
- curr = curr->next;
- }
-}
-
-/*
- * Load trusted certificates.
- */
-static void Tls_load_certificates()
-{
- /* curl-7.37.1 says that the following bundle locations are used on "Debian
- * systems", "Redhat and Mandriva", "old(er) Redhat", "FreeBSD", and
- * "OpenBSD", respectively -- and that the /etc/ssl/certs/ path is needed on
- * "SUSE". No doubt it's all changed some over time, but this gives us
- * something to work with.
- */
- uint_t u;
- char *userpath;
- mbedtls_x509_crt *curr;
-
- static const char *const ca_files[] = {
- "/etc/ssl/certs/ca-certificates.crt",
- "/etc/pki/tls/certs/ca-bundle.crt",
- "/usr/share/ssl/certs/ca-bundle.crt",
- "/usr/local/share/certs/ca-root.crt",
- "/etc/ssl/cert.pem",
- CA_CERTS_FILE
- };
-
- static const char *const ca_paths[] = {
- "/etc/ssl/certs/",
- CA_CERTS_DIR
- };
-
- for (u = 0; u < sizeof(ca_files)/sizeof(ca_files[0]); u++) {
- if (*ca_files[u])
- Tls_load_certificates_from_file(ca_files[u]);
- }
-
- for (u = 0; u < sizeof(ca_paths)/sizeof(ca_paths[0]); u++) {
- if (*ca_paths[u]) {
- Tls_load_certificates_from_path(ca_paths[u]);
- }
- }
-
- userpath = dStrconcat(dGethomedir(), "/.dillo/certs/", NULL);
- Tls_load_certificates_from_path(userpath);
- dFree(userpath);
-
- Tls_remove_duplicate_certificates();
-
- /* Count our trusted certificates */
- u = 0;
- if (cacerts.next) {
- u++;
- for (curr = cacerts.next; curr; curr = curr->next)
- u++;
- } else {
- mbedtls_x509_crt empty;
- mbedtls_x509_crt_init(&empty);
-
- if (memcmp(&cacerts, &empty, sizeof(mbedtls_x509_crt)))
- u++;
- }
-
- MSG("Trusting %u TLS certificate%s.\n", u, u==1 ? "" : "s");
-}
-
-/*
- * Remove the pre-shared key ciphersuites. There are lots of them,
- * and we aren't making any use of them.
- */
-static void Tls_remove_psk_ciphersuites()
-{
- const mbedtls_ssl_ciphersuite_t *cs_info;
- int *our_ciphers, *q;
- int n = 0;
-
- const int *default_ciphers = mbedtls_ssl_list_ciphersuites(),
- *p = default_ciphers;
-
- /* count how many we will want */
- while (*p) {
- cs_info = mbedtls_ssl_ciphersuite_from_id(*p);
- if (!mbedtls_ssl_ciphersuite_uses_psk(cs_info))
- n++;
- p++;
- }
- n++;
- our_ciphers = dNew(int, n);
-
- /* iterate through again and copy them over */
- p = default_ciphers;
- q = our_ciphers;
- while (*p) {
- cs_info = mbedtls_ssl_ciphersuite_from_id(*p);
-
- if (!mbedtls_ssl_ciphersuite_uses_psk(cs_info))
- *q++ = *p;
- p++;
- }
- *q = 0;
-
- mbedtls_ssl_conf_ciphersuites(&ssl_conf, our_ciphers);
-}
-
-/*
- * Initialize the mbed TLS library.
- */
-void a_Tls_init(void)
-{
- int ret;
-
- /* As of 2.3.0 in 2016, the 'default' profile allows SHA1, RIPEMD160,
- * and SHA224 (in addition to the stronger ones), and the 'next' profile
- * doesn't allow anything below SHA256. Since we're never going to hear
- * when/if RIPEMD160 and SHA224 are deprecated, and they're obscure enough
- * not to encounter, let's not allow those.
- * These profiles are for certificates, and mbed tls points out that these
- * have nothing to do with hashes during handshakes.
- * Their 'next' profile only allows "Curves at or above 128-bit security
- * level". For now, we follow 'default' and allow all curves.
- */
- static const mbedtls_x509_crt_profile prof = {
- MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA1 ) |
- MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
- MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ) |
- MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA512 ),
- 0xFFFFFFF, /* Any PK alg */
- 0xFFFFFFF, /* Any curve */
- 2048,
- };
-
- mbedtls_ssl_config_init(&ssl_conf);
-
- mbedtls_ssl_config_defaults(&ssl_conf, MBEDTLS_SSL_IS_CLIENT,
- MBEDTLS_SSL_TRANSPORT_STREAM,
- MBEDTLS_SSL_PRESET_DEFAULT);
- mbedtls_ssl_conf_cert_profile(&ssl_conf, &prof);
-
- /*
- * There are security concerns surrounding session tickets --
- * wrecking forward security, for instance.
- */
- mbedtls_ssl_conf_session_tickets(&ssl_conf,
- MBEDTLS_SSL_SESSION_TICKETS_DISABLED);
-
- Tls_remove_psk_ciphersuites();
-
- mbedtls_x509_crt_init(&cacerts); /* trusted root certificates */
- mbedtls_ctr_drbg_init(&ctr_drbg); /* Counter mode Deterministic Random Byte
- * Generator */
- mbedtls_entropy_init(&entropy);
-
- if((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
- (unsigned char*)"dillo tls", 9))) {
- ssl_enabled = FALSE;
- MSG_ERR("tls: mbedtls_ctr_drbg_seed() failed. TLS disabled.\n");
- return;
- }
-
- mbedtls_ssl_conf_authmode(&ssl_conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
- mbedtls_ssl_conf_ca_chain(&ssl_conf, &cacerts, NULL);
- mbedtls_ssl_conf_rng(&ssl_conf, mbedtls_ctr_drbg_random, &ctr_drbg);
-
- fd_map = dList_new(20);
- servers = dList_new(8);
- cert_authorities = dList_new(12);
-
- Tls_load_certificates();
-}
-
-/*
- * Ordered comparison of servers.
- */
-static int Tls_servers_cmp(const void *v1, const void *v2)
-{
- const Server_t *s1 = (const Server_t *)v1, *s2 = (const Server_t *)v2;
- int cmp = dStrAsciiCasecmp(s1->hostname, s2->hostname);
-
- if (!cmp)
- cmp = s1->port - s2->port;
- return cmp;
-}
-/*
- * Ordered comparison of server with URL.
- */
-static int Tls_servers_by_url_cmp(const void *v1, const void *v2)
-{
- const Server_t *s = (const Server_t *)v1;
- const DilloUrl *url = (const DilloUrl *)v2;
-
- int cmp = dStrAsciiCasecmp(s->hostname, URL_HOST(url));
-
- if (!cmp)
- cmp = s->port - URL_PORT(url);
- return cmp;
-}
-
-/*
- * The purpose here is to permit a single initial connection to a server.
- * Once we have the certificate, know whether we like it -- and whether the
- * user accepts it -- HTTP can run through queued sockets as normal.
- *
- * Return: TLS_CONNECT_READY or TLS_CONNECT_NOT_YET or TLS_CONNECT_NEVER.
- */
-int a_Tls_connect_ready(const DilloUrl *url)
-{
- Server_t *s;
- int ret = TLS_CONNECT_READY;
-
- dReturn_val_if_fail(ssl_enabled, TLS_CONNECT_NEVER);
-
- if ((s = dList_find_sorted(servers, url, Tls_servers_by_url_cmp))) {
- if (s->cert_status == CERT_STATUS_RECEIVING)
- ret = TLS_CONNECT_NOT_YET;
- else if (s->cert_status == CERT_STATUS_BAD)
- ret = TLS_CONNECT_NEVER;
-
- if (s->cert_status == CERT_STATUS_NONE)
- s->cert_status = CERT_STATUS_RECEIVING;
- } else {
- s = dNew(Server_t, 1);
-
- s->hostname = dStrdup(URL_HOST(url));
- s->port = URL_PORT(url);
- s->cert_status = CERT_STATUS_RECEIVING;
- dList_insert_sorted(servers, s, Tls_servers_cmp);
- }
- return ret;
-}
-
-static int Tls_cert_status(const DilloUrl *url)
-{
- Server_t *s = dList_find_sorted(servers, url, Tls_servers_by_url_cmp);
-
- return s ? s->cert_status : CERT_STATUS_NONE;
-}
-
-/*
- * Did we find problems with the certificate, and did the user proceed to
- * reject the connection?
- */
-static int Tls_user_said_no(const DilloUrl *url)
-{
- return Tls_cert_status(url) == CERT_STATUS_BAD;
-}
-
-/*
- * Did everything seem proper with the certificate -- no warnings to
- * click through?
- */
-int a_Tls_certificate_is_clean(const DilloUrl *url)
-{
- return Tls_cert_status(url) == CERT_STATUS_CLEAN;
-}
-
-#if 0
-/*
- * Print certificate and its chain of issuer certificates.
- */
-static void Tls_print_cert_chain(const mbedtls_x509_crt *cert)
-{
- /* print for first connection to server */
- const mbedtls_x509_crt *last_cert;
- const uint_t buflen = 2048;
- char buf[buflen];
- int key_bits;
- const char *sigalg;
-
- while (cert) {
- if (cert->sig_md == MBEDTLS_MD_SHA1) {
- MSG_WARN("In 2015, browsers have begun to deprecate SHA1 "
- "certificates.\n");
- }
-
- if (mbedtls_oid_get_sig_alg_desc(&cert->sig_oid, &sigalg))
- sigalg = "(??" ")";
-
- key_bits = mbedtls_pk_get_bitlen(&cert->pk);
- mbedtls_x509_dn_gets(buf, buflen, &cert->subject);
- MSG("%d-bit %s: %s\n", key_bits, sigalg, buf);
-
- last_cert = cert;
- cert = cert->next;
- }
- if (last_cert) {
- mbedtls_x509_dn_gets(buf, buflen, &last_cert->issuer);
- MSG("root: %s\n", buf);
- }
-}
-#endif
-
-/*
- * Generate dialog msg for expired cert.
- */
-static void Tls_cert_expired(const mbedtls_x509_crt *cert, Dstr *ds)
-{
- const mbedtls_x509_time *date = &cert->valid_to;
-
- dStr_sprintfa(ds,"Certificate expired at: %04d/%02d/%02d %02d:%02d:%02d.\n",
- date->year, date->mon, date->day, date->hour, date->min,
- date->sec);
-}
-
-/*
- * Generate dialog msg when certificate is not for this host.
- */
-static void Tls_cert_cn_mismatch(const mbedtls_x509_crt *cert, Dstr *ds)
-{
- const uint_t buflen = 2048;
- char cert_info_buf[buflen];
- char *san, *s;
-
- dStr_append(ds, "This host is not one of the hostnames listed on the TLS "
- "certificate that it sent");
- /*
- *
- * Taking the human-readable certificate info and scraping it is brittle
- * and horrible, but the alternative is to mimic
- * x509_info_subject_alt_name(), an option that seems equally brittle and
- * horrible.
- *
- * Once I find a case where SAN isn't used, I can add code to work with
- * the subject field as well.
- *
- */
- mbedtls_x509_crt_info(cert_info_buf, buflen, "", cert);
-
- if ((san = strstr(cert_info_buf, "subject alt name : "))) {
- san += 20;
- s = strchr(san, '\n');
- if (s) {
- *s = '\0';
- dStr_sprintfa(ds, " (%s)", san);
- }
- }
- dStr_append(ds, ".\n");
-}
-
-/*
- * Generate dialog msg when certificate is not trusted.
- */
-static void Tls_cert_trust_chain_failed(const mbedtls_x509_crt *cert, Dstr *ds)
-{
- const uint_t buflen = 2048;
- char buf[buflen];
-
- while (cert->next)
- cert = cert->next;
- mbedtls_x509_dn_gets(buf, buflen, &cert->issuer);
-
- dStr_sprintfa(ds, "Couldn't reach any trusted root certificate from "
- "supplied certificate. The issuer at the end of the "
- "chain was: \"%s\"\n", buf);
-}
-
-/*
- * Generate dialog msg when certificate start date is in the future.
- */
-static void Tls_cert_not_valid_yet(const mbedtls_x509_crt *cert, Dstr *ds)
-{
- const mbedtls_x509_time *date = &cert->valid_to;
-
- dStr_sprintfa(ds, "Certificate validity begins in the future at: "
- "%04d/%02d/%02d %02d:%02d:%02d.\n",
- date->year, date->mon, date->day, date->hour, date->min,
- date->sec);
-}
-
-/*
- * Generate dialog msg when certificate hash algorithm is not accepted.
- */
-static void Tls_cert_bad_hash(const mbedtls_x509_crt *cert, Dstr *ds)
-{
- const char *hash = (cert->sig_md == MBEDTLS_MD_MD5) ? "MD5" :
- (cert->sig_md == MBEDTLS_MD_MD4) ? "MD4" :
- (cert->sig_md == MBEDTLS_MD_MD2) ? "MD2" :
- (cert->sig_md == MBEDTLS_MD_SHA1) ? "SHA1" :
- (cert->sig_md == MBEDTLS_MD_SHA224) ? "SHA224" :
- (cert->sig_md == MBEDTLS_MD_RIPEMD160) ? "RIPEMD160" :
- (cert->sig_md == MBEDTLS_MD_SHA256) ? "SHA256" :
- (cert->sig_md == MBEDTLS_MD_SHA384) ? "SHA384" :
- (cert->sig_md == MBEDTLS_MD_SHA512) ? "SHA512" :
- "Unrecognized";
-
- dStr_sprintfa(ds, "This certificate's hash algorithm is not accepted "
- "(%s).\n", hash);
-}
-
-/*
- * Generate dialog msg when public key algorithm (RSA, ECDSA) is not accepted.
- */
-static void Tls_cert_bad_pk_alg(const mbedtls_x509_crt *cert, Dstr *ds)
-{
- const char *type_str = mbedtls_pk_get_name(&cert->pk);
-
- dStr_sprintfa(ds, "This certificate's public key algorithm is not accepted "
- "(%s).\n", type_str);
-}
-
-/*
- * Generate dialog msg when the public key is not acceptable. As of 2016,
- * this was triggered by RSA keys below 2048 bits, if I recall correctly.
- */
-static void Tls_cert_bad_key(const mbedtls_x509_crt *cert, Dstr *ds) {
- int key_bits = mbedtls_pk_get_bitlen(&cert->pk);
- const char *type_str = mbedtls_pk_get_name(&cert->pk);
-
- dStr_sprintfa(ds, "This certificate's key is not accepted, which generally "
- "means it's too weak (%d-bit %s).\n", key_bits, type_str);
-}
-
-/*
- * Make a dialog msg containing warnings about problems with the certificate.
- */
-static char *Tls_make_bad_cert_msg(const mbedtls_x509_crt *cert,uint32_t flags)
-{
- static const struct certerr {
- int val;
- void (*cert_err_fn)(const mbedtls_x509_crt *cert, Dstr *ds);
- } cert_error [] = {
- { MBEDTLS_X509_BADCERT_EXPIRED, Tls_cert_expired},
- { MBEDTLS_X509_BADCERT_CN_MISMATCH, Tls_cert_cn_mismatch},
- { MBEDTLS_X509_BADCERT_NOT_TRUSTED, Tls_cert_trust_chain_failed},
- { MBEDTLS_X509_BADCERT_FUTURE, Tls_cert_not_valid_yet},
- { MBEDTLS_X509_BADCERT_BAD_MD, Tls_cert_bad_hash},
- { MBEDTLS_X509_BADCERT_BAD_PK, Tls_cert_bad_pk_alg},
- { MBEDTLS_X509_BADCERT_BAD_KEY, Tls_cert_bad_key}
- };
- const uint_t ncert_errors = sizeof(cert_error) /sizeof(cert_error[0]);
- char *ret;
- Dstr *ds = dStr_new(NULL);
- uint_t u;
-
- for (u = 0; u < ncert_errors; u++) {
- if (flags & cert_error[u].val) {
- flags &= ~cert_error[u].val;
- cert_error[u].cert_err_fn(cert, ds);
- }
- }
- if (flags)
- dStr_sprintfa(ds, "Unknown certificate error(s): flag value 0x%04x",
- flags);
- ret = ds->str;
- dStr_free(ds, 0);
- return ret;
-}
-
-static int Tls_cert_auth_cmp(const void *v1, const void *v2)
-{
- const CertAuth_t *c1 = (CertAuth_t *)v1, *c2 = (CertAuth_t *)v2;
-
- return strcmp(c1->name, c2->name);
-}
-
-static int Tls_cert_auth_cmp_by_name(const void *v1, const void *v2)
-{
- const CertAuth_t *c = (CertAuth_t *)v1;
- const char *name = (char *)v2;
-
- return strcmp(c->name, name);
-}
-
-/*
- * Keep account of on whose authority we are trusting servers.
- */
-static void Tls_update_cert_authorities_data(const mbedtls_x509_crt *cert,
- Server_t *srv)
-{
- const uint_t buflen = 512;
- char buf[buflen];
- const mbedtls_x509_crt *last = cert;
-
- while (last->next)
- last = last->next;
-
- mbedtls_x509_dn_gets(buf, buflen, &last->issuer);
-
- CertAuth_t *ca = dList_find_custom(cert_authorities, buf,
- Tls_cert_auth_cmp_by_name);
- if (!ca) {
- ca = dNew(CertAuth_t, 1);
- ca->name = dStrdup(buf);
- ca->servers = dList_new(8);
- dList_insert_sorted(cert_authorities, ca, Tls_cert_auth_cmp);
- }
- dList_append(ca->servers, srv);
-}
-
-/*
- * Examine the certificate, and, if problems are detected, ask the user what
- * to do.
- * Return: -1 if connection should be canceled, or 0 if it should continue.
- */
-static int Tls_examine_certificate(mbedtls_ssl_context *ssl, Server_t *srv)
-{
- const mbedtls_x509_crt *cert;
- uint32_t st;
- int choice = -1, ret = -1;
- char *title = dStrconcat("Dillo TLS security warning: ",srv->hostname,NULL);
-
- cert = mbedtls_ssl_get_peer_cert(ssl);
- if (cert == NULL){
- /* Inform user that remote system cannot be trusted */
- choice = a_Dialog_choice(title,
- "No certificate received from this site. Can't verify who it is.",
- "Continue", "Cancel", NULL);
-
- /* Abort on anything but "Continue" */
- if (choice == 1){
- ret = 0;
- }
- } else {
- /* check the certificate */
- st = mbedtls_ssl_get_verify_result(ssl);
- if (st == 0) {
- if (srv->cert_status == CERT_STATUS_RECEIVING) {
- /* first connection to server */
-#if 0
- Tls_print_cert_chain(cert);
-#endif
- Tls_update_cert_authorities_data(cert, srv);
- }
- ret = 0;
- } else if (st == 0xFFFFFFFF) {
- /* "result is not available (eg because the handshake was aborted too
- * early)" is what the documentation says. Maybe it's only what
- * happens if you call get_verify_result() too early or when the
- * handshake failed. But just in case...
- */
- MSG_ERR("mbedtls_ssl_get_verify_result: result is not available");
- } else {
- char *dialog_warning_msg = Tls_make_bad_cert_msg(cert, st);
-
- choice = a_Dialog_choice(title, dialog_warning_msg, "Continue",
- "Cancel", NULL);
- if (choice == 1) {
- ret = 0;
- }
- dFree(dialog_warning_msg);
- }
- }
- dFree(title);
-
- if (choice == -1) {
- srv->cert_status = CERT_STATUS_CLEAN; /* no warning popups */
- } else if (choice == 1) {
- srv->cert_status = CERT_STATUS_USER_ACCEPTED; /* clicked Continue */
- } else {
- /* 2 for Cancel, or 0 when window closed. */
- srv->cert_status = CERT_STATUS_BAD;
- }
- return ret;
-}
-
-/*
- * If the connection was closed before we got the certificate, we need to
- * reset state so that we'll try again.
- */
-void a_Tls_reset_server_state(const DilloUrl *url)
-{
- if (servers) {
- Server_t *s = dList_find_sorted(servers, url, Tls_servers_by_url_cmp);
-
- if (s && s->cert_status == CERT_STATUS_RECEIVING)
- s->cert_status = CERT_STATUS_NONE;
- }
-}
-
-/*
- * Close an open TLS connection.
- */
-static void Tls_close_by_key(int connkey)
-{
- Conn_t *c;
-
- if ((c = a_Klist_get_data(conn_list, connkey))) {
- a_Tls_reset_server_state(c->url);
- if (c->connecting) {
- a_IOwatch_remove_fd(c->fd, -1);
- dClose(c->fd);
- }
- mbedtls_ssl_close_notify(c->ssl);
- mbedtls_ssl_free(c->ssl);
- dFree(c->ssl);
-
- a_Url_free(c->url);
- Tls_fd_map_remove_entry(c->fd);
- a_Klist_remove(conn_list, connkey);
- dFree(c);
- }
-}
-
-/*
- * Print a message about the fatal alert.
- *
- * The values have gaps, and a few are never fatal error values, and some may
- * never be sent to clients, but let's go ahead and translate every value that
- * we recognize.
- */
-static void Tls_fatal_error_msg(int error_type)
-{
- const char *errmsg;
-
- if (error_type == MBEDTLS_SSL_ALERT_MSG_CLOSE_NOTIFY)
- errmsg = "close notify";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE)
- errmsg = "unexpected message received";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_BAD_RECORD_MAC)
- errmsg = "record received with incorrect MAC";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_DECRYPTION_FAILED) {
- /* last used in TLS 1.1 */
- errmsg = "decryption failed";
- } else if (error_type == MBEDTLS_SSL_ALERT_MSG_RECORD_OVERFLOW)
- errmsg = "\"A TLSCiphertext record was received that had a length more "
- "than 2^14+2048 bytes, or a record decrypted to a TLSCompressed"
- " record with more than 2^14+1024 bytes.\"";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_DECOMPRESSION_FAILURE)
- errmsg = "\"decompression function received improper input\"";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE)
- errmsg = "\"sender was unable to negotiate an acceptable set of security"
- " parameters given the options available\"";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_NO_CERT)
- errmsg = "no cert (an obsolete alert last used in SSL3)";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_BAD_CERT)
- errmsg = "bad certificate";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT)
- errmsg = "certificate of unsupported type";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_CERT_REVOKED)
- errmsg = "certificate revoked by its signer";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_CERT_EXPIRED)
- errmsg = "certificate expired or not currently valid";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_CERT_UNKNOWN)
- errmsg = "certificate error of an unknown sort";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER)
- errmsg = "illegal parameter in handshake";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_UNKNOWN_CA)
- errmsg = "unknown CA";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_ACCESS_DENIED)
- errmsg = "access denied";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR)
- errmsg = "decode error";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR)
- errmsg = "decrypt error";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_EXPORT_RESTRICTION) {
- /* last used in TLS 1.0 */
- errmsg = "export restriction";
- } else if (error_type == MBEDTLS_SSL_ALERT_MSG_PROTOCOL_VERSION)
- errmsg = "protocol version is recognized but not supported";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_INSUFFICIENT_SECURITY)
- errmsg = "server requires ciphers more secure than those supported by "
- "the client";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR)
- errmsg = "internal error (not the client's fault)";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_INAPROPRIATE_FALLBACK)
- errmsg = "inappropriate fallback";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_USER_CANCELED)
- errmsg = "\"handshake is being canceled for some reason unrelated to a "
- "protocol failure\"";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_NO_RENEGOTIATION)
- errmsg = "no renegotiation";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT)
- errmsg = "unsupported ext";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_UNRECOGNIZED_NAME)
- errmsg = "unrecognized name";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_UNKNOWN_PSK_IDENTITY)
- errmsg = "unknown psk identity";
- else if (error_type == MBEDTLS_SSL_ALERT_MSG_NO_APPLICATION_PROTOCOL)
- errmsg = "no application protocol";
- else errmsg = "unknown alert value";
-
- MSG_WARN("mbedtls_ssl_handshake() received TLS fatal alert %d (%s)\n",
- error_type, errmsg);
-}
-
-/*
- * Connect, set a callback if it's still not completed. If completed, check
- * the certificate and report back to http.
- */
-static void Tls_handshake(int fd, int connkey)
-{
- int ret;
- bool_t ongoing = FALSE, failed = TRUE;
- Conn_t *conn;
-
- if (!(conn = a_Klist_get_data(conn_list, connkey))) {
- MSG("Tls_connect: conn for fd %d not valid\n", fd);
- return;
- }
-
- if (conn->ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) {
- ret = mbedtls_ssl_handshake(conn->ssl);
-
- if (ret == MBEDTLS_ERR_SSL_WANT_READ ||
- ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
- int want = ret == MBEDTLS_ERR_SSL_WANT_READ ? DIO_READ : DIO_WRITE;
-
- _MSG("iowatching fd %d for tls -- want %s\n", fd,
- ret == MBEDTLS_ERR_SSL_WANT_READ ? "read" : "write");
- a_IOwatch_remove_fd(fd, -1);
- a_IOwatch_add_fd(fd, want, Tls_handshake_cb, INT2VOIDP(connkey));
- ongoing = TRUE;
- failed = FALSE;
- } else if (ret == 0) {
- Server_t *srv = dList_find_sorted(servers, conn->url,
- Tls_servers_by_url_cmp);
-
- if (srv->cert_status == CERT_STATUS_RECEIVING) {
- /* Making first connection with the server. Show cipher used. */
- mbedtls_ssl_context *ssl = conn->ssl;
- const char *version = mbedtls_ssl_get_version(ssl),
- *cipher = mbedtls_ssl_get_ciphersuite(ssl);
-
- MSG("%s", URL_AUTHORITY(conn->url));
- if (URL_PORT(conn->url) != URL_HTTPS_PORT)
- MSG(":%d", URL_PORT(conn->url));
- MSG(" %s, cipher %s\n", version, cipher);
- }
- if (srv->cert_status == CERT_STATUS_USER_ACCEPTED ||
- (Tls_examine_certificate(conn->ssl, srv) != -1)) {
- failed = FALSE;
- }
- } else if (ret == MBEDTLS_ERR_NET_SEND_FAILED) {
- MSG("mbedtls_ssl_handshake() send failed. Server may not be accepting"
- " connections.\n");
- } else if (ret == MBEDTLS_ERR_NET_CONNECT_FAILED) {
- MSG("mbedtls_ssl_handshake() connect failed.\n");
- } else if (ret == MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE) {
- /* Paul Bakker, the mbed tls guy, says "beware, this might change in
- * future versions" and "ssl->in_msg[1] is not going to change anytime
- * soon, unless there are radical changes". It seems to be the best of
- * the alternatives.
- */
- Tls_fatal_error_msg(conn->ssl->in_msg[1]);
- } else if (ret == MBEDTLS_ERR_SSL_INVALID_RECORD) {
- MSG("mbedtls_ssl_handshake() failed upon receiving 'an invalid "
- "record'.\n");
- } else if (ret == MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE) {
- MSG("mbedtls_ssl_handshake() failed: 'The requested feature is not "
- "available.'\n");
- } else if (ret == MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE) {
- MSG("mbedtls_ssl_handshake() failed: 'Processing of the "
- "ServerKeyExchange handshake message failed.'\n");
- } else if (ret == MBEDTLS_ERR_SSL_CONN_EOF) {
- MSG("mbedtls_ssl_handshake() failed: Read EOF. Connection closed by "
- "server.\n");
- } else {
- MSG("mbedtls_ssl_handshake() failed with error -0x%04x\n", -ret);
- }
- }
-
- /*
- * If there were problems with the certificate, the connection may have
- * been closed by the server if the user responded too slowly to a popup.
- */
-
- if (!ongoing) {
- if (a_Klist_get_data(conn_list, connkey)) {
- conn->connecting = FALSE;
- if (failed) {
- Tls_close_by_key(connkey);
- }
- a_IOwatch_remove_fd(fd, -1);
- a_Http_connect_done(fd, failed ? FALSE : TRUE);
- } else {
- MSG("Connection disappeared. Too long with a popup popped up?\n");
- }
- }
-}
-
-static void Tls_handshake_cb(int fd, void *vconnkey)
-{
- Tls_handshake(fd, VOIDP2INT(vconnkey));
-}
-
-/*
- * Make TLS connection over a connect()ed socket.
- */
-void a_Tls_connect(int fd, const DilloUrl *url)
-{
- mbedtls_ssl_context *ssl = dNew0(mbedtls_ssl_context, 1);
- bool_t success = TRUE;
- int connkey = -1;
- int ret;
-
- if (!ssl_enabled)
- success = FALSE;
-
- if (success && Tls_user_said_no(url)) {
- success = FALSE;
- }
-
- if (success && (ret = mbedtls_ssl_setup(ssl, &ssl_conf))) {
- MSG("mbedtls_ssl_setup failed %d\n", ret);
- success = FALSE;
- }
-
- /* assign TLS connection to this file descriptor */
- if (success) {
- Conn_t *conn = Tls_conn_new(fd, url, ssl);
- connkey = Tls_make_conn_key(conn);
- mbedtls_ssl_set_bio(ssl, &conn->fd, mbedtls_net_send, mbedtls_net_recv,
- NULL);
- }
-
- if (success && (ret = mbedtls_ssl_set_hostname(ssl, URL_HOST(url)))) {
- MSG("mbedtls_ssl_set_hostname failed %d\n", ret);
- success = FALSE;
- }
-
- if (!success) {
- a_Tls_reset_server_state(url);
- a_Http_connect_done(fd, success);
- } else {
- Tls_handshake(fd, connkey);
- }
-}
-
-/*
- * Read data from an open TLS connection.
- */
-int a_Tls_read(void *conn, void *buf, size_t len)
-{
- Conn_t *c = (Conn_t*)conn;
- int ret = mbedtls_ssl_read(c->ssl, buf, len);
-
- if (ret < 0) {
- if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
- /* treat it as EOF */
- ret = 0;
- } else if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
- ret = -1;
- errno = EAGAIN; /* already happens to be set, but let's make sure */
- } else if (ret == MBEDTLS_ERR_NET_CONN_RESET) {
- MSG("READ failed: TLS connection reset by server.\n");
- } else {
- MSG("READ failed with -0x%04x: an mbed tls error.\n", -ret);
- }
- }
- return ret;
-}
-
-/*
- * Write data to an open TLS connection.
- */
-int a_Tls_write(void *conn, void *buf, size_t len)
-{
- Conn_t *c = (Conn_t*)conn;
- int ret = mbedtls_ssl_write(c->ssl, buf, len);
-
- if (ret < 0) {
- MSG("WRITE failed with -0x%04x: an mbed tls error\n", -ret);
- }
- return ret;
-}
-
-void a_Tls_close_by_fd(int fd)
-{
- FdMapEntry_t *fme = dList_find_custom(fd_map, INT2VOIDP(fd),
- Tls_fd_map_cmp);
-
- if (fme) {
- Tls_close_by_key(fme->connkey);
- }
-}
-
-static void Tls_cert_authorities_print_summary()
-{
- const int ca_len = dList_length(cert_authorities);
- Dstr *ds = dStr_new("");
- int i, j;
-
- if (ca_len)
- dStr_append(ds, "TLS: Certificate chain roots during this session:\n");
-
- for (i = 0; i < ca_len; i++) {
- CertAuth_t *ca = (CertAuth_t *)dList_nth_data(cert_authorities, i);
- const int servers_len = ca->servers ? dList_length(ca->servers) : 0;
- char *ca_name = strstr(ca->name, "CN=");
-
- if (!ca_name)
- ca_name = strstr(ca->name, "OU=");
-
- if (ca_name)
- ca_name += 3;
- else
- ca_name = ca->name;
- dStr_sprintfa(ds, "- %s for: ", ca_name);
-
- for (j = 0; j < servers_len; j++) {
- Server_t *s = dList_nth_data(ca->servers, j);
- bool_t ipv6 = a_Url_host_type(s->hostname) == URL_HOST_IPV6;
-
- dStr_sprintfa(ds, "%s%s%s", ipv6?"[":"", s->hostname, ipv6?"]":"");
- if (s->port != URL_HTTPS_PORT)
- dStr_sprintfa(ds, ":%d", s->port);
- dStr_append_c(ds, ' ');
- }
- dStr_append_c(ds, '\n');
- }
- MSG("%s", ds->str);
- dStr_free(ds, 1);
-}
-
-/*
- * Free mbed tls's chain of certificates and free our data on which root
- * certificates caused us to trust which servers.
- */
-static void Tls_cert_authorities_freeall()
-{
- if (cacerts.next)
- mbedtls_x509_crt_free(cacerts.next);
-
- if (cert_authorities) {
- CertAuth_t *ca;
- int i, n = dList_length(cert_authorities);
-
- for (i = 0; i < n; i++) {
- ca = (CertAuth_t *) dList_nth_data(cert_authorities, i);
- dFree(ca->name);
- if (ca->servers)
- dList_free(ca->servers);
- dFree(ca);
- }
- dList_free(cert_authorities);
- }
-}
-
-static void Tls_servers_freeall()
-{
- if (servers) {
- Server_t *s;
- int i, n = dList_length(servers);
-
- for (i = 0; i < n; i++) {
- s = (Server_t *) dList_nth_data(servers, i);
- dFree(s->hostname);
- dFree(s);
- }
- dList_free(servers);
- }
-}
-
-static void Tls_fd_map_remove_all()
-{
- if (fd_map) {
- FdMapEntry_t *fme;
- int i, n = dList_length(fd_map);
-
- for (i = 0; i < n; i++) {
- fme = (FdMapEntry_t *) dList_nth_data(fd_map, i);
- dFree(fme);
- }
- dList_free(fd_map);
- }
-}
-
-/*
- * Clean up
- */
-void a_Tls_freeall(void)
-{
- if (prefs.show_msg)
- Tls_cert_authorities_print_summary();
-
- Tls_fd_map_remove_all();
- Tls_cert_authorities_freeall();
- Tls_servers_freeall();
-}
-
-#endif /* ENABLE_SSL */