diff options
author | Rodrigo Arias Mallo <rodarima@gmail.com> | 2024-01-01 23:40:52 +0100 |
---|---|---|
committer | Rodrigo Arias Mallo <rodarima@gmail.com> | 2024-01-01 23:40:52 +0100 |
commit | 5ea943a5e789222472e45864e119cf786498bfcd (patch) | |
tree | ea307589de0fdb202474ad4d07c0bef7fe1c53e8 /old/test/mbedtls.diff |
Import original dillo.org website into old/
Diffstat (limited to 'old/test/mbedtls.diff')
-rw-r--r-- | old/test/mbedtls.diff | 1754 |
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(); + } + |