45#include "../../dlib/dlib.h"
46#include "../dialog.hh"
52#include <openssl/ssl.h>
53#include <openssl/rand.h>
54#include <openssl/err.h>
55#include <openssl/x509v3.h>
56#include <openssl/crypto.h>
58#define CERT_STATUS_NONE 0
59#define CERT_STATUS_RECEIVING 1
60#define CERT_STATUS_CLEAN 2
61#define CERT_STATUS_BAD 3
62#define CERT_STATUS_USER_ACCEPTED 4
105 const FdMapEntry_t *e = v1;
107 return (fd != e->fd);
112 FdMapEntry_t *e =
dNew0(FdMapEntry_t, 1);
114 e->connkey = connkey;
117 MSG_ERR(
"TLS FD ENTRY ALREADY FOUND FOR %d\n", e->fd);
137 MSG(
"TLS FD ENTRY NOT FOUND FOR %d\n", fd);
166 Conn_t *conn =
dNew0(Conn_t, 1);
170 conn->connecting =
TRUE;
171 conn->in_connect =
FALSE;
172 conn->do_shutdown =
TRUE;
186 if (where & SSL_CB_ALERT) {
187 const char *str = SSL_alert_desc_string_long(ret);
189 if (strcmp(str,
"close notify"))
190 MSG(
"TLS ALERT on %s: %s\n", (where & SSL_CB_READ) ?
"read" :
"write",
212 static const char *
const ca_files[] = {
213 "/etc/ssl/certs/ca-certificates.crt",
214 "/etc/pki/tls/certs/ca-bundle.crt",
215 "/usr/share/ssl/certs/ca-bundle.crt",
216 "/usr/local/share/certs/ca-root.crt",
221 static const char *
const ca_paths[] = {
226 X509_STORE *store = SSL_CTX_get_cert_store(
ssl_context);
227 X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
229 for (u = 0; u <
sizeof(ca_files) /
sizeof(ca_files[0]); u++) {
231 X509_LOOKUP_load_file(lookup, ca_files[u], X509_FILETYPE_PEM);
234 lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
235 for (u = 0; u <
sizeof(ca_paths)/
sizeof(ca_paths[0]); u++) {
237 X509_LOOKUP_add_dir(lookup, ca_paths[u], X509_FILETYPE_PEM);
241 X509_LOOKUP_add_dir(lookup, userpath, X509_FILETYPE_PEM);
245 while(ERR_get_error())
254 MSG(
"TLS library: %s\n", OpenSSL_version(OPENSSL_VERSION));
256 SSL_load_error_strings();
257 if (RAND_status() != 1) {
261 MSG_ERR(
"Disabling HTTPS: Insufficient entropy for openssl.\n");
268 MSG_ERR(
"Disabling HTTPS: Error creating SSL context.\n");
280 "ALL:!aNULL:!eNULL:!LOW:!EXPORT40:!RC4");
286 SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION);
289 SSL_CTX_set_verify(
ssl_context, SSL_VERIFY_NONE, NULL);
312 snprintf(buf, 4096,
"%s/.dillo/certs/",
dGethomedir());
316 snprintf(buf, 4096,
"%s/.dillo/certs/%lx.%u",
324 MSG(
"Unable to open cert save file in home dir\n");
327 PEM_write_X509(fp, cert);
329 MSG(
"Wrote certificate\n");
348 const Server_t *s1 = (
const Server_t *)v1, *s2 = (
const Server_t *)v2;
352 cmp = s1->port - s2->port;
360 const Server_t *s = (
const Server_t *)v1;
394 s =
dNew(Server_t, 1);
445 STACK_OF(X509) *sk = SSL_get_peer_cert_chain(ssl);
448 const uint_t buflen = 4096;
450 int rc, i, n = sk_X509_num(sk);
452 EVP_PKEY *public_key;
453 int key_type, key_bits;
454 const char *type_str;
457 for (i = 0; i < n; i++) {
458 cert = sk_X509_value(sk, i);
459 public_key = X509_get_pubkey(cert);
466 b = BIO_new(BIO_s_mem());
467 const ASN1_OBJECT *algorithm;
468 X509_ALGOR_get0(&algorithm, NULL, NULL, X509_get0_tbs_sigalg(cert));
469 rc = i2a_ASN1_OBJECT(b, algorithm);
472 rc = BIO_gets(b, buf, buflen);
475 strcpy(buf,
"(unknown)");
476 buf[buflen-1] =
'\0';
478 char *s = strstr(buf,
"With");
482 if (!strcmp(buf,
"sha1")) {
484 MSG_WARN(
"In 2015, browsers have begun to deprecate SHA1 "
486 }
else if (!strncmp(buf,
"md", 2) && success ==
TRUE) {
487 const char *msg =
"A certificate in the chain uses the MD5 "
488 "signature algorithm, which is too weak "
491 "Continue",
"Cancel", NULL);
502#if OPENSSL_VERSION_NUMBER < 0x30000000L
503 key_type = EVP_PKEY_type(EVP_PKEY_id(public_key));
505 key_type = EVP_PKEY_type(EVP_PKEY_get_id(public_key));
507 type_str = key_type == EVP_PKEY_RSA ?
"RSA" :
508 key_type == EVP_PKEY_DSA ?
"DSA" :
509 key_type == EVP_PKEY_DH ?
"DH" :
510 key_type == EVP_PKEY_EC ?
"EC" :
"???";
511 key_bits = EVP_PKEY_bits(public_key);
512 X509_NAME_oneline(X509_get_subject_name(cert), buf, buflen);
513 buf[buflen-1] =
'\0';
515 MSG(
"%d-bit %s: %s\n", key_bits, type_str, buf);
516 EVP_PKEY_free(public_key);
518 if (key_type == EVP_PKEY_RSA && key_bits <= 1024) {
520 MSG_WARN(
"In 2014/5, browsers have been deprecating 1024-bit "
526 X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen);
527 buf[buflen-1] =
'\0';
529 MSG(
"root: %s\n", buf);
537#define ASTERISK_EXCLUDES_DOT
556 const char *p = pattern, *n = string;
558 for (; (c = tolower (*p++)) !=
'\0'; n++)
561 for (c = tolower (*p); c ==
'*'; c = tolower (*++p))
563 for (; *n !=
'\0'; n++)
566#ifdef ASTERISK_EXCLUDES_DOT
574 if (c != tolower (*n))
589 if (cert == NULL || host == NULL)
593 GENERAL_NAMES *subjectAltNames;
595 char common_name[256];
607 subjectAltNames = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL);
614 dStr_sprintf(err,
"Hostname %s does not match any of certificate's "
615 "Subject Alternative Names: ", host);
619 ASN1_OCTET_STRING *host_in_octet_string = a2i_IPADDRESS (host);
621 int numaltnames = sk_GENERAL_NAME_num (subjectAltNames);
623 for (i=0; i < numaltnames; i++)
625 const GENERAL_NAME *name =
626 sk_GENERAL_NAME_value (subjectAltNames, i);
629 if (host_in_octet_string)
631 if (name->type == GEN_IPADD)
636 alt_name_checked =
TRUE;
637 if (!ASN1_STRING_cmp (host_in_octet_string,
643 else if (name->type == GEN_DNS)
647 unsigned char *name_in_utf8 = NULL;
650 alt_name_checked =
TRUE;
652 if (0 <= ASN1_STRING_to_UTF8 (&name_in_utf8, name->d.dNSName))
656 (strlen ((
char *)name_in_utf8) ==
657 (
size_t)ASN1_STRING_length (name->d.dNSName)))
659 OPENSSL_free (name_in_utf8);
663 OPENSSL_free (name_in_utf8);
668 sk_GENERAL_NAME_pop_free(subjectAltNames, GENERAL_NAME_free);
669 if (host_in_octet_string)
670 ASN1_OCTET_STRING_free(host_in_octet_string);
672 if (alt_name_checked ==
TRUE && i >= numaltnames)
676 err->
str,
"Continue",
"Cancel", NULL);
691 if (alt_name_checked ==
FALSE)
694 X509_NAME *xname = X509_get_subject_name(cert);
695 common_name[0] =
'\0';
696 X509_NAME_get_text_by_NID (xname, NID_commonName, common_name,
697 sizeof (common_name));
702 msg =
dStrconcat(
"Certificate common name ", common_name,
703 " doesn't match requested host name ", host, NULL);
705 msg,
"Continue",
"Cancel", NULL);
729 X509_NAME_ENTRY *xentry;
735 j = X509_NAME_get_index_by_NID (xname, NID_commonName, i);
741 xentry = X509_NAME_get_entry(xname,i);
742 sdata = X509_NAME_ENTRY_get_data(xentry);
743 if (strlen (common_name) != (
size_t)ASN1_STRING_length (sdata))
746 msg =
dStrconcat(
"Certificate common name is invalid (contains a NUL "
747 "character). This may be an indication that the "
748 "host is not who it claims to be -- that is, not "
749 "the real ", host, NULL);
751 msg,
"Continue",
"Cancel", NULL);
780 STACK_OF(X509) *sk = SSL_get_peer_cert_chain(ssl);
782 return sk ? sk_X509_value(sk, sk_X509_num(sk) - 1) : NULL;
788 X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen);
790 strcpy(buf,
"(unknown)");
791 buf[buflen-1] =
'\0';
797 ASN1_TIME *exp_date = X509_get_notAfter(cert);
798 BIO *b = BIO_new(BIO_s_mem());
799 int rc = ASN1_TIME_print(b, exp_date);
802 rc = BIO_gets(b, buf, buflen);
805 strcpy(buf,
"(unknown)");
806 buf[buflen-1] =
'\0';
820 const uint_t buflen = 4096;
821 char buf[buflen], *cn, *msg;
822 int choice = -1, ret = -1;
823 char *title =
dStrconcat(
"Dillo TLS security warning: ",srv->hostname,NULL);
825#if OPENSSL_VERSION_NUMBER < 0x30000000L
826 remote_cert = SSL_get_peer_certificate(ssl);
829 remote_cert = SSL_get1_peer_certificate(ssl);
831 if (remote_cert == NULL){
834 "The remote system is not presenting a certificate. "
835 "This site cannot be trusted. Sending data is not safe.",
836 "Continue",
"Cancel", NULL);
845 st = SSL_get_verify_result(ssl);
850 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
854 X509_NAME *subject_name = X509_get_subject_name(remote_cert);
855 char *subj = X509_NAME_oneline(subject_name, NULL, 0);
856 if ((cn = strstr(subj,
"/CN=")) == NULL) {
857 strcpy(buf,
"(no CN given)");
863 if ((cn_end = strstr(cn,
"/")) == NULL )
864 cn_end = cn + strlen(cn);
866 strncpy(buf, cn, (
size_t) (cn_end - cn));
867 buf[cn_end - cn] =
'\0';
870 msg =
dStrconcat(
"The remote certificate is self-signed and "
871 "untrusted. For address: ", buf, NULL);
873 msg,
"Continue",
"Cancel",
"Save Certificate", NULL);
891 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
892 case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
894 "The issuer for the remote certificate cannot be found. "
895 "The authenticity of the remote certificate cannot be trusted.",
896 "Continue",
"Cancel", NULL);
903 case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
904 case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
905 case X509_V_ERR_CERT_SIGNATURE_FAILURE:
906 case X509_V_ERR_CRL_SIGNATURE_FAILURE:
908 "The remote certificate signature could not be read "
909 "or is invalid and should not be trusted",
910 "Continue",
"Cancel", NULL);
916 case X509_V_ERR_CERT_NOT_YET_VALID:
917 case X509_V_ERR_CRL_NOT_YET_VALID:
919 "Part of the remote certificate is not yet valid. "
920 "Certificates usually have a range of dates over which "
921 "they are to be considered valid, and the certificate "
922 "presented has a starting validity after today's date "
923 "You should be cautious about using this site",
924 "Continue",
"Cancel", NULL);
930 case X509_V_ERR_CERT_HAS_EXPIRED:
931 case X509_V_ERR_CRL_HAS_EXPIRED:
933 msg =
dStrconcat(
"The remote certificate expired on: ", buf,
934 ". This site can no longer be trusted.", NULL);
942 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
943 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
944 case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
945 case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
947 "There was an error in the certificate presented. "
948 "Some of the certificate data was improperly formatted "
949 "making it impossible to determine if the certificate "
950 "is valid. You should not trust this certificate.",
951 "Continue",
"Cancel", NULL);
956 case X509_V_ERR_INVALID_CA:
957 case X509_V_ERR_INVALID_PURPOSE:
958 case X509_V_ERR_CERT_UNTRUSTED:
959 case X509_V_ERR_CERT_REJECTED:
960 case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
962 "One of the certificates in the chain is being used "
963 "incorrectly (possibly due to configuration problems "
964 "with the remote system. The connection should not "
966 "Continue",
"Cancel", NULL);
971 case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
972 case X509_V_ERR_AKID_SKID_MISMATCH:
973 case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
975 "Some of the information presented by the remote system "
976 "does not match other information presented. "
977 "This may be an attempt to eavesdrop on communications",
978 "Continue",
"Cancel", NULL);
983 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
985 msg =
dStrconcat(
"Certificate chain led to a self-signed certificate "
986 "instead of a trusted root. Name: ", buf , NULL);
993 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
995 msg =
dStrconcat(
"The issuer certificate of an untrusted certificate "
996 "cannot be found. Issuer: ", buf, NULL);
1005 "The remote certificate cannot be verified (code %ld)", st);
1007 buf,
"Continue",
"Cancel", NULL);
1013 X509_free(remote_cert);
1020 }
else if (choice == 1) {
1055 if (c->connecting) {
1059 if (c->do_shutdown && !SSL_in_init(c->ssl)) {
1063 SSL_shutdown(c->ssl);
1065 MSG(
"Tls_close_by_key: Avoiding SSL shutdown for: %s\n",
URL_STR(c->url));
1087 MSG(
"Tls_connect: conn for fd %d not valid\n", fd);
1091 if (conn->in_connect) {
1092 MSG(
"Tls_connect: nested call to Tls_connect(), aborting\n");
1095 conn->in_connect =
TRUE;
1098 if (ERR_peek_error()) {
1100 while ((err = ERR_get_error())) {
1101 MSG(
"Tls_connect: queued error: %s\n",
1102 ERR_error_string(err, NULL));
1107 ret = SSL_connect(conn->ssl);
1112 int err1_ret = SSL_get_error(conn->ssl, ret);
1113 if (err1_ret == SSL_ERROR_WANT_READ ||
1114 err1_ret == SSL_ERROR_WANT_WRITE) {
1117 _MSG(
"iowatching fd %d for tls -- want %s\n", fd,
1118 err1_ret == SSL_ERROR_WANT_READ ?
"read" :
"write");
1122 }
else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
1127 conn->do_shutdown =
FALSE;
1129 unsigned long err2_ret = ERR_get_error();
1133 MSG(
"SSL_connect() failed: %s\n",
1134 ERR_error_string(err2_ret, NULL));
1135 }
while ((err2_ret = ERR_get_error()));
1139 MSG(
"TLS connect error: \"an EOF was observed that violates "
1140 "the protocol\"\n");
1145 }
else if (ret == -1) {
1151 assert(errno != EAGAIN && errno != EINTR);
1153 MSG_ERR(
"According to the man page for SSL_get_error(), this "
1154 "was not a possibility (ret %d).\n", ret);
1158 MSG(
"SSL_get_error() returned %d on a connect.\n", err1_ret);
1166 SSL *ssl = conn->ssl;
1167 const char *version = SSL_get_version(ssl);
1168 const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
1171 SSL_CIPHER_get_name(cipher));
1187 conn->connecting =
FALSE;
1189 conn->in_connect =
FALSE;
1197 MSG(
"Connection disappeared. Too long with a popup popped up?\n");
1202 conn->in_connect =
FALSE;
1226 if (ERR_peek_error()) {
1228 while ((err = ERR_get_error())) {
1229 MSG(
"a_Tls_openssl_connect: queued error: %s\n",
1230 ERR_error_string(err, NULL));
1236 unsigned long err_ret = ERR_get_error();
1238 MSG(
"SSL_new() failed: %s\n", ERR_error_string(err_ret, NULL));
1239 }
while ((err_ret = ERR_get_error()));
1244 if (success && !SSL_set_fd(ssl, fd)) {
1245 unsigned long err_ret = ERR_get_error();
1247 MSG(
"SSL_set_fd() failed: %s\n", ERR_error_string(err_ret, NULL));
1248 }
while ((err_ret = ERR_get_error()));
1255#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1260 SSL_set_tlsext_host_name(ssl,
URL_HOST(url));
1285 SSL *ssl = conn->ssl;
1286 int err1_ret = SSL_get_error(ssl, ret);
1287 if (err1_ret == SSL_ERROR_NONE) {
1290 }
else if (err1_ret == SSL_ERROR_ZERO_RETURN) {
1293 }
else if (err1_ret == SSL_ERROR_WANT_READ || err1_ret == SSL_ERROR_WANT_WRITE) {
1296 }
else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
1297 conn->do_shutdown =
FALSE;
1298 unsigned long err2_ret = ERR_get_error();
1301 MSG(
"%s failed: %s\n", where, ERR_error_string(err2_ret, NULL));
1302 }
while ((err2_ret = ERR_get_error()));
1305 const char *which = err1_ret == SSL_ERROR_SYSCALL ?
"SYSCALL" :
"SSL";
1306 MSG(
"%s failed: SSL_ERROR_%s\n", where, which);
1312 MSG(
"%s failed: SSL_get_error() returned %d\n", where, err1_ret);
1322 Conn_t *c = (Conn_t*)conn;
1331 Conn_t *c = (Conn_t*)conn;
1351 for (i = 0; i < n; i++) {
1366 for (i = 0; i < n; i++) {
int a_Dialog_choice(const char *title, const char *msg,...)
Make a question-dialog with a question and alternatives.
char * dStrconcat(const char *s1,...)
Concatenate a NULL-terminated list of strings.
void dList_insert_sorted(Dlist *lp, void *data, dCompareFunc func)
Insert an element into a sorted list.
int dStrAsciiCasecmp(const char *s1, const char *s2)
void dStr_sprintfa(Dstr *ds, const char *format,...)
Printf-like function that appends.
char * dStrdup(const char *s)
Dlist * dList_new(int size)
Create a new empty list.
int dList_length(Dlist *lp)
For completing the ADT.
void * dList_nth_data(Dlist *lp, int n0)
Return the nth data item, NULL when not found or 'n0' is out of range.
void dList_remove_fast(Dlist *lp, const void *data)
Remove a data item without preserving order.
void dStr_free(Dstr *ds, int all)
Free a dillo string.
int dClose(int fd)
Close a FD handling EINTR.
void dStr_sprintf(Dstr *ds, const char *format,...)
Printf-like function.
Dstr * dStr_new(const char *s)
Create a new string.
void dList_append(Dlist *lp, void *data)
Append a data item to the list.
void * dList_find_sorted(Dlist *lp, const void *data, dCompareFunc func)
Search a sorted list.
void dList_free(Dlist *lp)
Free a list (not its elements)
void * dList_find_custom(Dlist *lp, const void *data, dCompareFunc func)
Search a data item using a custom function.
char * dGethomedir(void)
Return the home directory in a static string (don't free)
#define dNew0(type, count)
#define dNew(type, count)
void a_Http_connect_done(int fd, bool_t success)
void a_IOwatch_add_fd(int fd, int when, Fl_FD_Handler Callback, void *usr_data=0)
Hook a Callback for a certain activities in a FD.
void a_IOwatch_remove_fd(int fd, int when)
Remove a Callback for a given FD (or just remove some events)
void a_Klist_remove(Klist_t *Klist, int Key)
Remove data by Key.
void * a_Klist_get_data(Klist_t *Klist, int Key)
Return the data pointer for a given Key (or NULL if not found)
int a_Klist_insert(Klist_t **Klist, void *Data)
Insert a data pointer and return a key for it.
#define TLS_CONNECT_NEVER
#define TLS_CONNECT_READY
#define TLS_CONNECT_NOT_YET
void a_Tls_openssl_reset_server_state(const DilloUrl *url)
static int Tls_examine_certificate(SSL *ssl, Server_t *srv)
static void Tls_fd_map_remove_all(void)
static void Tls_fd_map_remove_entry(int fd)
static void Tls_get_issuer_name(X509 *cert, char *buf, uint_t buflen)
static void Tls_servers_freeall(void)
int a_Tls_openssl_read(void *conn, void *buf, size_t len)
static X509 * Tls_get_end_of_chain(SSL *ssl)
static int Tls_servers_cmp(const void *v1, const void *v2)
static void Tls_info_cb(const SSL *ssl, int where, int ret)
void * a_Tls_openssl_connection(int fd)
static int Tls_handle_error(Conn_t *conn, int ret, const char *where)
int a_Tls_openssl_connect_ready(const DilloUrl *url)
#define CERT_STATUS_USER_ACCEPTED
static void Tls_fd_map_add_entry(int fd, int connkey)
static int Tls_fd_map_cmp(const void *v1, const void *v2)
static int Tls_user_said_no(const DilloUrl *url)
static int Tls_conn_new(int fd, const DilloUrl *url, SSL *ssl)
static void Tls_close_by_key(int connkey)
#define CERT_STATUS_RECEIVING
void a_Tls_openssl_init(void)
static void Tls_get_expiration_str(X509 *cert, char *buf, uint_t buflen)
#define CERT_STATUS_CLEAN
static SSL_CTX * ssl_context
static int Tls_servers_by_url_cmp(const void *v1, const void *v2)
static void Tls_connect_cb(int fd, void *vconnkey)
static void Tls_connect(int fd, int connkey)
void a_Tls_openssl_close_by_fd(int fd)
static bool_t Tls_check_cert_hostname(X509 *cert, const char *host, int *choice)
void a_Tls_openssl_connect(int fd, const DilloUrl *url)
static int Tls_save_certificate_home(X509 *cert)
static int Tls_cert_status(const DilloUrl *url)
static void Tls_load_certificates(void)
static Klist_t * conn_list
static bool_t Tls_check_cert_strength(SSL *ssl, Server_t *srv, int *choice)
int a_Tls_openssl_certificate_is_clean(const DilloUrl *url)
void a_Tls_openssl_freeall(void)
static bool_t pattern_match(const char *pattern, const char *string)
int a_Tls_openssl_write(void *conn, void *buf, size_t len)
void a_Url_free(DilloUrl *url)
Free a DilloUrl.
int a_Url_host_type(const char *host)
What type of host is this?
DilloUrl * a_Url_dup(const DilloUrl *ori)
Duplicate a Url structure.