In some cases, the "TLS extensions" line output for the "--server-defaults" option will not show `"encrypt-then-mac/#22"` even if the server supports this extension. The reason is that a server will only include this extension in the ServerHello message if it supports the extension and the selected cipher is a CBC cipher. So, if `determine_tls_extensions()` connects to the server with a non-CBC cipher, then it will not detect if the server supports the encrypt-then-mac extension.
It is possible that support for the extension will be detected by `get_server_certificate()`, but only if one of the calls to that function results in a CBC cipher being selected and OpenSSL 1.1.0 is being used (as prior versions did not support the encrypt-then-mac extension).
In this PR, if `determine_tls_extensions()` is called and `$TLS_EXTENSIONS` does not already contain `"encrypt-then-mac/#22"`, then an attempt will be made to connect to the server with only CBC ciphers specified in the ClientHello. If the connection is not successful (presumably because the server does not support any CBC ciphers), then a second connection attempt will be made with the "default" ciphers being specified in the ClientHello.
en.wikipedia.org is an example of a server that supports the encrypt-then-mac extension, but for which the support is not currently detected (unless OpenSSL 1.1.0 is used) since in the call to `determine_tls_extension()` a non-CBC cipher is selected.
This PR changes `read_dhbits_from_file()` so that, when the "quiet" parameter is absent, the selected curve is shown in addition to the number of bits. This PR only affects the output of `run_client_simulation()` and the `Negotiated cipher` in `run_server_preference()`.
There are two places in `run_client_simulation()` in which `$OPENSSL s_client` is called, after which there is a `debugme echo` line to display the `$OPENSSL s_client` command line when testssl.sh is being run in debug mode, and then `sclient_connect_successful $? $TMPFILE` is called to determine whether `$OPENSSL s_client` successfully established a connection.
So, `sclient_connect_successful()` is being passed the result of the `debugme()` call, which always returns 0, rather than the result of the `$OPENSSL s_client` call.
This PR fixes the problem by moving the `debugme()` line to before the call to `$OPENSSL s_client`, so that `sclient_connect_successful()` is passed the results of the `$OPENSSL s_client` call.
Starting with OpenSSL 1.1.0, s_client will not offer TLS compression methods, even if OpenSSL is compiled with zlib support, unless the `-comp` flag is included in the command line.
This PR changes `run_crime()` to use `tls_sockets()` rather than failing if `$OPENSSL` lacks zlib support, unless `$SSL_NATIVE` is `true`.
At the moment, the ClientHello created by `socksend_tls_clienthello()` only specifies the NULL compression method. So, this PR adds a new parameter to `socksend_tls_clienthello()` and `tls_sockets()` to allow to caller to request that additional compression methods (DEFLATE and LZS) be specified in the ClientHello.
This PR makes another change to `run_crime()`. At the moment, if `$OPENSSL s_client` fails to connect to the server, `run_crime()` will report that the server is not vulnerable, since the output from `$OPENSSL s_client` includes the line "Compression: NONE" (see below). This PR changes that by checking whether the connection was successful, and reporting a "test failed (couldn't connect)" warning if it wasn't successful, rather than reporting "not vulnerable (OK)".
```
CONNECTED(00000003)
140338777061024:error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version:s23_clnt.c:769:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 389 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1
Cipher : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1483645971
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
```
This PR fixes a few bugs in `sslv2_sockets()`. The main issue is that a server may not send the entire ServerHello in a single packet. If it doesn't and the full response is being parsed (i.e., certificate and list of ciphers), then `parse_sslv2_serverhello()` will encounter errors, since it assumes that it has the entire ServerHello. This PR compares the length of the response to the length of the ServerHello as specified in the first two bytes of the response and requests more data from the server if the response appears incomplete.
This PR also modifies `parse_sslv2_serverhello()` to check for more errors. It compares the length of the response it has been provided to the specified length (`$v2_hello_length`) and returns an error if the response is shorter than `$v2_hello_length` and the full response is supposed to be parsed. It will also check whether there was an error in converting the certificate from DER to PEM format and will return an error if there was (and it will suppress the error message).
Some servers respond to an SSLv2 ClientHello with a list of all SSLv2 ciphers that the server supports rather than just a list of ciphers that it supports in common with the client (i.e., that appear in the ClientHello). This PR changes the sockets version of `std_cipherlists()` so that, if `sslv2_sockets()` is successful, it checks whether there are any ciphers in common between the ClientHello and the ServerHello before declaring that the server supports the specified cipher list.
Some servers respond to an SSLv2 ClientHello with a list of all SSLv2 ciphers that the server supports rather than just a list of ciphers that it supports in common with the client (i.e., that appear in the ClientHello). This PR changes the sockets version of `run_freak()` so that, if `sslv2_sockets()` is successful, it checks whether there are any ciphers in common between the ClientHello and the ServerHello before declaring that the server supports an export RSA cipher.
If `determine_tls_extensions()` does not create a temporary file (`$TEMPDIR/$NODEIP.determine_tls_extensions.txt`) then `run_server_defaults()` will display error messages when an attempt is made to copy this file or to search (grep) it. This may happen if `$OPTIMAL_PROTO` is `-ssl2` or if `determine_tls_extensions()` uses sockets and `parse_tls_serverhello()` encountered an error and did not create a temporary file (`$TEMPDIR/$NODEIP.parse_tls_serverhello.txt`). This PR fixes this by only trying to copy and search `$TEMPDIR/$NODEIP.determine_tls_extensions.txt` is `$OPTIMAL_PROTO` is not `-ssl2` and `determine_tls_extensions()` was successful (return value 0).
In response to your request in #572, this PR provides a starting point for addressing #120. It adds code to `run_logjam()` to try connecting to the server using any cipher that uses an ephemeral DH key. If successful, it gets the server's ephemeral key (in OpenSSL's PEM format) and then extracts the prime from the key and places it in `$dh_p`. So, all that needs to be done at this point is to compare `$dh_p` against a set of "bad" primes. I'm not sure if I'll be able to work on that part soon, so if someone else has the time, that would be great.
I actually found the `-msg` option easy to use. I moved the code in `parse_tls_serverhello()` that extracts the DH ephemeral public key from the ServerKeyExchange message into a separate function. Then, if using OpenSSL with the `-msg` option, I extract the ServerKeyExchange message from `$TMPFILE` and call this new function to extract the key and convert it to PEM format. That way the new code in `run_logjam()` can use either `$OPENSSL` or `tls_sockets()`.
This PR changes `run_http2()` so that it uses `tls_sockets()` rather than failing, if `$OPENSSL` does not support the `-alpn` option. If `$OPENSSL` supports the `-alpn` option (or if `$SSL_NATIVE` is true), then this PR has no effect.
This PR change `run_std_cipherlists()` to use sockets. As noted in isse #554, I have some questions about the definitions of the cipher lists, but I wrote the code so that the ciphers that are tested when using sockets are the same as those that are tested when using OpenSSL. For a few of the cipherlists, the sockets version tests a few additional ciphers; but these are ciphers that are not supported by OpenSSL, and whose definitions are consistent with the ciphers that OpenSSL includes.
As written, `std_cipherlists` will use sockets for testing by default, except in two cases:
* If the `$SSL_NATIVE` is true, then only OpenSSL is used, and if OpenSSL doesn't support any ciphers in the cipherlist, then the test is skipped.
* If `$FAST` is true (but `$SSL_NATIVE` is false), then OpenSSL is used whenever it supports at least one cipher from the cipherlist, and `tls_sockets()` (or `sslv2_sockets()`) is only used when OpenSSL doesn't support any ciphers from the cipherlist.
This PR changes `run_ssl_poodle()` to use sockets. This PR is particularly useful when $OPENSSL is OpenSSL 1.1.0, since OpenSS 1.1.0 does not support SSLv3 by default. But, it is also useful if $OPENSSL supports some, but not all, of the CBC ciphers.
As with `run_beast()`, there is a small change to `$cbc_cipher_list`. The following two ciphers were added:
```
0x00,0x0B - EXP-DH-DSS-DES-CBC-SHA SSLv3 Kx=DH/DSS Au=DH Enc=DES(40) Mac=SHA1 export
0x00,0x0E - EXP-DH-RSA-DES-CBC-SHA SSLv3 Kx=DH/RSA Au=DH Enc=DES(40) Mac=SHA1 export
```
The ciphers that were removed are all SSLv2 ciphers:
```
0x07,0x00,0xC0 - DES-CBC3-MD5 SSLv2 Kx=RSA Au=RSA Enc=3DES(168) Mac=MD5
0x06,0x00,0x40 - DES-CBC-MD5 SSLv2 Kx=RSA Au=RSA Enc=DES(56) Mac=MD5
0x04,0x00,0x80 - EXP-RC2-CBC-MD5 SSLv2 Kx=RSA(512) Au=RSA Enc=RC2(40) Mac=MD5 export
0x05,0x00,0x80 - IDEA-CBC-MD5 SSLv2 Kx=RSA Au=RSA Enc=IDEA(128) Mac=MD5
0x03,0x00,0x80 - RC2-CBC-MD5 SSLv2 Kx=RSA Au=RSA Enc=RC2(128) Mac=MD5
```
(EXP-RC2-CBC-MD5 is both an SSLv2 and an SSLv3 cipher. Previously it was listed twice in `$cbc_cipher_list`, now it appears once.)
In a few places testssl.sh tries to determine $OPENSSL s_client's capabilities by calling `$OPENSSL s_client` without specifying a host to which to connect. For example:
```
$OPENSSL s_client -no_ssl2 2>&1
```
This idea is that `$OPENSSL s_client` should reveal something about its capabilities without actually trying to connect to a host.
This works in most cases. However, the manual pages for s_client states:
```
-connect host:port
This specifies the host and optional port to connect to. If not specified then an attempt is made to connect to the local host on port 4433.
```
So, the above call is actually trying to connect to the local host on port 4433. If the local host is running `$OPENSSL s_server`, then `$OPENSSL s_server` will by default be listening on port 4433, and the connection attempt will most likely succeed. Since the `OPENSSL s_client` command does not include a `< /dev/null`, the `OPENSSL s_client` will just hang waiting for additional input.
Adding `-connect x` to the `$OPENSSL s_client` prevents $OPENSSL from trying to connect to a host, but seems to still provide the necessary information about OpenSSL's capabilities.
This PR adds ",exp" to the bits column when `run_rc4()` is run in the "--wide" mode and the cipher is an export cipher. This makes the wide mode of `run_rc4()` align with other functions, such as `run_allciphers()`.
This PR adds the use of sockets to `run_server_preference()` to determine the "Negotiated cipher per proto." It only uses sockets in two cases:
* For SSLv2, if $OPENSSL does not support SSLv2.
* For SSLv2, if $OPENSSL does not support SSLv3.
This PR will have no effect if the provided OpenSSL binaries are used.
When `test_just_one()` uses `neat_list()` with a cipher that is not available and that uses DH for key exchange, the columns do not line up correctly. `test_just_one()` adds "TBD" in gray to "DH", and while `neat_list()` tries to adjust for the presence of color codes, it doesn't seem to correctly handle the gray color code here.
Rather than try to fix this in `neat_list()`, I propose to just remove the "TBD". Adding it is inconsistent with other functions (like `run_allciphers()`), and it seems inappropriate, since there is nothing "to be determined," as the cipher suite isn't supported by the server.
If adding "TBD" were appropriate anywhere, it would seem to be in cases in which the server does support the cipher, but the number of bits in the ephemeral key couldn't be determined because the version of OpenSSL being used can't show DH/ECDH bits. (Not that I'm proposing this. I think the one-line warning, "(Your $OPENSSL cannot show DH/ECDH bits)", is enough.
Here is an example of `test_just_one()` with some ciphers not supported by the server that use DH key exchange:
```
Testing single cipher with word pattern "CAMELLIA" (ignore case)
Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Cipher Suite Name (RFC)
---------------------------------------------------------------------------------------------------------------------------
xc077 ECDHE-RSA-CAMELLIA256-SHA384 ECDH TBD Camellia 256 TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 not a/v
xc073 ECDHE-ECDSA-CAMELLIA256-SHA384 ECDH TBD Camellia 256 TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 not a/v
xc4 DHE-RSA-CAMELLIA256-SHA256 DH TBD Camellia 256 TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 not a/v
xc3 DHE-DSS-CAMELLIA256-SHA256 DH TBD Camellia 256 TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 not a/v
xc2 DH-RSA-CAMELLIA256-SHA256 DH/RSA Camellia 256 TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 not a/v
xc1 DH-DSS-CAMELLIA256-SHA256 DH/DSS Camellia 256 TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 not a/v
x88 DHE-RSA-CAMELLIA256-SHA DH 2048 Camellia 256 TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA available
x87 DHE-DSS-CAMELLIA256-SHA DH TBD Camellia 256 TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA not a/v
x86 DH-RSA-CAMELLIA256-SHA DH/RSA Camellia 256 TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA not a/v
x85 DH-DSS-CAMELLIA256-SHA DH/DSS Camellia 256 TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA not a/v
xc5 ADH-CAMELLIA256-SHA256 DH TBD Camellia 256 TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 not a/v
x89 ADH-CAMELLIA256-SHA DH TBD Camellia 256 TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA not a/v
xc079 ECDH-RSA-CAMELLIA256-SHA384 ECDH/RSA Camellia 256 TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 not a/v
xc075 ECDH-ECDSA-CAMELLIA256-SHA384 ECDH/ECDSA Camellia 256 TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 not a/v
xc0 CAMELLIA256-SHA256 RSA Camellia 256 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 not a/v
x84 CAMELLIA256-SHA RSA Camellia 256 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA not a/v
xc076 ECDHE-RSA-CAMELLIA128-SHA256 ECDH TBD Camellia 128 TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 not a/v
xc072 ECDHE-ECDSA-CAMELLIA128-SHA256 ECDH TBD Camellia 128 TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 not a/v
xbe DHE-RSA-CAMELLIA128-SHA256 DH TBD Camellia 128 TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 not a/v
xbd DHE-DSS-CAMELLIA128-SHA256 DH TBD Camellia 128 TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 not a/v
xbc DH-RSA-CAMELLIA128-SHA256 DH/RSA Camellia 128 TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 not a/v
xbb DH-DSS-CAMELLIA128-SHA256 DH/DSS Camellia 128 TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 not a/v
x45 DHE-RSA-CAMELLIA128-SHA DH 2048 Camellia 128 TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA available
x44 DHE-DSS-CAMELLIA128-SHA DH TBD Camellia 128 TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA not a/v
x43 DH-RSA-CAMELLIA128-SHA DH/RSA Camellia 128 TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA not a/v
x42 DH-DSS-CAMELLIA128-SHA DH/DSS Camellia 128 TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA not a/v
xbf ADH-CAMELLIA128-SHA256 DH TBD Camellia 128 TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 not a/v
x46 ADH-CAMELLIA128-SHA DH TBD Camellia 128 TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA not a/v
xc078 ECDH-RSA-CAMELLIA128-SHA256 ECDH/RSA Camellia 128 TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 not a/v
xc074 ECDH-ECDSA-CAMELLIA128-SHA256 ECDH/ECDSA Camellia 128 TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 not a/v
xba CAMELLIA128-SHA256 RSA Camellia 128 TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 not a/v
x41 CAMELLIA128-SHA RSA Camellia 128 TLS_RSA_WITH_CAMELLIA_128_CBC_SHA not a/v
```
The Postgres protocol uses STARTTLS with a custom start packet. This
functionality is supported by openssl s_client in the current openssl
master branch but not yet in any released version.
This patch detects whether the given openssl binary supports postgres
and runs the default tests against a postgres server.
Example of no openssl support:
~/bin/testssl$ ./testssl.sh --quiet
--openssl=/opt/openssl/openssl-1.1.0c/bin/openssl --starttls=postgres
test.postgres.server.com:5432
Start 2016-12-07 18:03:24 -->> ip.add.re.ss:5432
(test.postgres.server.com:5432) <<--
Fatal error: Your /opt/openssl/openssl-1.1.0c/bin/openssl does not
support the "-starttls postgres" option
Example of openssl support:
~/bin/testssl$ ./testssl.sh --quiet
--openssl=/opt/openssl/openssl-2016-12-07/bin/openssl --startt ls=postgres
test.postgres.server.com:5432
Start 2016-12-07 18:06:03 -->> ip.add.re.ss:5432
(test.postgres.server.com:5432) <<--
Service set: STARTTLS via POSTGRES
Testing protocols (via openssl, SSLv2 via sockets)
SSLv2 not offered (OK)
SSLv3 offered (NOT ok)
TLS 1 offered
TLS 1.1 offered
TLS 1.2 offered (OK)
SPDY/NPN (SPDY is an HTTP protocol and thus not tested here)
HTTP2/ALPN (HTTP/2 is a HTTP protocol and thus not tested
here)
...
This PR implements `run_pfs()` in a manner similar to `run_allciphers()`. It uses OpenSSL followed by `tls_sockets()` to test for both supported PFS cipher suites as well as elliptic curves offered.
I made an attempt at addressing #548 by using different colors to print the different curve names, depending on strength. The colors chosen are exactly the same as those that would be chosen by `read_dhbits_from_file()`:
```
# bits <= 163: pr_svrty_medium
163 < # bits <= 193: pr_svrty_minor
193 < # bits <= 224: out
# bits > 224: pr_done_good
```
I also added code for #464 to create a list of the DH groups from RFC 7919 that a server supports. However, since no servers seem to support this at the moment (except with TLS 1.3), I marked this code to only run if the $EXPERIMENTAL flag is set.
For several elliptic curves the number of bits, as indicated by OpenSSL, is slightly different than the name implies. For example, for sect239k1 OpenSSL outputs: `Server Temp Key: ECDH, sect239k1, 238 bits`.
This PR aligns the output created by `parse_tls_serverhello()` with OpenSSL.
When the cipher-mapping.txt file is read, the contents of the "Mac=..." column is placed in `TLS_CIPHER_EXPORT` rather than the contents of the "export" column. This PR fixes that.
This PR address a problem in `run_drown()` when the server does not support SSLv2, but does support multiple certificates or doesn't have an RSA certificate.
One example of the problem can be seen with www.facebook.com. If `run_server_preferences()` is run before `run_drown()`, then the results of `run_drown()` are:
```
DROWN (2016-0800, CVE-2016-0703) not vulnerable on this port (OK)
make sure you don't use this certificate elsewhere with SSLv2 enabled services
https://censys.io/ipv4?q=A626B154CC65634181250B810B1BD4C89EC277CEA08D785EEBE7E768BDA7BB00 SHA256 A3F474FB17509AE6C5B6BA5E46B79E0DE6AF1BF1EEAA040A6114676E714C9965 could help you to find out
```
If only `run_drown()` is performed, then the result is:
```
DROWN (2016-0800, CVE-2016-0703) not vulnerable on this port (OK)
make sure you don't use this certificate elsewhere with SSLv2 enabled services
https://censys.io/ipv4?q=A626B154CC65634181250B810B1BD4C89EC277CEA08D785EEBE7E768BDA7BB00 could help you to find out
```
However, A626B154CC65634181250B810B1BD4C89EC277CEA08D785EEBE7E768BDA7BB00 is the fingerprint of Facebook's ECDSA certificate, not its RSA certificate.
In addition, as noted in the "FIXME," `run_drown()` will display the warning "make sure you don't use this certificate elsewhere with SSLv2 enabled services" even if the server doesn't have an RSA certificate, even though SSLv2 can only use RSA certificates.
This PR fixes this issue by only showing the warning if the server has an RSA certificate and by ensuring that the `$cert_fingerprint_sha2` used to construct the "https://censys.io/ipv4?q=..." URL only contains a single SHA256 fingerprint and that it is the fingerprint of the server's RSA certificate.
This PR modifies `cipher_pref_check()` to use `tls_sockets()`. As with similar PRs for `run_allciphers()`, `run_cipher_per_proto()`, and `run_rc4()`, it also makes use of `$OPENSSL s_client`, since `$OPENSSL s_client` is faster than `tls_sockets()`.
With this PR, `cipher_pref_check()` first uses `$OPENSSL s_client` to obtain an ordered list of ciphers. It then makes one call to `tls_sockets()` (or a few calls if proto is TLSv1.2 and `$SERVER_SIZE_LIMIT_BUG` is `true`) to find if the server supports any ciphers that are not detected by `$OPENSSL s_client`. If not, then it is done. If it finds one, then it throws out the previous results and starts over with `tls_sockets()`. [If proto is TLSv1.2 and `$SERVER_SIZE_LIMIT_BUG` is `true`, then it doesn't throw out the `$OPENSSL s_client` results. Instead, it continues with `tls_sockets()` to get the full list of supported ciphers, and then uses `tls_sockets()` to order that list.]
The result is that this PR works almost as fast as the current `cipher_pref_check()` if `$OPENSSL s_client` finds all of the supported ciphers, at the cost of a performance penalty when testing servers that support ciphers that would have otherwise been missed using just OpenSSL.
Note that in this PR I removed SSLv2 from the list of protocols tested. This is because https://community.qualys.com/thread/16255 states that "in SSLv2 the client selects the suite to use." It seems that in SSLv2, the client sends a list of ciphers that it supports, the server responds with a list of ciphers that the client and server have in common, and then "the client selects the suite to use." So, showing a cipher order for SSLv2 is a bit misleading.
As noted in #543, this PR does not modify the second part of `cipher_pref_check()`, which deals with NPN protocols.
This PR implements `run_rc4()` in a similar manner to `run_allciphers()` and `run_cipher_per_proto()` (in PR #541). The change doesn't seem to have much of an impact on speed, but when sockets are used it can detect ciphers that aren't locally supported by OpenSSL.
This PR fixes two minor bugs in run_allciphers():
* If `$SSL_NATIVE` or `$FAST` is `true`, then the cipher mapping file will not be used (unless `$OPENSSL ciphers` does not support the `-V` option), so there is no "fallback" to openssl, even if `[[ $TLS_NR_CIPHERS == 0 ]]`.
* If `$using_sockets` is `false` and `$SHOW_EACH_C` is `true`, then `ossl_supported` should be checked to see if the cipher was tested, not `TLS_CIPHER_OSSL_SUPPORTED`.
This function reorganizes `run_server_defaults()` based on the suggestion in #515.
The current `determine_tls_extensions()` is renamed to `get_server_certificate()`, and two changes are made to it:
*it no longer includes an extra call to `$OPENSSL s_client` to check for the ALPN extension; and
* rather than setting `$TLS_EXTENSIONS` to be the extensions found during this call to the function, it adds any newly found extensions to those already in `$TLS_EXTENSIONS`.
The PR then adds a new function, `determine_tls_extensions()`, which borrows some logic from the old `determine_tls_extensions()`, but this new `determine_tls_extensions()` only looks for additional TLS extensions, including ALPN.
`run_server_defaults()` makes multiple calls to `get_server_certificate()` (as it previously did to `determine_tls_extensions()`) in order to collect all of the server's certificates, and then it makes one call to `determine_tls_extensions()`, which checks for support for extensions that were not checked for by `get_server_certificate()` (e.g., ALPN, extended master secret, signed certificate timestamps).
The new `determine_tls_extensions()` will check for most of the extensions that are checked for by
`run_server_defaults()`, including the heartbeat extension, so the call to `determine_tls_extensions()` from `run_heartbleed()` will still work.
> The dh_bits are still not shown, maybe because of #531.
This PR fixes the issue of dh_bits not being shown if the cipher-mapping.txt file is missing. The problem is that the code in `parse_tls_serverhello()` that parses the ServerKeyExchange message assumes that `$rfc_cipher_suite` has the RFC version of the name the cipher suite. However, if the cipher-mapping.txt file is missing, `$rfc_cipher_suite` will have the OpenSSL name of the cipher suite. This PR changes the code to recognize either the RFC or OpenSSL names for ciphers with ephemeral DH or ECDH keys.
When `tls_sockets()` is used with the "full" option and the chosen cipher suite involves an ephemeral finite-field DH key (DH), this PR extracts the public key from the ServerKeyExchange message and adds it to `$TMPFILE`. In addition (and the primary reason for this PR), it compares the ephemeral public key's parameters to those specified in RFC 7919, and indicates whether one the groups from that RFC was used. This will allow `run_pfs()` to be extended to indicate which, if any, RFC 7919 DH groups a server supports.
This PR adds parsing of the CertificateStatus message to `parse_tls_serverhello()`. If the caller requests that the "full" response be parsed, then the CertificateStatus message is parsed, and the OCSP response is added to $TMPFILE, in a manner similar to the output of `$OPENSSL s_client` when the `-status` option is used.
The string "CamelliaGCM" is too long for the "Encryption" column printed by `neat_list()`. So, either "CamelliaGCM" needs to be shortened to "Camellia" (as this PR does), or the "Encryption" column needs to be made wider.
Client simulations can still use sockets even if the cipher mapping file is missing. If the cipher file is present, then `parse_tls_serverhello()` write the RFC name for the cipher and then `run_client_simulation()` converts that to the OpenSSL name (so that the output is the same as if OpenSSL were used). This PR changes `parse_tls_serverhello()` so that it writes the OpenSSL name for the cipher if the mapping file is missing, which `run_client_simulation()` can then just display.
This PR also unsets `ADD_RFC_STR` if the mapping file is missing, so that `neat_list()` won't try to display the RFC names for the ciphers.
This PR speeds up the implementation of `run_allciphers()` by introducing a number of changes:
* Rather than check for implemented ciphers in a hierarchical manner (as introduced in #326), this PR follows the approach of `cipher_pref_check()`. Testing a block of ciphers, marking the selected cipher as implemented, and then testing same block of ciphers, minus those that have previously been selected, until a test fails. Thus the number of calls to `$OPENSSL s_client` is just one more than the number of ciphers implemented. (Since some servers cannot handle ClientHellos with more than 128 messages, the tests are performed on blocks of 128 or few ciphers. So, if OpenSSL supports 197 ciphers, the number of calls to `$OPENSSL s_client` is 2 plus the number of ciphers supported by the server.
* If $using_sockets is true, then OpenSSL is used first to find all supported ciphers that OpenSSL supports (since OpenSSL is faster than `tls_sockets()`), and then `tls_sockets()` is only used to test those cipher suites that were not found to be supported by OpenSSL.
* The `prepare_debug()` function, which reads in `$CIPHERS_BY_STRENGTH_FILE` determines which ciphers are supported by the version of OpenSSL being used. If a version of OpenSSL older than 1.0 is being used, then this is used to determine which ciphers to test using OpenSSL rather than using `$OPENSSL ciphers -V`.
Following the approach of `cipher_pref_check()` reduces the number of queries to the server. Using OpenSSL before `tls_sockets()` reduces the number of calls to `tls_sockets()` to 3 plus the number of ciphers supported by the server that are not supported by OpenSSL, so the cost penalty over just using OpenSSL is fairly small.
The `tls_sockets()` and `sslv2_sockets()` use `get_pub_key_size()` to extract the size of the server's public key if the full response is being processed, and `get_pub_key_size()` uses `$OPENSSL pkey` to extract the server's public key from the certificate. However, OpenSSL 0.9.8 does not support the "pkey" command. This PR changes `get_pub_key_size()` to suppress the error message displayed by OpenSSL when the "pkey" command is not supported.
This PR adds parsing of the Certificate message to `parse_tls_serverhello()`. If the caller requests that the "full" response be parsed, then the Certificate message is parsed, the server's certificate is placed in $HOSTCERT and the intermediate certificates are placed in $TEMPDIR/intermediatecerts.pem. The certificates are also added to $TMPFILE, in a manner similar to the output of `$OPENSSL s_client` when the `-showcerts` option is used.
This PR uses `tls_sockets()` to determine whether a server supports certain extensions that may not be supported by `$OPENSSL`. At the moment it checks for max_fragment_length, client_certificate_url, truncated_hmac, ALPN, signed_certificate_timestamp, encrypt_then_mac, and extended_master_secret.
In https://github.com/dcooper16/testssl.sh/blob/extended_tls_sockets/testssl.sh, `run_server_defaults()` is re-written to use `tls_sockets()` instead of `$OPENSSL`, with just one call to `$OPENSSL s_client` to get the session ticket, which reduces the dependence on `$OPENSSL`, but this PR limits the number of calls to `tls_sockets()`, which is still slow.
Note: I included ALPN in the `tls_sockets()` ClientHello since a single call to `tls_sockets()` cannot test for both NPN and ALPN, and since support for NPN was added to OpenSSL before support for ALPN was added, I figured it was more likely that `determine_tls_extensions()` had already determined whether the server supported NPN.
This PR fixes the same issues as were fixed in PR #513, but also makes two changes to `parse_tls_serverhello()`:
* It changes the number of bits for curve X25519 from 256 to 253 to match OpenSSL.
* It removes the "ECDH, " from the "Server Temp Key: " line in order to match OpenSSL's output.
This PR fixes two issues related to curve X25519.
First, while OpenSSL 1.1.0 supports curve X25519, it is not included in the output of `$OPENSSL ecparam -list_curves`. I tried several versions of OpenSSL (and one version of LibreSSL), and every version output either "Error with command" or "unknown option" in response to `$OPENSSL s_client -curves $curve` if it either did not support the `-curves` option or did not support `$curve`. (When the `-curve` option was supported with `$curve`, a "connect" error was output.)
The second issue is that the "Server Temp Key" line in the output of `s_client` is different for curve X25519. For other elliptic curves, the output is
```
Server Temp Key: ECDH, P-256, 256 bits
```
For X25519 it is:
```
Server Temp Key: X25519, 253 bits
```
So, `read_dhbits_from_file()` needs to allow for `$what_dh` being "X25519" rather than "ECDH" and `run_pfs()` needs to allow for the possibility that the curve name will be the first field rather than the second.
The PR changes `run_allciphers()` to use `tls_sockets()` (and `sslv2_sockets()`)rather than `$OPENSSL` unless `$SSL_NATIVE` is set or `$STARTTLS` is non-empty. Using sockets allows `run_allciphers()` to test all ciphers, rather than just those supported by `$OPENSSL`.
Using sockets results in `run_allciphers()` running more slowly, partially since it is testing more ciphers, but mostly since `tls_sockets()` is currently slower than `$OPENSSL` (as noted in #413).
This PR makes similar changes to `run_client_simulation()` as were made to `tls_sockets()`, so that `run_client_simulation()` retrieves the entire server response, even if it is split across multiple packets, and it has `parse_tls_serverhello()` extract information about the server's ephemeral public key, if present.
The PR also changes `run_client_simulation()` to use information about the ephemeral public key. It includes the length of the public key in the output and, if it is a DH public key, checks that the size is within the acceptable range (`${minDhBits[i]} <= dh_bits <= ${maxDhBits[i]}`).
This PR adds initial parsing of the ServerKeyExchange message to `parse_tls_serverhello()`. For ephemeral DH keys, it extracts the length of the key. For ephemeral ECDH keys that are encoded using the named_curve option, it extracts the length of the key and the name of the curve.
This PR allows the caller to provide additional extensions to `tls_sockets()` to be included in the ClientHello. If the caller provides an extension that would have already been included in the ClientHello, then the caller's value for the extension is used rather than the default value.
This PR extended `parse_tls_serverhello()` in a few ways:
* If the "full" response is to be parsed, then additional checks are performed to verify that `$tls_hello_ascii` contains the entire response
* The extensions field is parsed and the list of extensions found is placed in `$TLS_EXTENSIONS` (if the "full" response is being parsed).
* Initial support for TLS 1.3 is added:
- Accounts for differences between TLS 1.2 ServerHello and TLS 1.3 ServerHello (as outlined in PR #499).
- Recognizes new alerts and handshake message types.
- Allows for server response to include message fragments of type "application data"
I forgot that `parse_tls_serverhello()` is also called by `client_simulation_sockets()`. Since PR #499 changed the input to `parse_tls_serverhello()`, the change needs to be made in `client_simulation_sockets()` as well.
Now that the mapping file is no longer used, `$ADD_RFC_STR` should not be unset just because the mapping file cannot be found.
In addition, since `show_rfc_style()` is now used in `parse_tls_serverhello()`, it cannot return an empty string just because the user set "--mapping no-rfc" on the command line. Instead, `neat_list()` should check the value of `$ADD_RFC_STR` and not call `show_rfc_style()` if it has been unset.
Finally, since `show_rfc_style()` no longer returns strings with extra spaces, there is no need to call `strip_spaces()`
In some cases the server's response to a ClientHello spans more than one packet. If the goal is just to determine whether the connection was successful and to extract a few pieces of information from the ServerHello message, then this is unlikely to be a problem. However, if there is a desire to extract the server's certificate chain (Certificate message) or to determine the type and size of the server's ephemeral public key (ServerKeyExchange message), then the entire response needs to be obtained, even if it spans multiple packets.
This PR adds a new function, `check_tls_serverhellodone()`, that checks whether the entire response has been received (e.g., whether the ServerHelloDone message has been received). If the response indicates that the response is incomplete, then `tls_sockets()` requests more data from the server until the response is complete or until the server doesn't provide any more data in response.
The PR only changes the behavior of `tls_sockets()` if the caller indicates that it wants to extract the ephemeral key or that it wants the entire response to be parsed. Otherwise, only the first packet returned by the server is sent to `parse_tls_serverhello()`. [The value of `$process_full` is not used at the moment, but will be in a subsequent PR that modifies `parse_tls_serverhello()`.]
This PR also changes `tls_sockets()` to send a close_notify to the server if the connection was successfully established.
PR #346 added a test for version tolerance to `run_protocols()`, but I think it may now be more appropriate to remove that test. Draft -16 of TLS 1.3, which was posted on September 22, changed the way that version negotiation is handled for TLS 1.3 and above. The current version tolerance test sends a ClientHello with the version field set to "03, 05", to represent a TLS 1.4 ClientHello. While this was consistent with RFC 5246 and with drafts of TLS 1.3 up to -15, draft -16 changed the version field to `legacy_version` and declared that its value should be "03, 03" for TLS 1.2 and above. (For TLS 1.3 and above a Supported Versions extension is included to inform the server which versions of TLS the client supports.) The change in draft -16 was made as a result of the problems with servers not handling version negotiation correctly.
Since the current draft suggests that a server should never be presented with a ClientHello with a version higher than "03, 03" (even for clients that support TLS versions higher than 1.2), it seems there is no reason to include the version tolerance test anymore.
For servers that do not support TLS 1.2, the additional checks that were added by PR #346 will already detect if the server cannot perform version negotiation correctly.
This PR adds the option for `parse_sslv2_serverhello()` to extract information from the ServerHello (server key size and cipher suites supported) and write the information to `$TMPFILE` as well as to write the server's certificate to `$HOSTCERT`.
The mapping file is now only used in `show_rfc_style()`. This PR changes `show_rfc_style()` to use the `$TLS_CIPHER_HEXCODE` and `$TLS_CIPHER_RFC_NAME` arrays.
Note that `get_install_dir()` still searches for the mapping-rfc.txt in order to determine `$INSTALL_DIR`. `$INSTALL_DIR` is only used to determine the location of the CA bundles in `determine_trust()`:
```
local ca_bundles="$INSTALL_DIR/etc/*.pem"
```
This PR changes `sslv2_sockets()` so that a list of ciphers may optionally be passed as an argument. This will support the use of `sslv2_sockets()` in some places where `$OPENSSL s_client` is currently used.
s_client's manpage states for -nextprotoneg:
"Empty list of protocols is treated specially and will cause the client
to advertise support for the TLS extension but disconnect just after
reciving ServerHello with a list of server supported protocols."
Consequently, the previous workaround of just quoting an empty variable
is insufficient and the "-nextprotoneg" parameter has to be removed
entirely from the command-line in case of an empty argument.
In other locations where "-nextprotoneg" is used
- its argument cannot be empty ($NPN_PROTOs is initialized to a non-
empty value and set read-only) or
- its argument is intended to be empty (line 3724) or
- the command will not be invoked at all (for-loop parameter, line 3725)
This fixes#467 - again.
Additionally this patch prefers usage of -alpn over -nextprotoneg if the
openssl binary used supports it.
Refactor the while loop so it doesn't use a subshell anymore. Also use
"read -r" to prevent backslash escaping.
```
In testssl.sh line 1193:
app_banners="$app_bannersline"
^-- SC2030: Modification of app_banners is local (to subshell caused by pipeline).
In testssl.sh line 1195:
fileout "app_banner" "WARN" "Application Banners found: $app_banners"
^-- SC2031: app_banners was modified in a subshell. That change might be lost.
```
Found by ShellCheck.
This commit fixes the following two instances of referenced but not assigned
variables:
```
In testssl.sh line 1159:
rp_banners="$rp_bannersline"
^-- SC2154: rp_bannersline is referenced but not assigned.
In testssl.sh line 1193:
app_banners="$app_bannersline"
^-- SC2154: app_bannersline is referenced but not assigned.
```
Found by ShellCheck.
The argument to -nextprotoneg is provided in sometimes empty an unquoted
variables. Because of the missing quotes, the next word on the line "-status"
gets parsed as "-nextprotoneg"'s argument instead of enabling the OCSP status
check.
This fixes#467.
Fix referenced but not assigned variable 'sign_algo'.
In testssl.sh line 4309:
fileout "${json_prefix}algorithm" "DEBUG" "Signature Algorithm: $sign_algo"
^-- SC2154: sign_algo is referenced but not assigned.
Found by ShellCheck.
Two instances of referenced but not assigned variables ('req' instead of
'ret').
In testssl.sh line 4130:
if [[ $req -eq 0 ]]; then
^-- SC2154: req is referenced but not assigned.
Found by ShellCheck.
Changed `Enc=CHACHA20/POLY1305(256)` to `Enc=ChaCha20(256)` and `Enc=GOST-28178-89-CNT(256)` to `Enc=GOST(256)` in order to shorten the names that are printed, so that they fit in the allocated column.
Added the four experimental post-quantum cipher suites mentioned in #462.
`socksend_tls_clienthello()` always includes a server name extension in the ClientHello (for TLS 1.0 and above), even if `$SNI` is empty. If `$NODE` is an IP address, then the IP address is placed in the extension, even though RFC 6066 says that only DNS names are supported in the extension.
This PR changes `socksend_tls_clienthello()` so that the server name extension is only included in the ClientHello is `$SNI` is not empty.
This PR is an attempt to address issue #447. If more than one certificate is being displayed, then a parenthetical saying "(in response to request w/o SNI)" is added for any certificate that was obtained using `$SNI=""`.
In addition, if the certificate was obtained without SNI, then `certificate_info()` doesn't call `$OPENSSL s_client` in order to obtain the non-SNI host certificate and it does not display a separate "Trust (hostname)" finding for the non-SNI certificate.
When `certificate_info()` is given a certificate with a DH public key it displays something like:
```
Server key size fixme: dhKeyAgreement 3072 bits (FIXME: can't tell whether this is good or not)
```
This PR fixes that so that the output is:
```
Server key size DH 3072 bits
```
This PR is in response to issue #454. I tried repeating the reported problem by creating a certificate in which the extendedKeyUsage extension was present and only included the anyExtendedKeyUsage OID. In running the test, I discovered two problems.
First, when `determine_trust()` is calling `verify_retcode_helper()` to display the reason that path validation failed, it assumes that there are at least two certificate bundles provided. (I was running the test using just one certificate bundle, containing my local root.) So, I changed `determine_trust()` to use `${verify_retcode[1]}` rather than `${verify_retcode[2]}` in the case that all bundles failed (it seems that 2 vs. 1 was an arbitrary choice).
Once that was fixed, testssl.sh output "NOT ok (unknown, pls report) 26". So, the second thing this PR fixes is to output "NOT ok (unsupported certificate purpose)" if OpenSSL responds with an unsupported certificate purpose error.
With OpenSSL 1.1.0, `s_client -no_ssl2` fails with an "unknown option" error. At the moment the `-no_ssl2` option is only used in two functions, `run_client_simulation()` and `run_crime()`. In `run_crime()`, the `-no_ssl2` option is only included if the OpenSSL version is 0.9.8.
This PR checks whether the OpenSSL version in use supports the `-no_ssl2` option, and if it doesn't, it removes it from the calls to `s_client` in `run_client_simulation()`.
If the version of OpenSSL being used doesn't support `s_client -ssl3` (e.g., OpenSSL 1.1.0), `run_beast()` doesn't display a warning that testing for CBC in SSLv3 isn't locally supported.
This PR adds a "Local problem" warning if the OpenSSL being used doesn't support `s_client -ssl3`.
The test for whether a server only supports SSLv2 was broken, since `$OPTIMAL_PROTO` will be `-ssl2` whether SSLv2 is the only protocol that succeeds or no protocol succeeds.
This PR sets $OPTIMAL_PROTO (or $STARTTLS_OPTIMAL_PROTO) to "" if no protocol succeeds.
If the version of OpenSSL being used doesn't support `s_client -ssl3` (e.g., OpenSSL 1.1.0), `run_ssl_poodle()` displays `not vulnerable (OK)` even though it can't test whether the server is vulnerable.
This PR fixes it so that a "Local problem" warning is displayed is `s_client -ssl3` isn't supported.
The PR also removes the `$SNI` from the call to `$OPENSSL s_client` since OpenSSL ignores the `-servername` directive for `-ssl3` anyways.
If testssl.sh is called with `--devel 22` and the response from `sslv2_sockets()` is not 0, then `tls_sockets()` will be called, and the result of the `tls_sockets()` command will be output rather than the result of the `sslv2_sockets()` command.
This PR addresses the "FIXME" in `run_protocols()`:
```
sslv2_sockets #FIXME: messages/output need to be moved to this (higher) level
```
It also changes `run_drown()` to call `sslv2_sockets()` in order to avoid duplicate code.
This PR is in response to issue #352, where it was noted that Bash does not support binary data in strings.
I replaced all calls to `sockread()` with calls to `sockread_serverhello()`, and then, since is now used everywhere and not just to read ServerHello messages, I renamed `sockread_serverhello()` to `sockread()`.
I tested the revised code against several servers, including one that is vulnerable to CCS and Heartbleed, and got the same results as with the current code (although the hexdumps displayed in debug mode differ).
One concern I have is the code in `run_ccs_injection()`. The current code is:
```
byte6=$(echo "$SOCKREPLY" | "${HEXDUMPPLAIN[@]}" | sed 's/^..........//')
lines=$(echo "$SOCKREPLY" | "${HEXDUMP[@]}" | count_lines )
debugme echo "lines: $lines, byte6: $byte6"
if [[ "$byte6" == "0a" ]] || [[ "$lines" -gt 1 ]]; then
pr_done_best "not vulnerable (OK)"
...
```
I revised this to:
```
if [[ -s "$SOCK_REPLY_FILE" ]]; then
byte6=$(hexdump -ve '1/1 "%.2x"' "$SOCK_REPLY_FILE" | sed 's/^..........//')
lines=$(hexdump -ve '16/1 "%02x " " \n"' "$SOCK_REPLY_FILE" | count_lines )
debugme echo "lines: $lines, byte6: $byte6"
fi
rm "$SOCK_REPLY_FILE"
if [[ "$byte6" == "0a" ]] || [[ "$lines" -gt 1 ]]; then
...
```
In the revised code `byte6` is initialized to `0a` so that the response is `not vulnerable (OK)` if `$SOCK_REPLY_FILE` is empty. This has worked okay since for all of the servers that I tested that weren't vulnerable `$SOCK_REPLY_FILE` was empty. Since I haven't seen any other examples, I don't understand why check for vulnerability was written the way it was. So, I'm a bit concerned that the test in the revised code may produce incorrect results now that `hexdump -ve '1/1 "%.2x"' "$SOCK_REPLY_FILE"` is an accurate hexdump of the reply.