diff options
Diffstat (limited to 'src/IO/tls.c')
-rw-r--r-- | src/IO/tls.c | 1217 |
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 */ |