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())
253 const char *ver = OpenSSL_version(OPENSSL_VERSION);
254 if (snprintf(buf, n,
"%s", ver) >= n)
258 char *sp1 = strchr(ossl,
' ');
261 char *sp2 = strchr(ossl,
' ');
275 MSG(
"TLS library: %s\n", OpenSSL_version(OPENSSL_VERSION));
277 SSL_load_error_strings();
278 if (RAND_status() != 1) {
282 MSG_ERR(
"Disabling HTTPS: Insufficient entropy for openssl.\n");
289 MSG_ERR(
"Disabling HTTPS: Error creating SSL context.\n");
301 "ALL:!aNULL:!eNULL:!LOW:!EXPORT40:!RC4");
307 SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION);
310 SSL_CTX_set_verify(
ssl_context, SSL_VERIFY_NONE, NULL);
333 snprintf(buf, 4096,
"%s/.dillo/certs/",
dGethomedir());
337 snprintf(buf, 4096,
"%s/.dillo/certs/%lx.%u",
345 MSG(
"Unable to open cert save file in home dir\n");
348 PEM_write_X509(fp, cert);
350 MSG(
"Wrote certificate\n");
369 const Server_t *s1 = (
const Server_t *)v1, *s2 = (
const Server_t *)v2;
373 cmp = s1->port - s2->port;
381 const Server_t *s = (
const Server_t *)v1;
415 s =
dNew(Server_t, 1);
466 STACK_OF(X509) *sk = SSL_get_peer_cert_chain(ssl);
469 const uint_t buflen = 4096;
471 int rc, i, n = sk_X509_num(sk);
473 EVP_PKEY *public_key;
474 int key_type, key_bits;
475 const char *type_str;
478 for (i = 0; i < n; i++) {
479 cert = sk_X509_value(sk, i);
480 public_key = X509_get_pubkey(cert);
487 b = BIO_new(BIO_s_mem());
488 const ASN1_OBJECT *algorithm;
489 X509_ALGOR_get0(&algorithm, NULL, NULL, X509_get0_tbs_sigalg(cert));
490 rc = i2a_ASN1_OBJECT(b, algorithm);
493 rc = BIO_gets(b, buf, buflen);
496 strcpy(buf,
"(unknown)");
497 buf[buflen-1] =
'\0';
499 char *s = strstr(buf,
"With");
503 if (!strcmp(buf,
"sha1")) {
505 MSG_WARN(
"In 2015, browsers have begun to deprecate SHA1 "
507 }
else if (!strncmp(buf,
"md", 2) && success ==
TRUE) {
508 const char *msg =
"A certificate in the chain uses the MD5 "
509 "signature algorithm, which is too weak "
512 "Continue",
"Cancel", NULL);
523#if OPENSSL_VERSION_NUMBER < 0x30000000L
524 key_type = EVP_PKEY_type(EVP_PKEY_id(public_key));
526 key_type = EVP_PKEY_type(EVP_PKEY_get_id(public_key));
528 type_str = key_type == EVP_PKEY_RSA ?
"RSA" :
529 key_type == EVP_PKEY_DSA ?
"DSA" :
530 key_type == EVP_PKEY_DH ?
"DH" :
531 key_type == EVP_PKEY_EC ?
"EC" :
"???";
532 key_bits = EVP_PKEY_bits(public_key);
533 X509_NAME_oneline(X509_get_subject_name(cert), buf, buflen);
534 buf[buflen-1] =
'\0';
536 MSG(
"%d-bit %s: %s\n", key_bits, type_str, buf);
537 EVP_PKEY_free(public_key);
539 if (key_type == EVP_PKEY_RSA && key_bits <= 1024) {
541 MSG_WARN(
"In 2014/5, browsers have been deprecating 1024-bit "
547 X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen);
548 buf[buflen-1] =
'\0';
550 MSG(
"root: %s\n", buf);
558#define ASTERISK_EXCLUDES_DOT
577 const char *p = pattern, *n = string;
579 for (; (c = tolower (*p++)) !=
'\0'; n++)
582 for (c = tolower (*p); c ==
'*'; c = tolower (*++p))
584 for (; *n !=
'\0'; n++)
587#ifdef ASTERISK_EXCLUDES_DOT
595 if (c != tolower (*n))
610 if (cert == NULL || host == NULL)
614 GENERAL_NAMES *subjectAltNames;
616 char common_name[256];
628 subjectAltNames = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL);
635 dStr_sprintf(err,
"Hostname %s does not match any of certificate's "
636 "Subject Alternative Names: ", host);
640 ASN1_OCTET_STRING *host_in_octet_string = a2i_IPADDRESS (host);
642 int numaltnames = sk_GENERAL_NAME_num (subjectAltNames);
644 for (i=0; i < numaltnames; i++)
646 const GENERAL_NAME *name =
647 sk_GENERAL_NAME_value (subjectAltNames, i);
650 if (host_in_octet_string)
652 if (name->type == GEN_IPADD)
657 alt_name_checked =
TRUE;
658 if (!ASN1_STRING_cmp (host_in_octet_string,
664 else if (name->type == GEN_DNS)
668 unsigned char *name_in_utf8 = NULL;
671 alt_name_checked =
TRUE;
673 if (0 <= ASN1_STRING_to_UTF8 (&name_in_utf8, name->d.dNSName))
677 (strlen ((
char *)name_in_utf8) ==
678 (
size_t)ASN1_STRING_length (name->d.dNSName)))
680 OPENSSL_free (name_in_utf8);
684 OPENSSL_free (name_in_utf8);
689 sk_GENERAL_NAME_pop_free(subjectAltNames, GENERAL_NAME_free);
690 if (host_in_octet_string)
691 ASN1_OCTET_STRING_free(host_in_octet_string);
693 if (alt_name_checked ==
TRUE && i >= numaltnames)
697 err->
str,
"Continue",
"Cancel", NULL);
712 if (alt_name_checked ==
FALSE)
715 X509_NAME *xname = X509_get_subject_name(cert);
716 common_name[0] =
'\0';
717 X509_NAME_get_text_by_NID (xname, NID_commonName, common_name,
718 sizeof (common_name));
723 msg =
dStrconcat(
"Certificate common name ", common_name,
724 " doesn't match requested host name ", host, NULL);
726 msg,
"Continue",
"Cancel", NULL);
750 X509_NAME_ENTRY *xentry;
756 j = X509_NAME_get_index_by_NID (xname, NID_commonName, i);
762 xentry = X509_NAME_get_entry(xname,i);
763 sdata = X509_NAME_ENTRY_get_data(xentry);
764 if (strlen (common_name) != (
size_t)ASN1_STRING_length (sdata))
767 msg =
dStrconcat(
"Certificate common name is invalid (contains a NUL "
768 "character). This may be an indication that the "
769 "host is not who it claims to be -- that is, not "
770 "the real ", host, NULL);
772 msg,
"Continue",
"Cancel", NULL);
801 STACK_OF(X509) *sk = SSL_get_peer_cert_chain(ssl);
803 return sk ? sk_X509_value(sk, sk_X509_num(sk) - 1) : NULL;
809 X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen);
811 strcpy(buf,
"(unknown)");
812 buf[buflen-1] =
'\0';
818 ASN1_TIME *exp_date = X509_get_notAfter(cert);
819 BIO *b = BIO_new(BIO_s_mem());
820 int rc = ASN1_TIME_print(b, exp_date);
823 rc = BIO_gets(b, buf, buflen);
826 strcpy(buf,
"(unknown)");
827 buf[buflen-1] =
'\0';
841 const uint_t buflen = 4096;
842 char buf[buflen], *cn, *msg;
843 int choice = -1, ret = -1;
844 char *title =
dStrconcat(
"Dillo TLS security warning: ",srv->hostname,NULL);
846#if OPENSSL_VERSION_NUMBER < 0x30000000L
847 remote_cert = SSL_get_peer_certificate(ssl);
850 remote_cert = SSL_get1_peer_certificate(ssl);
852 if (remote_cert == NULL){
855 "The remote system is not presenting a certificate. "
856 "This site cannot be trusted. Sending data is not safe.",
857 "Continue",
"Cancel", NULL);
866 st = SSL_get_verify_result(ssl);
871 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
875 X509_NAME *subject_name = X509_get_subject_name(remote_cert);
876 char *subj = X509_NAME_oneline(subject_name, NULL, 0);
877 if ((cn = strstr(subj,
"/CN=")) == NULL) {
878 strcpy(buf,
"(no CN given)");
884 if ((cn_end = strstr(cn,
"/")) == NULL )
885 cn_end = cn + strlen(cn);
887 strncpy(buf, cn, (
size_t) (cn_end - cn));
888 buf[cn_end - cn] =
'\0';
891 msg =
dStrconcat(
"The remote certificate is self-signed and "
892 "untrusted. For address: ", buf, NULL);
894 msg,
"Continue",
"Cancel",
"Save Certificate", NULL);
912 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
913 case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
915 "The issuer for the remote certificate cannot be found. "
916 "The authenticity of the remote certificate cannot be trusted.",
917 "Continue",
"Cancel", NULL);
924 case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
925 case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
926 case X509_V_ERR_CERT_SIGNATURE_FAILURE:
927 case X509_V_ERR_CRL_SIGNATURE_FAILURE:
929 "The remote certificate signature could not be read "
930 "or is invalid and should not be trusted",
931 "Continue",
"Cancel", NULL);
937 case X509_V_ERR_CERT_NOT_YET_VALID:
938 case X509_V_ERR_CRL_NOT_YET_VALID:
940 "Part of the remote certificate is not yet valid. "
941 "Certificates usually have a range of dates over which "
942 "they are to be considered valid, and the certificate "
943 "presented has a starting validity after today's date "
944 "You should be cautious about using this site",
945 "Continue",
"Cancel", NULL);
951 case X509_V_ERR_CERT_HAS_EXPIRED:
952 case X509_V_ERR_CRL_HAS_EXPIRED:
954 msg =
dStrconcat(
"The remote certificate expired on: ", buf,
955 ". This site can no longer be trusted.", NULL);
963 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
964 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
965 case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
966 case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
968 "There was an error in the certificate presented. "
969 "Some of the certificate data was improperly formatted "
970 "making it impossible to determine if the certificate "
971 "is valid. You should not trust this certificate.",
972 "Continue",
"Cancel", NULL);
977 case X509_V_ERR_INVALID_CA:
978 case X509_V_ERR_INVALID_PURPOSE:
979 case X509_V_ERR_CERT_UNTRUSTED:
980 case X509_V_ERR_CERT_REJECTED:
981 case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
983 "One of the certificates in the chain is being used "
984 "incorrectly (possibly due to configuration problems "
985 "with the remote system. The connection should not "
987 "Continue",
"Cancel", NULL);
992 case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
993 case X509_V_ERR_AKID_SKID_MISMATCH:
994 case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
996 "Some of the information presented by the remote system "
997 "does not match other information presented. "
998 "This may be an attempt to eavesdrop on communications",
999 "Continue",
"Cancel", NULL);
1004 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
1006 msg =
dStrconcat(
"Certificate chain led to a self-signed certificate "
1007 "instead of a trusted root. Name: ", buf , NULL);
1014 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
1016 msg =
dStrconcat(
"The issuer certificate of an untrusted certificate "
1017 "cannot be found. Issuer: ", buf, NULL);
1026 "The remote certificate cannot be verified (code %ld)", st);
1028 buf,
"Continue",
"Cancel", NULL);
1034 X509_free(remote_cert);
1041 }
else if (choice == 1) {
1076 if (c->connecting) {
1080 if (c->do_shutdown && !SSL_in_init(c->ssl)) {
1084 SSL_shutdown(c->ssl);
1086 MSG(
"Tls_close_by_key: Avoiding SSL shutdown for: %s\n",
URL_STR(c->url));
1108 MSG(
"Tls_connect: conn for fd %d not valid\n", fd);
1112 if (conn->in_connect) {
1113 MSG(
"Tls_connect: nested call to Tls_connect(), aborting\n");
1116 conn->in_connect =
TRUE;
1119 if (ERR_peek_error()) {
1121 while ((err = ERR_get_error())) {
1122 MSG(
"Tls_connect: queued error: %s\n",
1123 ERR_error_string(err, NULL));
1128 ret = SSL_connect(conn->ssl);
1133 int err1_ret = SSL_get_error(conn->ssl, ret);
1134 if (err1_ret == SSL_ERROR_WANT_READ ||
1135 err1_ret == SSL_ERROR_WANT_WRITE) {
1138 _MSG(
"iowatching fd %d for tls -- want %s\n", fd,
1139 err1_ret == SSL_ERROR_WANT_READ ?
"read" :
"write");
1143 }
else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
1148 conn->do_shutdown =
FALSE;
1150 unsigned long err2_ret = ERR_get_error();
1154 MSG(
"SSL_connect() failed: %s\n",
1155 ERR_error_string(err2_ret, NULL));
1156 }
while ((err2_ret = ERR_get_error()));
1160 MSG(
"TLS connect error: \"an EOF was observed that violates "
1161 "the protocol\"\n");
1166 }
else if (ret == -1) {
1172 assert(errno != EAGAIN && errno != EINTR);
1174 MSG_ERR(
"According to the man page for SSL_get_error(), this "
1175 "was not a possibility (ret %d).\n", ret);
1179 MSG(
"SSL_get_error() returned %d on a connect.\n", err1_ret);
1187 SSL *ssl = conn->ssl;
1188 const char *version = SSL_get_version(ssl);
1189 const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
1192 SSL_CIPHER_get_name(cipher));
1208 conn->connecting =
FALSE;
1210 conn->in_connect =
FALSE;
1218 MSG(
"Connection disappeared. Too long with a popup popped up?\n");
1223 conn->in_connect =
FALSE;
1247 if (ERR_peek_error()) {
1249 while ((err = ERR_get_error())) {
1250 MSG(
"a_Tls_openssl_connect: queued error: %s\n",
1251 ERR_error_string(err, NULL));
1257 unsigned long err_ret = ERR_get_error();
1259 MSG(
"SSL_new() failed: %s\n", ERR_error_string(err_ret, NULL));
1260 }
while ((err_ret = ERR_get_error()));
1265 if (success && !SSL_set_fd(ssl, fd)) {
1266 unsigned long err_ret = ERR_get_error();
1268 MSG(
"SSL_set_fd() failed: %s\n", ERR_error_string(err_ret, NULL));
1269 }
while ((err_ret = ERR_get_error()));
1276#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1281 SSL_set_tlsext_host_name(ssl,
URL_HOST(url));
1306 SSL *ssl = conn->ssl;
1307 int err1_ret = SSL_get_error(ssl, ret);
1308 if (err1_ret == SSL_ERROR_NONE) {
1311 }
else if (err1_ret == SSL_ERROR_ZERO_RETURN) {
1314 }
else if (err1_ret == SSL_ERROR_WANT_READ || err1_ret == SSL_ERROR_WANT_WRITE) {
1317 }
else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
1318 conn->do_shutdown =
FALSE;
1319 unsigned long err2_ret = ERR_get_error();
1322 MSG(
"%s failed: %s\n", where, ERR_error_string(err2_ret, NULL));
1323 }
while ((err2_ret = ERR_get_error()));
1326 const char *which = err1_ret == SSL_ERROR_SYSCALL ?
"SYSCALL" :
"SSL";
1327 MSG(
"%s failed: SSL_ERROR_%s\n", where, which);
1333 MSG(
"%s failed: SSL_get_error() returned %d\n", where, err1_ret);
1343 Conn_t *c = (Conn_t*)conn;
1352 Conn_t *c = (Conn_t*)conn;
1372 for (i = 0; i < n; i++) {
1387 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)
const char * a_Tls_openssl_version(char *buf, int n)
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.