summaryrefslogtreecommitdiff
path: root/old/test/mbedtls.diff
diff options
context:
space:
mode:
Diffstat (limited to 'old/test/mbedtls.diff')
-rw-r--r--old/test/mbedtls.diff1754
1 files changed, 1754 insertions, 0 deletions
diff --git a/old/test/mbedtls.diff b/old/test/mbedtls.diff
new file mode 100644
index 0000000..d9a0ad4
--- /dev/null
+++ b/old/test/mbedtls.diff
@@ -0,0 +1,1754 @@
+diff -r c67e33f51c67 AUTHORS
+--- a/AUTHORS Tue Jun 28 13:53:56 2016 +0000
++++ b/AUTHORS Sat Jul 02 00:52:17 2016 +0000
+@@ -87,5 +87,3 @@
+ * src/md5.[ch] contain code by L. Peter Deutsch whose copyright is held by
+ Aladdin Enterprises.
+ * src/tipwin.cc contains code by Greg Ercolano.
+-* src/IO/tls.c contains code from wget whose copyright is held by the
+- Free Software Foundation.
+diff -r c67e33f51c67 configure.ac
+--- a/configure.ac Tue Jun 28 13:53:56 2016 +0000
++++ b/configure.ac Sat Jul 02 00:52:17 2016 +0000
+@@ -24,7 +24,7 @@
+ , enable_gprof=no)
+ AC_ARG_ENABLE(insure, [ --enable-insure Try to compile and run with Insure++],
+ , enable_insure=no)
+-AC_ARG_ENABLE(ssl, [ --enable-ssl Enable SSL/HTTPS/TLS (EXPERIMENTAL CODE)],
++AC_ARG_ENABLE(ssl, [ --enable-ssl Enable SSL/HTTPS/TLS],
+ , enable_ssl=no)
+ AC_ARG_WITH(ca-certs-file, [ --with-ca-certs-file=FILE Specify where to find a bundle of trusted CA certificates for TLS], CA_CERTS_FILE=$withval)
+ AC_ARG_WITH(ca-certs-dir, [ --with-ca-certs-dir=DIR Specify where to find a directory containing trusted CA certificates for TLS], CA_CERTS_DIR=$withval)
+@@ -294,19 +294,18 @@
+ dnl --------------------------
+ dnl
+ if test "x$enable_ssl" = "xyes"; then
+- AC_CHECK_HEADER(openssl/ssl.h, ssl_ok=yes, ssl_ok=no)
++ AC_CHECK_HEADER(mbedtls/ssl.h, ssl_ok=yes, ssl_ok=no)
+
+ if test "x$ssl_ok" = "xyes"; then
+ old_libs="$LIBS"
+- AC_CHECK_LIB(ssl, SSL_library_init, ssl_ok=yes, ssl_ok=no, -lcrypto)
++ AC_CHECK_LIB(mbedtls, mbedtls_ssl_init, ssl_ok=yes, ssl_ok=no, -lmbedx509 -lmbedcrypto)
+ LIBS="$old_libs"
+ fi
+
+ if test "x$ssl_ok" = "xyes"; then
+- LIBSSL_LIBS="-lcrypto -lssl"
+- AC_MSG_WARN([*** Enabling SSL/HTTPS/TLS support. THIS IS EXPERIMENTAL CODE ***])
++ LIBSSL_LIBS="-lmbedtls -lmbedx509 -lmbedcrypto"
+ else
+- AC_MSG_WARN([*** No libssl found. Disabling SSL/HTTPS/TLS support. ***])
++ AC_MSG_WARN([*** mbed TLS 2 not found. Disabling SSL/HTTPS/TLS support. ***])
+ fi
+ fi
+
+diff -r c67e33f51c67 src/IO/IO.c
+--- a/src/IO/IO.c Tue Jun 28 13:53:56 2016 +0000
++++ b/src/IO/IO.c Sat Jul 02 00:52:17 2016 +0000
+@@ -37,7 +37,7 @@
+ int Op; /* IORead | IOWrite */
+ int FD; /* Current File Descriptor */
+ int Flags; /* Flag array (look definitions above) */
+- int Status; /* errno code */
++ int Status; /* nonzero upon IO failure */
+ Dstr *Buf; /* Internal buffer */
+
+ void *Info; /* CCC Info structure for this IO */
+@@ -184,9 +184,14 @@
+ ret = TRUE;
+ break;
+ } else {
+- io->Status = errno;
+- MSG("READ Failed: %s\n", strerror(errno));
+- break;
++ if (conn) {
++ io->Status = St;
++ break;
++ } else {
++ io->Status = errno;
++ MSG("READ Failed with %d: %s\n", St, strerror(errno));
++ break;
++ }
+ }
+ } else { /* St == 0 */
+ break;
+@@ -234,8 +239,14 @@
+ ret = TRUE;
+ break;
+ } else {
+- io->Status = errno;
+- break;
++ if (conn) {
++ io->Status = St;
++ break;
++ } else {
++ io->Status = errno;
++ MSG("WRITE Failed with %d: %s\n", St, strerror(errno));
++ break;
++ }
+ }
+ } else if (St < io->Buf->len) {
+ /* Not all data written */
+diff -r c67e33f51c67 src/IO/tls.c
+--- a/src/IO/tls.c Tue Jun 28 13:53:56 2016 +0000
++++ b/src/IO/tls.c Sat Jul 02 00:52:17 2016 +0000
+@@ -1,24 +1,15 @@
+ /*
+ * File: tls.c
+ *
+- * Copyright 2004 Garrett Kajmowicz <gkajmowi@tbaytel.net>
+- * (for some bits derived from the https dpi, e.g., certificate handling)
+- * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
+- * 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+- * (for the certificate hostname checking from wget)
+ * 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.
+ *
+- * As a special exception, permission is granted to link Dillo with the OpenSSL
+- * or LibreSSL library, and distribute the linked executables without
+- * including the source code for OpenSSL or LibreSSL in the source
+- * distribution. You must obey the GNU General Public License, version 3, in
+- * all respects for all of the code used other than OpenSSL or LibreSSL.
+ */
+
+ /* https://www.ssllabs.com/ssltest/viewMyClient.html
+@@ -43,13 +34,8 @@
+ #else
+
+ #include <assert.h>
++#include <errno.h>
+
+-#include <sys/stat.h>
+-#include <sys/types.h>
+-
+-#include <ctype.h> /* tolower for wget stuff */
+-#include <stdio.h>
+-#include <errno.h>
+ #include "../../dlib/dlib.h"
+ #include "../dialog.hh"
+ #include "../klist.h"
+@@ -57,10 +43,13 @@
+ #include "tls.h"
+ #include "Url.h"
+
+-#include <openssl/ssl.h>
+-#include <openssl/rand.h>
+-#include <openssl/err.h>
+-#include <openssl/x509v3.h> /* for hostname checking */
++#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
+@@ -75,6 +64,11 @@
+ } Server_t;
+
+ typedef struct {
++ char *name;
++ Dlist *servers;
++} CertAuth_t;
++
++typedef struct {
+ int fd;
+ int connkey;
+ } FdMapEntry_t;
+@@ -85,18 +79,21 @@
+ typedef struct {
+ int fd;
+ DilloUrl *url;
+- SSL *ssl;
++ mbedtls_ssl_context *ssl;
+ bool_t connecting;
+ } Conn_t;
+
+ /* List of active TLS connections */
+ static Klist_t *conn_list = NULL;
+
+-/*
+- * If ssl_context is still NULL, this corresponds to TLS being disabled.
+- */
+-static SSL_CTX *ssl_context;
++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_connect_cb(int fd, void *vconnkey);
+@@ -164,43 +161,92 @@
+ /*
+ * Add a new TLS connection information node.
+ */
+-static int Tls_conn_new(int fd, const DilloUrl *url, SSL *ssl)
++static Conn_t *Tls_conn_new(int fd, const DilloUrl *url,
++ mbedtls_ssl_context *ssl)
+ {
+- int key;
+-
+ Conn_t *conn = dNew0(Conn_t, 1);
+ conn->fd = fd;
+ conn->url = a_Url_dup(url);
+ conn->ssl = ssl;
+ conn->connecting = TRUE;
++ return conn;
++}
+
+- key = a_Klist_insert(&conn_list, conn);
++static int Tls_make_conn_key(Conn_t *conn)
++{
++ int key = a_Klist_insert(&conn_list, conn);
+
+- Tls_fd_map_add_entry(fd, key);
++ Tls_fd_map_add_entry(conn->fd, key);
+
+ return key;
+ }
+
+ /*
+- * Let's monitor for TLS alerts.
++ * Load certificates from a given filename.
+ */
+-static void Tls_info_cb(const SSL *ssl, int where, int ret)
++static void Tls_load_certificates_from_file(const char *const filename)
+ {
+- if (where & SSL_CB_ALERT) {
+- const char *str = SSL_alert_desc_string_long(ret);
++ int ret = mbedtls_x509_crt_parse_file(&cacerts, filename);
+
+- if (strcmp(str, "close notify"))
+- MSG("TLS ALERT on %s: %s\n", (where & SSL_CB_READ) ? "read" : "write",
+- str);
++ 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;
++ /* mbedtls_x509_crt_free() is a little strange. */
++ duplicate->next = NULL;
++ mbedtls_x509_crt_free(duplicate);
++ dFree(duplicate);
++ } else {
++ cp = cp->next;
++ }
++ }
++ curr = curr->next;
+ }
+ }
+
+ /*
+ * Load trusted certificates.
+- * This is like using SSL_CTX_load_verify_locations() but permitting more
+- * than one bundle and more than one directory. Due to the notoriously
+- * abysmal openssl documentation, this was worked out from reading discussion
+- * on the web and then reading openssl source to see what it normally does.
+ */
+ static void Tls_load_certificates()
+ {
+@@ -212,6 +258,8 @@
+ */
+ 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",
+@@ -226,120 +274,121 @@
+ CA_CERTS_DIR
+ };
+
+- X509_STORE *store = SSL_CTX_get_cert_store(ssl_context);
+- X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+-
+- for (u = 0; u < sizeof(ca_files) / sizeof(ca_files[0]); u++) {
++ for (u = 0; u < sizeof(ca_files)/sizeof(ca_files[0]); u++) {
+ if (*ca_files[u])
+- X509_LOOKUP_load_file(lookup, ca_files[u], X509_FILETYPE_PEM);
++ Tls_load_certificates_from_file(ca_files[u]);
+ }
+
+- lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
+ for (u = 0; u < sizeof(ca_paths)/sizeof(ca_paths[0]); u++) {
+- if (*ca_paths[u])
+- X509_LOOKUP_add_dir(lookup, ca_paths[u], X509_FILETYPE_PEM);
++ if (*ca_paths[u]) {
++ Tls_load_certificates_from_path(ca_paths[u]);
++ }
+ }
+
+ userpath = dStrconcat(dGethomedir(), "/.dillo/certs/", NULL);
+- X509_LOOKUP_add_dir(lookup, userpath, X509_FILETYPE_PEM);
++ Tls_load_certificates_from_path(userpath);
+ dFree(userpath);
+
+- /* Clear out errors in the queue (file not found, etc.) */
+- while(ERR_get_error())
+- ;
++ Tls_remove_duplicate_certificates();
++ for (curr = &cacerts, u = 1; curr->next; curr = curr->next, u++) ;
++
++ MSG("Trusting %u TLS certificates.\n", u);
+ }
+
+ /*
+- * Initialize the OpenSSL library.
++ * 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)
+ {
+- SSL_library_init();
+- SSL_load_error_strings();
+- if (RAND_status() != 1) {
+- /* The standard solution is to provide it with more entropy, but this
+- * involves knowing very well that you are doing exactly the right thing.
+- */
+- MSG_ERR("Disabling HTTPS: Insufficient entropy for openssl.\n");
++ 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);
++
++ Tls_remove_psk_ciphersuites();
++
++ mbedtls_x509_crt_init(&cacerts);
++ mbedtls_ctr_drbg_init(&ctr_drbg);
++ 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;
+ }
+
+- /* Create SSL context */
+- ssl_context = SSL_CTX_new(SSLv23_client_method());
+- if (ssl_context == NULL) {
+- MSG_ERR("Disabling HTTPS: Error creating SSL context.\n");
+- return;
+- }
+-
+- SSL_CTX_set_info_callback(ssl_context, Tls_info_cb);
+-
+- /* Don't want: eNULL, which has no encryption; aNULL, which has no
+- * authentication; LOW, which as of 2014 use 64 or 56-bit encryption;
+- * EXPORT40, which uses 40-bit encryption; RC4, for which methods were
+- * found in 2013 to defeat it somewhat too easily.
+- */
+- SSL_CTX_set_cipher_list(ssl_context,
+- "ALL:!aNULL:!eNULL:!LOW:!EXPORT40:!RC4");
+-
+- /* SSL2 has been known to be insecure forever, disabling SSL3 is in response
+- * to POODLE, and disabling compression is in response to CRIME.
+- */
+- SSL_CTX_set_options(ssl_context,
+- SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION);
+-
+- /* This lets us deal with self-signed certificates */
+- SSL_CTX_set_verify(ssl_context, SSL_VERIFY_NONE, NULL);
+-
+- Tls_load_certificates();
++ 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);
+
+-/*
+- * Save certificate with a hashed filename.
+- * Return: 0 on success, 1 on failure.
+- */
+-static int Tls_save_certificate_home(X509 * cert)
+-{
+- char buf[4096];
+-
+- FILE * fp = NULL;
+- uint_t i = 0;
+- int ret = 1;
+-
+- /* Attempt to create .dillo/certs blindly - check later */
+- snprintf(buf, 4096, "%s/.dillo/", dGethomedir());
+- mkdir(buf, 01777);
+- snprintf(buf, 4096, "%s/.dillo/certs/", dGethomedir());
+- mkdir(buf, 01777);
+-
+- do {
+- snprintf(buf, 4096, "%s/.dillo/certs/%lx.%u",
+- dGethomedir(), X509_subject_name_hash(cert), i);
+-
+- fp=fopen(buf, "r");
+- if (fp == NULL){
+- /* File name doesn't exist so we can use it safely */
+- fp=fopen(buf, "w");
+- if (fp == NULL){
+- MSG("Unable to open cert save file in home dir\n");
+- break;
+- } else {
+- PEM_write_X509(fp, cert);
+- fclose(fp);
+- MSG("Wrote certificate\n");
+- ret = 0;
+- break;
+- }
+- } else {
+- fclose(fp);
+- }
+- i++;
+- /* Don't loop too many times - just give up */
+- } while (i < 1024);
+-
+- return ret;
++ Tls_load_certificates();
+ }
+
+ /*
+@@ -381,7 +430,7 @@
+ Server_t *s;
+ int ret = TLS_CONNECT_READY;
+
+- dReturn_val_if_fail(ssl_context, TLS_CONNECT_NEVER);
++ 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)
+@@ -427,379 +476,232 @@
+ return Tls_cert_status(url) == CERT_STATUS_CLEAN;
+ }
+
++#if 0
+ /*
+- * We are both checking whether the certificates are using a strong enough
+- * hash algorithm and key as well as printing out certificate information the
+- * first time that we see it. Mixing these two actions together is generally
+- * not good practice, but feels justified by the fact that it's so much
+- * trouble to get this information out of openssl even once.
+- *
+- * Return FALSE if MD5 (MD*) hash is found and user does not accept it,
+- * otherwise TRUE.
++ * Print certificate and its chain of issuer certificates.
+ */
+-static bool_t Tls_check_cert_strength(SSL *ssl, Server_t *srv, int *choice)
++static void Tls_print_cert_chain(const mbedtls_x509_crt *cert)
+ {
+ /* print for first connection to server */
+- const bool_t print_chain = srv->cert_status == CERT_STATUS_RECEIVING;
+- bool_t success = TRUE;
++ const mbedtls_x509_crt *last_cert;
++ const uint_t buflen = 2048;
++ char buf[buflen];
++ int key_bits;
++ const char *sigalg;
+
+- STACK_OF(X509) *sk = SSL_get_peer_cert_chain(ssl);
+-
+- if (sk) {
+- const uint_t buflen = 4096;
+- char buf[buflen];
+- int rc, i, n = sk_X509_num(sk);
+- X509 *cert = NULL;
+- EVP_PKEY *public_key;
+- int key_type, key_bits;
+- const char *type_str;
+- BIO *b;
+-
+- for (i = 0; i < n; i++) {
+- cert = sk_X509_value(sk, i);
+- public_key = X509_get_pubkey(cert);
+-
+- /* We are trying to find a way to get the hash function used
+- * with a certificate. This way, which is not very pleasant, puts
+- * a string such as "sha256WithRSAEncryption" in our buffer and we
+- * then trim off the "With..." part.
+- */
+- b = BIO_new(BIO_s_mem());
+- rc = i2a_ASN1_OBJECT(b, cert->sig_alg->algorithm);
+-
+- if (rc > 0) {
+- rc = BIO_gets(b, buf, buflen);
+- }
+- if (rc <= 0) {
+- strcpy(buf, "(unknown)");
+- buf[buflen-1] = '\0';
+- } else {
+- char *s = strstr(buf, "With");
+-
+- if (s) {
+- *s = '\0';
+- if (!strcmp(buf, "sha1")) {
+- if (print_chain)
+- MSG_WARN("In 2015, browsers have begun to deprecate SHA1 "
+- "certificates.\n");
+- } else if (!strncmp(buf, "md", 2) && success == TRUE) {
+- const char *msg = "A certificate in the chain uses the MD5 "
+- "signature algorithm, which is too weak "
+- "to trust.";
+- *choice = a_Dialog_choice("Dillo TLS security warning", msg,
+- "Continue", "Cancel", NULL);
+- if (*choice != 1)
+- success = FALSE;
+- }
+- }
+- }
+- BIO_free(b);
+-
+- if (print_chain)
+- MSG("%s ", buf);
+-
+- key_type = EVP_PKEY_type(public_key->type);
+- type_str = key_type == EVP_PKEY_RSA ? "RSA" :
+- key_type == EVP_PKEY_DSA ? "DSA" :
+- key_type == EVP_PKEY_DH ? "DH" :
+- key_type == EVP_PKEY_EC ? "EC" : "???";
+- key_bits = EVP_PKEY_bits(public_key);
+- X509_NAME_oneline(X509_get_subject_name(cert), buf, buflen);
+- buf[buflen-1] = '\0';
+- if (print_chain)
+- MSG("%d-bit %s: %s\n", key_bits, type_str, buf);
+- EVP_PKEY_free(public_key);
+-
+- if (key_type == EVP_PKEY_RSA && key_bits <= 1024) {
+- if (print_chain)
+- MSG_WARN("In 2014/5, browsers have been deprecating 1024-bit "
+- "RSA keys.\n");
+- }
++ while (cert) {
++ if (cert->sig_md == MBEDTLS_MD_SHA1) {
++ MSG_WARN("In 2015, browsers have begun to deprecate SHA1 "
++ "certificates.\n");
+ }
+
+- if (cert) {
+- X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen);
+- buf[buflen-1] = '\0';
+- if (print_chain)
+- MSG("root: %s\n", buf);
+- }
++ 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;
+ }
+- return success;
++ if (last_cert) {
++ mbedtls_x509_dn_gets(buf, buflen, &last_cert->issuer);
++ MSG("root: %s\n", buf);
++ }
+ }
++#endif
+
+-/******************** BEGINNING OF STUFF DERIVED FROM wget-1.16.3 */
++/*
++ * 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;
+
+-#define ASTERISK_EXCLUDES_DOT /* mandated by rfc2818 */
+-
+-/* Return true is STRING (case-insensitively) matches PATTERN, false
+- otherwise. The recognized wildcard character is "*", which matches
+- any character in STRING except ".". Any number of the "*" wildcard
+- may be present in the pattern.
+-
+- This is used to match of hosts as indicated in rfc2818: "Names may
+- contain the wildcard character * which is considered to match any
+- single domain name component or component fragment. E.g., *.a.com
+- matches foo.a.com but not bar.foo.a.com. f*.com matches foo.com but
+- not bar.com [or foo.bar.com]."
+-
+- If the pattern contain no wildcards, pattern_match(a, b) is
+- equivalent to !strcasecmp(a, b). */
+-
+-static bool_t pattern_match (const char *pattern, const char *string)
+-{
+-
+- const char *p = pattern, *n = string;
+- char c;
+- for (; (c = tolower (*p++)) != '\0'; n++)
+- if (c == '*')
+- {
+- for (c = tolower (*p); c == '*'; c = tolower (*++p))
+- ;
+- for (; *n != '\0'; n++)
+- if (tolower (*n) == c && pattern_match (p, n))
+- return TRUE;
+-#ifdef ASTERISK_EXCLUDES_DOT
+- else if (*n == '.')
+- return FALSE;
+-#endif
+- return c == '\0';
+- }
+- else
+- {
+- if (c != tolower (*n))
+- return FALSE;
+- }
+- return *n == '\0';
++ 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);
+ }
+
+ /*
+- * Check that the certificate corresponds to the site it's presented for.
+- *
+- * Return TRUE if the hostname matched or the user indicated acceptance.
+- * FALSE on failure.
++ * Generate dialog msg when certificate is not for this host.
+ */
+-static bool_t Tls_check_cert_hostname(X509 *cert, const char *host,
+- int *choice)
++static void Tls_cert_cn_mismatch(const mbedtls_x509_crt *cert, Dstr *ds)
+ {
+- dReturn_val_if_fail(cert && host, FALSE);
++ const uint_t buflen = 2048;
++ char cert_info_buf[buflen];
++ char *san, *s;
+
+- char *msg;
+- GENERAL_NAMES *subjectAltNames;
+- bool_t success = TRUE, alt_name_checked = FALSE;;
+- char common_name[256];
++ dStr_append(ds, "This host is not one of the hostnames listed on the TLS "
++ "certificate");
++ /*
++ *
++ * 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.
++ *
++ *
++ */
++ mbedtls_x509_crt_info(cert_info_buf, buflen, "", cert);
+
+- /* Check that HOST matches the common name in the certificate.
+- #### The following remains to be done:
+-
+- - When matching against common names, it should loop over all
+- common names and choose the most specific one, i.e. the last
+- one, not the first one, which the current code picks.
+-
+- - Ensure that ASN1 strings from the certificate are encoded as
+- UTF-8 which can be meaningfully compared to HOST. */
+-
+- subjectAltNames = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL);
+-
+- if (subjectAltNames)
+- {
+- /* Test subject alternative names */
+-
+- Dstr *err = dStr_new("");
+- dStr_sprintf(err, "Hostname %s does not match any of certificate's "
+- "Subject Alternative Names: ", host);
+-
+- /* Do we want to check for dNSNAmes or ipAddresses (see RFC 2818)?
+- * Signal it by host_in_octet_string. */
+- ASN1_OCTET_STRING *host_in_octet_string = a2i_IPADDRESS (host);
+-
+- int numaltnames = sk_GENERAL_NAME_num (subjectAltNames);
+- int i;
+- for (i=0; i < numaltnames; i++)
+- {
+- const GENERAL_NAME *name =
+- sk_GENERAL_NAME_value (subjectAltNames, i);
+- if (name)
+- {
+- if (host_in_octet_string)
+- {
+- if (name->type == GEN_IPADD)
+- {
+- /* Check for ipAddress */
+- /* TODO: Should we convert between IPv4-mapped IPv6
+- * addresses and IPv4 addresses? */
+- alt_name_checked = TRUE;
+- if (!ASN1_STRING_cmp (host_in_octet_string,
+- name->d.iPAddress))
+- break;
+- dStr_sprintfa(err, "%s ", name->d.iPAddress);
+- }
+- }
+- else if (name->type == GEN_DNS)
+- {
+- /* dNSName should be IA5String (i.e. ASCII), however who
+- * does trust CA? Convert it into UTF-8 for sure. */
+- unsigned char *name_in_utf8 = NULL;
+-
+- /* Check for dNSName */
+- alt_name_checked = TRUE;
+-
+- if (0 <= ASN1_STRING_to_UTF8 (&name_in_utf8, name->d.dNSName))
+- {
+- /* Compare and check for NULL attack in ASN1_STRING */
+- if (pattern_match ((char *)name_in_utf8, host) &&
+- (strlen ((char *)name_in_utf8) ==
+- (size_t)ASN1_STRING_length (name->d.dNSName)))
+- {
+- OPENSSL_free (name_in_utf8);
+- break;
+- }
+- dStr_sprintfa(err, "%s ", name_in_utf8);
+- OPENSSL_free (name_in_utf8);
+- }
+- }
+- }
+- }
+- sk_GENERAL_NAME_pop_free(subjectAltNames, GENERAL_NAME_free);
+- if (host_in_octet_string)
+- ASN1_OCTET_STRING_free(host_in_octet_string);
+-
+- if (alt_name_checked == TRUE && i >= numaltnames)
+- {
+- success = FALSE;
+- *choice = a_Dialog_choice("Dillo TLS security warning",
+- err->str, "Continue", "Cancel", NULL);
+-
+- switch (*choice){
+- case 1:
+- success = TRUE;
+- break;
+- case 2:
+- break;
+- default:
+- break;
+- }
+- }
+- dStr_free(err, 1);
+- }
+-
+- if (alt_name_checked == FALSE)
+- {
+- /* Test commomName */
+- X509_NAME *xname = X509_get_subject_name(cert);
+- common_name[0] = '\0';
+- X509_NAME_get_text_by_NID (xname, NID_commonName, common_name,
+- sizeof (common_name));
+-
+- if (!pattern_match (common_name, host))
+- {
+- success = FALSE;
+- msg = dStrconcat("Certificate common name ", common_name,
+- " doesn't match requested host name ", host, NULL);
+- *choice = a_Dialog_choice("Dillo TLS security warning",
+- msg, "Continue", "Cancel", NULL);
+- dFree(msg);
+-
+- switch (*choice){
+- case 1:
+- success = TRUE;
+- break;
+- case 2:
+- break;
+- default:
+- break;
+- }
+- }
+- else
+- {
+- /* We now determine the length of the ASN1 string. If it
+- * differs from common_name's length, then there is a \0
+- * before the string terminates. This can be an instance of a
+- * null-prefix attack.
+- *
+- * https://www.blackhat.com/html/bh-usa-09/bh-usa-09-archives.html#Marlinspike
+- * */
+-
+- int i = -1, j;
+- X509_NAME_ENTRY *xentry;
+- ASN1_STRING *sdata;
+-
+- if (xname) {
+- for (;;)
+- {
+- j = X509_NAME_get_index_by_NID (xname, NID_commonName, i);
+- if (j == -1) break;
+- i = j;
+- }
+- }
+-
+- xentry = X509_NAME_get_entry(xname,i);
+- sdata = X509_NAME_ENTRY_get_data(xentry);
+- if (strlen (common_name) != (size_t)ASN1_STRING_length (sdata))
+- {
+- success = FALSE;
+- msg = dStrconcat("Certificate common name is invalid (contains a NUL "
+- "character). This may be an indication that the "
+- "host is not who it claims to be -- that is, not "
+- "the real ", host, NULL);
+- *choice = a_Dialog_choice("Dillo TLS security warning",
+- msg, "Continue", "Cancel", NULL);
+- dFree(msg);
+-
+- switch (*choice){
+- case 1:
+- success = TRUE;
+- break;
+- case 2:
+- break;
+- default:
+- break;
+- }
+- }
+- }
+- }
+- return success;
++ 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");
+ }
+
+-/******************** END OF STUFF DERIVED FROM wget-1.16.3 */
++/*
++ * 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);
++}
+
+ /*
+- * Get the certificate at the end of the chain, or NULL on failure.
+- *
+- * Rumor has it that the stack can be NULL if a connection has been reused
+- * and that the stack can then be reconstructed if necessary, but it doesn't
+- * sound like a case we'll encounter.
++ * Generate dialog msg when certificate start date is in the future.
+ */
+-static X509 *Tls_get_end_of_chain(SSL *ssl)
++static void Tls_cert_not_valid_yet(const mbedtls_x509_crt *cert, Dstr *ds)
+ {
+- STACK_OF(X509) *sk = SSL_get_peer_cert_chain(ssl);
++ const mbedtls_x509_time *date = &cert->valid_to;
+
+- return sk ? sk_X509_value(sk, sk_X509_num(sk) - 1) : NULL;
++ 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);
+ }
+
+-static void Tls_get_issuer_name(X509 *cert, char *buf, uint_t buflen)
++/*
++ * Generate dialog msg when certificate hash algorithm is not accepted.
++ */
++static void Tls_cert_bad_hash(const mbedtls_x509_crt *cert, Dstr *ds)
+ {
+- if (cert) {
+- X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen);
+- } else {
+- strcpy(buf, "(unknown)");
+- buf[buflen-1] = '\0';
+- }
++ const char *hash = (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);
+ }
+
+-static void Tls_get_expiration_str(X509 *cert, char *buf, uint_t buflen)
++/*
++ * 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)
+ {
+- ASN1_TIME *exp_date = X509_get_notAfter(cert);
+- BIO *b = BIO_new(BIO_s_mem());
+- int rc = ASN1_TIME_print(b, exp_date);
++ const char *type_str = mbedtls_pk_get_name(&cert->pk);
+
+- if (rc > 0) {
+- rc = BIO_gets(b, buf, buflen);
++ 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 (rc <= 0) {
+- strcpy(buf, "(unknown)");
+- buf[buflen-1] = '\0';
++ 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 = 1024;
++ 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(16);
++ dList_insert_sorted(cert_authorities, ca, Tls_cert_auth_cmp);
+ }
+- BIO_free(b);
++ dList_append(ca->servers, srv);
+ }
+
+ /*
+@@ -807,196 +709,53 @@
+ * to do.
+ * Return: -1 if connection should be canceled, or 0 if it should continue.
+ */
+-static int Tls_examine_certificate(SSL *ssl, Server_t *srv)
++static int Tls_examine_certificate(mbedtls_ssl_context *ssl, Server_t *srv)
+ {
+- X509 *remote_cert;
+- long st;
+- const uint_t buflen = 4096;
+- char buf[buflen], *cn, *msg;
++ const mbedtls_x509_crt *cert;
++ uint32_t st;
+ int choice = -1, ret = -1;
+ char *title = dStrconcat("Dillo TLS security warning: ",srv->hostname,NULL);
+
+- remote_cert = SSL_get_peer_certificate(ssl);
+- if (remote_cert == NULL){
++ cert = mbedtls_ssl_get_peer_cert(ssl);
++ if (cert == NULL){
+ /* Inform user that remote system cannot be trusted */
+ choice = a_Dialog_choice(title,
+- "The remote system is not presenting a certificate. "
+- "This site cannot be trusted. Sending data is not safe.",
++ "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 if (Tls_check_cert_strength(ssl, srv, &choice) &&
+- Tls_check_cert_hostname(remote_cert, srv->hostname, &choice)) {
+- /* Figure out if (and why) the remote system can't be trusted */
+- st = SSL_get_verify_result(ssl);
+- switch (st) {
+- case X509_V_OK:
++ } 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;
+- break;
+- case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+- /* Either self signed and untrusted */
+- /* Extract CN from certificate name information */
+- if ((cn = strstr(remote_cert->name, "/CN=")) == NULL) {
+- strcpy(buf, "(no CN given)");
+- } else {
+- char *cn_end;
++ } 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);
+
+- cn += 4;
+-
+- if ((cn_end = strstr(cn, "/")) == NULL )
+- cn_end = cn + strlen(cn);
+-
+- strncpy(buf, cn, (size_t) (cn_end - cn));
+- buf[cn_end - cn] = '\0';
+- }
+- msg = dStrconcat("The remote certificate is self-signed and "
+- "untrusted. For address: ", buf, NULL);
+- choice = a_Dialog_choice(title,
+- msg, "Continue", "Cancel", "Save Certificate", NULL);
+- dFree(msg);
+-
+- switch (choice){
+- case 1:
+- ret = 0;
+- break;
+- case 2:
+- break;
+- case 3:
+- /* Save certificate to a file */
+- Tls_save_certificate_home(remote_cert);
+- ret = 0;
+- break;
+- default:
+- break;
+- }
+- break;
+- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+- case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+- choice = a_Dialog_choice(title,
+- "The issuer for the remote certificate cannot be found. "
+- "The authenticity of the remote certificate cannot be trusted.",
+- "Continue", "Cancel", NULL);
+-
++ choice = a_Dialog_choice(title, dialog_warning_msg, "Continue",
++ "Cancel", NULL);
+ if (choice == 1) {
+ ret = 0;
+ }
+- break;
+-
+- case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+- case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+- case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+- case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+- choice = a_Dialog_choice(title,
+- "The remote certificate signature could not be read "
+- "or is invalid and should not be trusted",
+- "Continue", "Cancel", NULL);
+-
+- if (choice == 1) {
+- ret = 0;
+- }
+- break;
+- case X509_V_ERR_CERT_NOT_YET_VALID:
+- case X509_V_ERR_CRL_NOT_YET_VALID:
+- choice = a_Dialog_choice(title,
+- "Part of the remote certificate is not yet valid. "
+- "Certificates usually have a range of dates over which "
+- "they are to be considered valid, and the certificate "
+- "presented has a starting validity after today's date "
+- "You should be cautious about using this site",
+- "Continue", "Cancel", NULL);
+-
+- if (choice == 1) {
+- ret = 0;
+- }
+- break;
+- case X509_V_ERR_CERT_HAS_EXPIRED:
+- case X509_V_ERR_CRL_HAS_EXPIRED:
+- Tls_get_expiration_str(remote_cert, buf, buflen);
+- msg = dStrconcat("The remote certificate expired on: ", buf,
+- ". This site can no longer be trusted.", NULL);
+-
+- choice = a_Dialog_choice(title, msg, "Continue", "Cancel", NULL);
+- if (choice == 1) {
+- ret = 0;
+- }
+- dFree(msg);
+- break;
+- case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+- case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+- case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+- case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+- choice = a_Dialog_choice(title,
+- "There was an error in the certificate presented. "
+- "Some of the certificate data was improperly formatted "
+- "making it impossible to determine if the certificate "
+- "is valid. You should not trust this certificate.",
+- "Continue", "Cancel", NULL);
+- if (choice == 1) {
+- ret = 0;
+- }
+- break;
+- case X509_V_ERR_INVALID_CA:
+- case X509_V_ERR_INVALID_PURPOSE:
+- case X509_V_ERR_CERT_UNTRUSTED:
+- case X509_V_ERR_CERT_REJECTED:
+- case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+- choice = a_Dialog_choice(title,
+- "One of the certificates in the chain is being used "
+- "incorrectly (possibly due to configuration problems "
+- "with the remote system. The connection should not "
+- "be trusted",
+- "Continue", "Cancel", NULL);
+- if (choice == 1) {
+- ret = 0;
+- }
+- break;
+- case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
+- case X509_V_ERR_AKID_SKID_MISMATCH:
+- case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
+- choice = a_Dialog_choice(title,
+- "Some of the information presented by the remote system "
+- "does not match other information presented. "
+- "This may be an attempt to eavesdrop on communications",
+- "Continue", "Cancel", NULL);
+- if (choice == 1) {
+- ret = 0;
+- }
+- break;
+- case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+- Tls_get_issuer_name(Tls_get_end_of_chain(ssl), buf, buflen);
+- msg = dStrconcat("Certificate chain led to a self-signed certificate "
+- "instead of a trusted root. Name: ", buf , NULL);
+- choice = a_Dialog_choice(title, msg, "Continue", "Cancel", NULL);
+- if (choice == 1) {
+- ret = 0;
+- }
+- dFree(msg);
+- break;
+- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+- Tls_get_issuer_name(Tls_get_end_of_chain(ssl), buf, buflen);
+- msg = dStrconcat("The issuer certificate of an untrusted certificate "
+- "cannot be found. Issuer: ", buf, NULL);
+- choice = a_Dialog_choice(title, msg, "Continue", "Cancel", NULL);
+- if (choice == 1) {
+- ret = 0;
+- }
+- dFree(msg);
+- break;
+- default: /* Need to add more options later */
+- snprintf(buf, 80,
+- "The remote certificate cannot be verified (code %ld)", st);
+- choice = a_Dialog_choice(title,
+- buf, "Continue", "Cancel", NULL);
+- /* abort on anything but "Continue" */
+- if (choice == 1){
+- ret = 0;
+- }
++ dFree(dialog_warning_msg);
+ }
+- X509_free(remote_cert);
+- remote_cert = 0;
+ }
+ dFree(title);
+
+@@ -1005,10 +764,7 @@
+ } else if (choice == 1) {
+ srv->cert_status = CERT_STATUS_USER_ACCEPTED; /* clicked Continue */
+ } else {
+- /* 2 for Cancel, or 0 when window closed. Treating 0 as meaning 'No' is
+- * probably not exactly correct, but adding complexity to handle this
+- * obscure case does not seem justifiable.
+- */
++ /* 2 for Cancel, or 0 when window closed. */
+ srv->cert_status = CERT_STATUS_BAD;
+ }
+ return ret;
+@@ -1041,13 +797,9 @@
+ a_IOwatch_remove_fd(c->fd, -1);
+ dClose(c->fd);
+ }
+- if (!SSL_in_init(c->ssl)) {
+- /* openssl 1.0.2f does not like shutdown being called during
+- * handshake, resulting in ssl_undefined_function in the error queue.
+- */
+- SSL_shutdown(c->ssl);
+- }
+- SSL_free(c->ssl);
++ 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);
+@@ -1057,6 +809,88 @@
+ }
+
+ /*
++ * 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("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.
+ */
+@@ -1071,71 +905,63 @@
+ return;
+ }
+
+- assert(!ERR_get_error());
++ if (conn->ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) {
++ ret = mbedtls_ssl_handshake(conn->ssl);
+
+- ret = SSL_connect(conn->ssl);
+-
+- if (ret <= 0) {
+- int err1_ret = SSL_get_error(conn->ssl, ret);
+- if (err1_ret == SSL_ERROR_WANT_READ ||
+- err1_ret == SSL_ERROR_WANT_WRITE) {
+- int want = err1_ret == SSL_ERROR_WANT_READ ? DIO_READ : DIO_WRITE;
++ 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,
+- err1_ret == SSL_ERROR_WANT_READ ? "read" : "write");
++ ret == MBEDTLS_ERR_SSL_WANT_READ ? "read" : "write");
+ a_IOwatch_remove_fd(fd, -1);
+ a_IOwatch_add_fd(fd, want, Tls_connect_cb, INT2VOIDP(connkey));
+ ongoing = TRUE;
+ failed = FALSE;
+- } else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
+- unsigned long err2_ret = ERR_get_error();
++ } else if (ret == 0) {
++ Server_t *srv = dList_find_sorted(servers, conn->url,
++ Tls_servers_by_url_cmp);
++#if 0
++ 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);
+
+- if (err2_ret) {
+- do {
+- MSG("SSL_connect() failed: %s\n",
+- ERR_error_string(err2_ret, NULL));
+- } while ((err2_ret = ERR_get_error()));
+- } else {
+- /* nothing in the error queue */
+- if (ret == 0) {
+- MSG("TLS connect error: \"an EOF was observed that violates "
+- "the protocol\"\n");
+- /*
+- * I presume we took too long on our side and the server grew
+- * impatient.
+- */
+- } else if (ret == -1) {
+- MSG("TLS connect error: %s\n", dStrerror(errno));
+-
+- /* If the following can happen, I'll add code to handle it, but
+- * I don't want to add code blindly if it isn't getting used
+- */
+- assert(errno != EAGAIN && errno != EINTR);
+- } else {
+- MSG_ERR("According to the man page for SSL_get_error(), this "
+- "was not a possibility (ret %d).\n", ret);
+- }
++ MSG("%s: %s, cipher %s\n", URL_AUTHORITY(conn->url), version,
++ cipher);
+ }
++#endif
++ 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("SSL_get_error() returned %d on a connect.\n", err1_ret);
+- }
+- } else {
+- 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. */
+- SSL *ssl = conn->ssl;
+- const char *version = SSL_get_version(ssl);
+- const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
+-
+- MSG("%s: %s, cipher %s\n", URL_AUTHORITY(conn->url), version,
+- SSL_CIPHER_get_name(cipher));
+- }
+-
+- if (srv->cert_status == CERT_STATUS_USER_ACCEPTED ||
+- (Tls_examine_certificate(conn->ssl, srv) != -1)) {
+- failed = FALSE;
++ MSG("mbedtls_ssl_handshake() failed with error -0x%04x\n", -ret);
+ }
+ }
+
+@@ -1150,7 +976,7 @@
+ if (failed) {
+ Tls_close_by_key(connkey);
+ }
+- a_IOwatch_remove_fd(fd, DIO_READ|DIO_WRITE);
++ 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");
+@@ -1168,47 +994,36 @@
+ */
+ void a_Tls_handshake(int fd, const DilloUrl *url)
+ {
+- SSL *ssl;
++ mbedtls_ssl_context *ssl = dNew0(mbedtls_ssl_context, 1);
+ bool_t success = TRUE;
+ int connkey = -1;
++ int ret;
+
+- if (!ssl_context)
++ if (!ssl_enabled)
+ success = FALSE;
+
+ if (success && Tls_user_said_no(url)) {
+ success = FALSE;
+ }
+
+- assert(!ERR_get_error());
+-
+- if (success && !(ssl = SSL_new(ssl_context))) {
+- unsigned long err_ret = ERR_get_error();
+- do {
+- MSG("SSL_new() failed: %s\n", ERR_error_string(err_ret, NULL));
+- } while ((err_ret = ERR_get_error()));
++ 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 && !SSL_set_fd(ssl, fd)) {
+- unsigned long err_ret = ERR_get_error();
+- do {
+- MSG("SSL_set_fd() failed: %s\n", ERR_error_string(err_ret, NULL));
+- } while ((err_ret = ERR_get_error()));
++ 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)
+- connkey = Tls_conn_new(fd, url, ssl);
+-
+-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+- /* Server Name Indication. From the openssl changelog, it looks like this
+- * came along in 2010.
+- */
+- if (success && !a_Url_host_is_ip(URL_HOST(url)))
+- SSL_set_tlsext_host_name(ssl, URL_HOST(url));
+-#endif
+-
+ if (!success) {
+ a_Tls_reset_server_state(url);
+ a_Http_connect_done(fd, success);
+@@ -1223,7 +1038,22 @@
+ int a_Tls_read(void *conn, void *buf, size_t len)
+ {
+ Conn_t *c = (Conn_t*)conn;
+- return SSL_read(c->ssl, buf, len);
++ 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;
+ }
+
+ /*
+@@ -1232,7 +1062,12 @@
+ int a_Tls_write(void *conn, void *buf, size_t len)
+ {
+ Conn_t *c = (Conn_t*)conn;
+- return SSL_write(c->ssl, buf, len);
++ 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)
+@@ -1245,6 +1080,57 @@
+ }
+ }
+
++static void Tls_cert_authorities_print_summary()
++{
++ const int ca_len = dList_length(cert_authorities);
++ int i, j;
++
++ 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;
++
++ for (j = 0; j < servers_len; j++) {
++ Server_t *s = dList_nth_data(ca->servers, j);
++
++ MSG("%s - %s:%d\n", ca_name, s->hostname, s->port);
++ }
++ }
++
++}
++
++/*
++ * Free mbed tls's chain of certificates and free our data tracking which
++ * CAs 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) {
+@@ -1275,13 +1161,14 @@
+ }
+
+ /*
+- * Clean up the OpenSSL library
++ * Clean up
+ */
+ void a_Tls_freeall(void)
+ {
+- if (ssl_context)
+- SSL_CTX_free(ssl_context);
++ Tls_cert_authorities_print_summary();
++
+ Tls_fd_map_remove_all();
++ Tls_cert_authorities_freeall();
+ Tls_servers_freeall();
+ }
+