See https://github.com/soylent-io/privoxy/pull/1
Some benchmarks
Tested using ab-proxy (https://github.com/maurice2k/ab-proxy) on FreeBSD 12 .1-RELEASE running in VMware ESXi 6.7, 2G RAM, 4 vCPU (Intel(R) Xeon(R) CPU E5-2630 0 @ 2.30GHz)
mbedtls
(6.5K html file)
root@lab-mg1:~/ab-proxy# ./ab-proxy -c=100 -n=10000 --bursts=3 --show-errors -H "Connection: close" -X http://172.16.0.127:8118/ https://download.soylent.io/test.html Benchmarking 'https://download.soylent.io/test.html' using proxy 'http://172.16.0.127:8118/' with a total of 30000 GET requests: Number of bursts: 3 Number of request per burst 10000 Concurrency level: 100 Time taken for tests: 5m12.699317685s Total initiated requests: 30000 Completed requests: 30000 HTTP-200 completed: 30000 Failed requests: 0 Total transferred: 199890000 bytes Requests per second: 95.939 Time per request: 10.42331ms
(157K html file)
root@lab-mg1:~/ab-proxy# ./ab-proxy -c=100 -n=10000 --bursts=3 --show-errors -H "Connection: close" -X http://172.16.0.127:8118/ https://download.soylent.io/fb.html Benchmarking 'https://download.soylent.io/fb.html' using proxy 'http://172.16.0.127:8118/' with a total of 30000 GET requests: Number of bursts: 3 Number of request per burst 10000 Concurrency level: 100 Time taken for tests: 5m20.061175585s Total initiated requests: 30000 Completed requests: 30000 HTTP-200 completed: 30000 Failed requests: 0 Total transferred: 4819230000 bytes Requests per second: 93.732 Time per request: 10.668705ms
openssl
(6.5K html file)
root@lab-mg1:~/ab-proxy# ./ab-proxy -c=100 -n=10000 --bursts=3 --show-errors -H "Connection: close" -X http://172.16.0.127:8118/ https://download.soylent.io/test.html Benchmarking 'https://download.soylent.io/test.html' using proxy 'http://172.16.0.127:8118/' with a total of 30000 GET requests: Number of bursts: 3 Number of request per burst 10000 Concurrency level: 100 Time taken for tests: 2m15.991686555s Total initiated requests: 30000 Completed requests: 30000 HTTP-200 completed: 30000 Failed requests: 0 Total transferred: 199890000 bytes Requests per second: 220.602 Time per request: 4.533056ms
(157K html file)
root@lab-mg1:~/ab-proxy# ./ab-proxy -c=100 -n=10000 --bursts=3 --show-errors -H "Connection: close" -X http://172.16.0.127:8118/ https://download.soylent.io/fb.html Benchmarking 'https://download.soylent.io/fb.html' using proxy 'http://172.16.0.127:8118/' with a total of 30000 GET requests: Number of bursts: 3 Number of request per burst 10000 Concurrency level: 100 Time taken for tests: 2m45.293560019s Total initiated requests: 30000 Completed requests: 30000 HTTP-200 completed: 30000 Failed requests: 0 Total transferred: 4819230000 bytes Requests per second: 181.495 Time per request: 5.509785ms
Summary: about 100% more RPS with with openssl (2x improvement)
Thanks a lot for the patch.
Unfortunately it does not compile for me on a system based on FreeBSD 11.4-STABLE using OpenSSL 1.0.2t from base:
fk@t520 ~/git/privoxy.git $gmake
[...]
cc -c -pipe -fstack-protector-all -ggdb -Wshadow -Wconversion -I/usr/local/include/ -pthread -Wall ssl_common.c -o ssl_common.o
cc -c -pipe -fstack-protector-all -ggdb -Wshadow -Wconversion -I/usr/local/include/ -pthread -Wall openssl.c -o openssl.o
openssl.c:1656:7: warning: implicit declaration of function 'SSL_COMP_free_compression_methods' is invalid in C99 [-Wimplicit-function-declaration]
SSL_COMP_free_compression_methods();
^
openssl.c:1661:7: warning: implicit declaration of function 'COMP_zlib_cleanup' is invalid in C99 [-Wimplicit-function-declaration]
COMP_zlib_cleanup();
^
2 warnings generated.
cc -L/usr/local/lib -pthread -o privoxy actions.o cgi.o cgiedit.o cgisimple.o deanimate.o encode.o errlog.o filters.o gateway.o jbsockets.o jcc.o list.o loadcfg.o loaders.o miscutil.o parsers.o ssplit.o urlmatch.o client-tags.o pcrs.o ssl_common.o openssl.o -lbrotlidec -lcrypto -lz -lpcre -lpcreposix -lssl -lcrypto -lbrotlidec
openssl.o: In function
ssl_release': /home/fk/git/privoxy.git/openssl.c:1656: undefined reference to
SSL_COMP_free_compression_methods'cc: error: linker command failed with exit code 1 (use -v to see invocation)
gmake: *** [GNUmakefile:778: privoxy] Error 1
The complaint about COMP_zlib_cleanup() can be silenced by including openssl/comp.h.
SSL_COMP_free_compression_methods() seems to be defined in openssl/ssl.h but including that header doesn't help.
Any ideas?
My mistake. My build environment included libressl which was picked up instead of the base openssl.
After removing libressl from the build environment and with minor changes this built on a system based on FreeBSD 11.4-STABLE.
It seems that invalid certificates aren't handled properly as can be tested at:
https://badssl.com/
1) If I open https://expired.badssl.com/ with default settings I get the "Connection failure" page and the log shows:
Error: BIO_do_handshake failed: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed
The expected behavior is that ssl_send_certificate_error() is used to explain the certificate error.
2) If I use the action section:
{+ignore-certificate-errors}
expired.badssl.com/
opening https://expired.badssl.com/ results in ssl_send_certificate_error() getting called but the error reason is "Reason: error:FFFFFFFF:lib(255):func(4095):reason(4095)" which obviously isn't very helpful.
The expected behaviour is that the request is let through despite the certificate error.
Can you please propose fixes?
Another issue is that generated certificates aren't renewed when they are expired.
The following seems to fix this:
diff --git a/openssl.c b/openssl.c
index 1904317c..447cb44c 100644
--- a/openssl.c
+++ b/openssl.c
@@ -1179,7 +1179,7 @@ static int ssl_certificate_is_invalid(const char *cert_file)
return 1;
}
ret = X509_cmp_current_time(X509_get_notAfter(cert));
if (ret == 0)
{
log_ssl_errors(LOG_LEVEL_ERROR,
@@ -1188,7 +1188,7 @@ static int ssl_certificate_is_invalid(const char *cert_file)
X509_free(cert);
return ret == -1 ? 0 : 1;
}
Do you agree?
Also the code allows the use of SSLv3 which has the POODLE vulnerability.
It should probably be disabled with something like:
--- a/openssl.c
+++ b/openssl.c
@@ -649,6 +649,8 @@ extern int create_server_ssl_connection(struct client_state *csp)
goto exit;
}
+
/*
*/
A few more comments after more testing and reviewing:
1) It looks like SSLv3 should also be disabled in create_client_ssl_connection().
2) In openssl.c's generate_webpage_certificate() an error message references the wrong key:
--- a/openssl.c
+++ b/openssl.c
@@ -1548,7 +1548,7 @@ static int generate_webpage_certificate(struct client_state *csp)
if (!(pk_bio = BIO_new_file(cert_opt.issuer_key, "r")))
{
log_ssl_errors(LOG_LEVEL_ERROR, "Failure opening issuer key %s BIO",
- cert_opt.subject_key);
+ cert_opt.issuer_key);
ret = -1;
goto exit;
}
3) The Parameters comment for both ssl_crt_verify_info() implementations references buf multiple times.
4) In both ssl_release() descriptions "resources" is misspelled.
5) The openssl version of ssl_crt_verify_info() should probably be something like:
--- a/openssl.c
+++ b/openssl.c
@@ -1775,7 +1775,7 @@ exit:
**********/
extern void ssl_crt_verify_info(char buf, size_t size, uint32_t verification_result)
{
- ERR_error_string_n(verification_result, buf, size);
+ strlcpy(buf, X509_verify_cert_error_string(verification_result), size);
}
6) The OpenSSL version of create_server_ssl_connection() currently doesn't properly set csp->server_cert_verification_result which explains why the invalid certifcates aren't handled properly as mentioned in the previous comment.
7) The SSL_get_peer_certificate() call in SSL_get_peer_certificate() seems to always fail. Probably the certificates has to be gathered from a callback set with SSL_CTX_set_verify() similar to the way it's done when using mbedTLS.
8) It seems that the OpenSSL version currently does not validate that the certificate matches the host. Example: https://wrong.host.badssl.com/
The comment "Set the hostname to check against the received server certificate" above SSL_set_tlsext_host_name() appears to be incorrect and the function merely sets the SNI extension but does not affect the certificate validation.
@fabiankeil please check new commit in the PR which should fix all the issues found during the review. Thanks!
Last edit: Maxim Khon 2020-08-26
Thanks a lot for the update.
I spotted a few more issues but they are all minor and I already have fixes for them in my local repository:
1) ssl_send_data() can loop endlessly if BIO_write() consistently returns 0.
I've fixed this with:
--- a/openssl.c
+++ b/openssl.c
@@ -171,7 +171,7 @@ extern int ssl_send_data(struct ssl_attr ssl_attr, const unsigned char buf, si
/
while ((ret = BIO_write(bio,
(const unsigned char )(buf + pos),
- send_len)) < 0)
+ send_len)) <= 0)
{
if (!BIO_should_retry(bio))
2) ssl_store_cert() does not check html_encode()'s return code for NULL.
This is probalby a copy and paste issue as ssl_verify_callback() lacks the check as well.
I fixed both.
3) generate_key() does not check the return code of BN_set_word()
4) Several error messages in generate_webpage_certificate() are incorrect.
This looks like a copy and paste issue as well.
So far the code seems to be working great and I could reproduce the "2x improvement".
If no new issues are discovered I intend to squash and push your commits at the end of the month.
Squashed and pushed with a couple of follow-up fixes.