diff options
-rw-r--r-- | src/IO/tls.c | 199 |
1 files changed, 113 insertions, 86 deletions
diff --git a/src/IO/tls.c b/src/IO/tls.c index dfe76744..cfe050ec 100644 --- a/src/IO/tls.c +++ b/src/IO/tls.c @@ -427,6 +427,106 @@ int a_Tls_certificate_is_clean(const DilloUrl *url) return Tls_cert_status(url) == CERT_STATUS_CLEAN; } +/* + * 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. + */ +static bool_t Tls_check_cert_strength(SSL *ssl, Server_t *srv, int *choice) +{ + /* print for first connection to server */ + const bool_t print_chain = srv->cert_status == CERT_STATUS_RECEIVING; + bool_t success = TRUE; + + 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"); + } + } + + if (cert) { + X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen); + buf[buflen-1] = '\0'; + if (print_chain) + MSG("root: %s\n", buf); + } + } + return success; +} + /******************** BEGINNING OF STUFF DERIVED FROM wget-1.16.3 */ #define ASTERISK_EXCLUDES_DOT /* mandated by rfc2818 */ @@ -728,8 +828,8 @@ static int Tls_examine_certificate(SSL *ssl, Server_t *srv,const char *host) if (choice == 1){ ret = 0; } - - } else if (Tls_check_cert_hostname(remote_cert, host, &choice)) { + } else if (Tls_check_cert_strength(ssl, srv, &choice) && + Tls_check_cert_hostname(remote_cert, host, &choice)) { /* Figure out if (and why) the remote system can't be trusted */ st = SSL_get_verify_result(ssl); switch (st) { @@ -902,13 +1002,17 @@ static int Tls_examine_certificate(SSL *ssl, Server_t *srv,const char *host) } dFree(title); - if (choice == 2) + if (choice == -1) { + srv->cert_status = CERT_STATUS_CLEAN; /* no warning popups */ + } else if (choice == 1) { + srv->cert_status = CERT_STATUS_USER_ACCEPTED; /* clicked Continue */ + } else { + /* 2 for Cancel, or 0 when window closed. Treating 0 as meaning 'No' is + * probably not exactly correct, but adding complexity to handle this + * obscure case does not seem justifiable. + */ srv->cert_status = CERT_STATUS_BAD; - else if (choice == -1) - srv->cert_status = CERT_STATUS_CLEAN; - else - srv->cert_status = CERT_STATUS_USER_ACCEPTED; - + } return ret; } @@ -949,82 +1053,6 @@ static void Tls_close_by_key(int connkey) } } -static void Tls_print_cert_chain(SSL *ssl) -{ - 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")) { - MSG_WARN("In 2015, browsers have begun to deprecate SHA1 " - "certificates.\n"); - } else if (!strncmp(buf, "md", 2)) { - MSG_ERR("Browsers stopped accepting MD5 certificates around " - "2012.\n"); - } - } - } - BIO_free(b); - 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'; - 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) { - /* TODO: Gather warnings into one popup. */ - MSG_WARN("In 2014/5, browsers have been deprecating 1024-bit RSA " - "keys.\n"); - } - } - - if (cert) { - X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen); - buf[buflen-1] = '\0'; - MSG("root: %s\n", buf); - } - } -} - /* * Connect, set a callback if it's still not completed. If completed, check * the certificate and report back to http. @@ -1093,14 +1121,13 @@ static void Tls_connect(int fd, int connkey) Tls_servers_by_url_cmp); if (srv->cert_status == CERT_STATUS_RECEIVING) { - /* Making first connection with the server. Show some information. */ + /* 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)); - Tls_print_cert_chain(ssl); } if (srv->cert_status == CERT_STATUS_USER_ACCEPTED || |