Merge pull request #2822 from testssl/quic

First try for QUIC (OpenSSL only and only checking the protocol)
This commit is contained in:
Dirk Wetter
2025-07-05 13:24:25 +02:00
committed by GitHub
9 changed files with 2760 additions and 1263 deletions

View File

@ -12,5 +12,5 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: codespell-project/actions-codespell@master - uses: codespell-project/actions-codespell@master
with: with:
skip: ca_hashes.txt,tls_data.txt,*.pem,OPENSSL-LICENSE.txt,CREDITS.md,openssl.cnf,fedora-dirk-ipv6.diff skip: ca_hashes.txt,tls_data.txt,*.pem,OPENSSL-LICENSE.txt,CREDITS.md,openssl.cnf,fedora-dirk-ipv6.diff,testssl.1
ignore_words_list: borken,gost,ciph,ba,bloc,isnt,chello,fo,alle,anull ignore_words_list: borken,gost,ciph,ba,bloc,isnt,chello,fo,alle,anull

View File

@ -1,6 +1,10 @@
## Change Log ## Change Log
### Features implemented / improvements in 3.3dev
* QUIC protocol check
### Features implemented / improvements in 3.2 ### Features implemented / improvements in 3.2
* Rating (SSL Labs) * Rating (SSL Labs)

View File

@ -5,7 +5,7 @@ Contributing / participating is always welcome!
Please note the following: Please note the following:
* Please read the [coding convention](https://github.com/testssl/testssl.sh/blob/3.2/Coding_Convention.md). * Please read the [coding convention](https://github.com/testssl/testssl.sh/blob/3.3dev/Coding_Convention.md).
* If you have something new and/or bigger which you like to contribute, better open an issue first before you get frustrated. * If you have something new and/or bigger which you like to contribute, better open an issue first before you get frustrated.
* Please one pull request per feature or bug fix or improvement. Please do not mix issues. * Please one pull request per feature or bug fix or improvement. Please do not mix issues.
* Documentation pays off in the long run. So please your document your code and the pull request and/or commit message. * Documentation pays off in the long run. So please your document your code and the pull request and/or commit message.

View File

@ -15,3 +15,5 @@ $(NAME).$(MANSECTION): $(NAME).$(MANSECTION).md
$(NAME).$(MANSECTION).html: template.html $(NAME).$(MANSECTION).md $(NAME).$(MANSECTION).html: template.html $(NAME).$(MANSECTION).md
$(PANDOC) --standalone --to html5 --template template.html --metadata title="$(TITLE)" $(NAME).$(MANSECTION).md -o $@ $(PANDOC) --standalone --to html5 --template template.html --metadata title="$(TITLE)" $(NAME).$(MANSECTION).md -o $@
# Source is Markdown

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -175,7 +175,7 @@ Any single check switch supplied as an argument prevents testssl.sh from doing a
`-f, --fs, --nsa, --forward-secrecy` Checks robust forward secrecy key exchange. "Robust" means that ciphers having intrinsic severe weaknesses like Null Authentication or Encryption, 3DES and RC4 won't be considered here. There shouldn't be the wrong impression that a secure key exchange has been taking place and everything is fine when in reality the encryption sucks. Also this section lists the available elliptical curves and Diffie Hellman groups, as well as FFDHE groups (TLS 1.2 and TLS 1.3). `-f, --fs, --nsa, --forward-secrecy` Checks robust forward secrecy key exchange. "Robust" means that ciphers having intrinsic severe weaknesses like Null Authentication or Encryption, 3DES and RC4 won't be considered here. There shouldn't be the wrong impression that a secure key exchange has been taking place and everything is fine when in reality the encryption sucks. Also this section lists the available elliptical curves and Diffie Hellman groups, as well as FFDHE groups (TLS 1.2 and TLS 1.3).
`-p, --protocols` checks TLS/SSL protocols SSLv2, SSLv3, TLS 1.0 through TLS 1.3 and for HTTP: SPDY (NPN) and ALPN, a.k.a. HTTP/2. For TLS 1.3 several drafts (from 18 on) and final are supported and being tested for. Note the supplied openssl-bad version doesn't support TLS 1.3 . As the check for TLS 1.3 will be done in sockets this normally does not pose a problem. However if a TLS-1.3-only host is encountered and to have a complete test coverage (e.g. header checks) `/usr/bin/openssl` (or the content of `OPENSSL2`) is checked for existence and support of TLS 1.3 and if those tests succeeded it will be switched to this binary. A message will notify you. `-p, --protocols` checks TLS/SSL protocols SSLv2, SSLv3, TLS 1.0 through TLS 1.3. And for HTTP also QUIC (HTTP/3), SPDY (NPN) and ALPN (HTTP/2). For TLS 1.3 the final version and several drafts (from 18 on) are tested. QUIC needs OpenSSL >= 3.2 which can be automatically picked up when in `/usr/bin/openssl` (or when defined environment variable OPENSSL2). If a TLS-1.3-only host is encountered and the openssl-bad version is used testssl.sh will e.g. for HTTP header checks switch to `/usr/bin/openssl` (or when defined via ENV to OPENSSL2). Also this will be tried for the QUIC check.
`-P, --server-preference, --preference` displays the servers preferences: cipher order, with used openssl client: negotiated protocol and cipher. If there's a cipher order enforced by the server it displays it for each protocol (openssl+sockets). If there's not, it displays instead which ciphers from the server were picked with each protocol. `-P, --server-preference, --preference` displays the servers preferences: cipher order, with used openssl client: negotiated protocol and cipher. If there's a cipher order enforced by the server it displays it for each protocol (openssl+sockets). If there's not, it displays instead which ciphers from the server were picked with each protocol.
@ -520,6 +520,7 @@ Please note that for plain TLS-encrypted ports you must not specify the protocol
* RFC 8143: Using Transport Layer Security (TLS) with Network News Transfer Protocol (NNTP) * RFC 8143: Using Transport Layer Security (TLS) with Network News Transfer Protocol (NNTP)
* RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3 * RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3
* RFC 8701: Applying Generate Random Extensions And Sustain Extensibility (GREASE) to TLS Extensibility * RFC 8701: Applying Generate Random Extensions And Sustain Extensibility (GREASE) to TLS Extensibility
* RFC 9000: QUIC: A UDP-Based Multiplexed and Secure Transport
* W3C CSP: Content Security Policy Level 1-3 * W3C CSP: Content Security Policy Level 1-3
* TLSWG Draft: The Transport Layer Security (TLS) Protocol Version 1.3 * TLSWG Draft: The Transport Layer Security (TLS) Protocol Version 1.3

View File

@ -8,6 +8,7 @@
"TLS1_1","testssl.sh/81.169.166.184","443","LOW","offered (deprecated)","","" "TLS1_1","testssl.sh/81.169.166.184","443","LOW","offered (deprecated)","",""
"TLS1_2","testssl.sh/81.169.166.184","443","OK","offered","","" "TLS1_2","testssl.sh/81.169.166.184","443","OK","offered","",""
"TLS1_3","testssl.sh/81.169.166.184","443","OK","offered with final","","" "TLS1_3","testssl.sh/81.169.166.184","443","OK","offered with final","",""
"QUIC","testssl.sh/81.169.166.184","443","WARN","not tested due to lack of local OpenSSL support","",""
"NPN","testssl.sh/81.169.166.184","443","INFO","offered with h2, http/1.1 (advertised)","","" "NPN","testssl.sh/81.169.166.184","443","INFO","offered with h2, http/1.1 (advertised)","",""
"ALPN_HTTP2","testssl.sh/81.169.166.184","443","OK","h2","","" "ALPN_HTTP2","testssl.sh/81.169.166.184","443","OK","h2","",""
"ALPN","testssl.sh/81.169.166.184","443","INFO","http/1.1","","" "ALPN","testssl.sh/81.169.166.184","443","INFO","http/1.1","",""

View File

@ -122,7 +122,7 @@ trap "child_error" USR1
########### Internal definitions ########### Internal definitions
# #
declare -r VERSION="3.2.1" declare -r VERSION="3.3dev"
declare -r SWCONTACT="dirk aet testssl dot sh" declare -r SWCONTACT="dirk aet testssl dot sh"
[[ "$VERSION" =~ dev|rc|beta ]] && \ [[ "$VERSION" =~ dev|rc|beta ]] && \
SWURL="https://testssl.sh/dev/" || SWURL="https://testssl.sh/dev/" ||
@ -205,6 +205,7 @@ MAX_OSSL_FAIL=${MAX_OSSL_FAIL:-2} # If this many failures for s_client con
MAX_STARTTLS_FAIL=${MAX_STARTTLS_FAIL:-2} # max number of STARTTLS handshake failures in plaintext phase MAX_STARTTLS_FAIL=${MAX_STARTTLS_FAIL:-2} # max number of STARTTLS handshake failures in plaintext phase
MAX_HEADER_FAIL=${MAX_HEADER_FAIL:-2} # If this many failures for HTTP GET are encountered we don't try again to get the header MAX_HEADER_FAIL=${MAX_HEADER_FAIL:-2} # If this many failures for HTTP GET are encountered we don't try again to get the header
MAX_WAITSOCK=${MAX_WAITSOCK:-10} # waiting at max 10 seconds for socket reply. There shouldn't be any reason to change this. MAX_WAITSOCK=${MAX_WAITSOCK:-10} # waiting at max 10 seconds for socket reply. There shouldn't be any reason to change this.
QUIC_WAIT=${QUIC_WAIT:-3} # QUIC is UDP. Thus we run the connect in the background. This is how long to wait
CCS_MAX_WAITSOCK=${CCS_MAX_WAITSOCK:-5} # for the two CCS payload (each). There shouldn't be any reason to change this. CCS_MAX_WAITSOCK=${CCS_MAX_WAITSOCK:-5} # for the two CCS payload (each). There shouldn't be any reason to change this.
HEARTBLEED_MAX_WAITSOCK=${HEARTBLEED_MAX_WAITSOCK:-8} # for the heartbleed payload. There shouldn't be any reason to change this. HEARTBLEED_MAX_WAITSOCK=${HEARTBLEED_MAX_WAITSOCK:-8} # for the heartbleed payload. There shouldn't be any reason to change this.
STARTTLS_SLEEP=${STARTTLS_SLEEP:-10} # max time wait on a socket for STARTTLS. MySQL has a fixed value of 1 which can't be overwritten (#914) STARTTLS_SLEEP=${STARTTLS_SLEEP:-10} # max time wait on a socket for STARTTLS. MySQL has a fixed value of 1 which can't be overwritten (#914)
@ -339,6 +340,8 @@ HAS_TLS1=false
HAS_TLS11=false HAS_TLS11=false
HAS_TLS12=false HAS_TLS12=false
HAS_TLS13=false HAS_TLS13=false
HAS_QUIC=false
HAS2_QUIC=false # for automagically determined second OPENSSL version
HAS_X448=false HAS_X448=false
HAS_X25519=false HAS_X25519=false
HAS_SIGALGS=false HAS_SIGALGS=false
@ -367,7 +370,7 @@ HAS_AES128_GCM=false
HAS_AES256_GCM=false HAS_AES256_GCM=false
HAS_ZLIB=false HAS_ZLIB=false
HAS_UDS=false HAS_UDS=false
HAS_UDS2=false HAS2_UDS=false
HAS_ENABLE_PHA=false HAS_ENABLE_PHA=false
HAS_DIG=false HAS_DIG=false
HAS_DIG_R=true HAS_DIG_R=true
@ -5468,6 +5471,7 @@ add_proto_offered() {
# arg1: protocol string or hex code for TLS protocol # arg1: protocol string or hex code for TLS protocol
# echos: 0 if proto known being offered, 1: known not being offered, 2: we don't know yet whether proto is being offered # echos: 0 if proto known being offered, 1: known not being offered, 2: we don't know yet whether proto is being offered
# return value is always zero # return value is always zero
#
has_server_protocol() { has_server_protocol() {
local proto local proto
local proto_val_pair local proto_val_pair
@ -5502,6 +5506,7 @@ has_server_protocol() {
# the protocol check needs to be revamped. It sucks, see above # the protocol check needs to be revamped. It sucks, see above
#
run_protocols() { run_protocols() {
local using_sockets=true local using_sockets=true
local supported_no_ciph1="supported but couldn't detect a cipher (may need debugging)" local supported_no_ciph1="supported but couldn't detect a cipher (may need debugging)"
@ -6125,10 +6130,59 @@ run_protocols() {
[[ $? -ne 0 ]] && exit $ERR_CLUELESS [[ $? -ne 0 ]] && exit $ERR_CLUELESS
fi fi
sub_quic
return $ret return $ret
} }
# We do QUIC check first purely via OpenSSL, supposed it is supported by openssl
#
sub_quic() {
local alpn=""
local use_openssl=""
local proxy_hint_str=""
local sclient_outfile="$TEMPDIR/$NODEIP.quic_connect.txt"
local sclient_errfile="$TEMPDIR/$NODEIP.quic_connect_err.txt"
local jsonID="QUIC"
[[ $DEBUG -ne 0 ]] && sclient_errfile=/dev/null
pr_bold " QUIC ";
if "$HAS2_QUIC" || "$HAS_QUIC"; then
# Proxying QUIC is not supported
# The s_client call would block if either the remote side doesn't support QUIC or outbound traffic is blocked
if "$HAS2_QUIC"; then
use_openssl="$OPENSSL2"
else
use_openssl="$OPENSSL"
fi
OPENSSL_CONF='' $use_openssl s_client -quic -alpn h3 -connect $NODEIP:$PORT -servername $NODE </dev/null \
2>$sclient_errfile >$sclient_outfile &
wait_kill $! $QUIC_WAIT
if [[ $? -ne 0 ]]; then
if [[ -n "$PROXY" ]]; then
proxy_hint_str="(tried directly, is not proxyable):"
fi
outln "$proxy_hint_str not offered or timed out"
fileout "$jsonID" "INFO" "$proxy_hint_str not offered"
else
pr_svrty_best "offered (OK)"
fileout "$jsonID" "OK" "offered"
alpn="$(awk -F':' '/^ALPN protocol/ { print $2 }' < $sclient_outfile)"
alpn="$(strip_spaces $alpn)"
outln ": $(awk '/^Protocol:/ { print $2 }' < $sclient_outfile) ($alpn)"
fi
else
prln_local_problem "No OpenSSL QUIC support"
fileout "$jsonID" "WARN" "not tested due to lack of local OpenSSL support"
fi
return 0
}
# list ciphers (and makes sure you have them locally configured) # list ciphers (and makes sure you have them locally configured)
# arg[1]: non-TLSv1.3 cipher list (or anything else) # arg[1]: non-TLSv1.3 cipher list (or anything else)
# arg[2]: TLSv1.3 cipher list # arg[2]: TLSv1.3 cipher list
@ -19900,7 +19954,7 @@ run_starttls_injection() {
outln "Need socat for this check" outln "Need socat for this check"
return 1 return 1
fi fi
if ! "$HAS_UDS2" && ! "$HAS_UDS"; then if ! "$HAS2_UDS" && ! "$HAS_UDS"; then
fileout "$jsonID" "WARN" "Need OpenSSL with Unix-domain socket s_client support for this check" "$cve" "$cwe" "$hint" fileout "$jsonID" "WARN" "Need OpenSSL with Unix-domain socket s_client support for this check" "$cve" "$cwe" "$hint"
outln "Need an OpenSSL with Unix-domain socket s_client support for this check" outln "Need an OpenSSL with Unix-domain socket s_client support for this check"
return 1 return 1
@ -19926,7 +19980,7 @@ run_starttls_injection() {
if "$HAS_UDS"; then if "$HAS_UDS"; then
openssl_bin="$OPENSSL" openssl_bin="$OPENSSL"
elif "$HAS_UDS2"; then elif "$HAS2_UDS"; then
openssl_bin="$OPENSSL2" openssl_bin="$OPENSSL2"
fi fi
# normally the interesting fallback we grep later for is in fd2 but we'll catch also stdout here # normally the interesting fallback we grep later for is in fd2 but we'll catch also stdout here
@ -20684,7 +20738,7 @@ find_openssl_binary() {
local s_client_has=$TEMPDIR/s_client_has.txt local s_client_has=$TEMPDIR/s_client_has.txt
local s_client_has2=$TEMPDIR/s_client_has2.txt local s_client_has2=$TEMPDIR/s_client_has2.txt
local s_client_starttls_has=$TEMPDIR/s_client_starttls_has.txt local s_client_starttls_has=$TEMPDIR/s_client_starttls_has.txt
local s_client_starttls_has2=$TEMPDIR/s_client_starttls_has2 local s_client2_starttls_has=$TEMPDIR/s_client2_starttls_has
local openssl_location="" cwd="" local openssl_location="" cwd=""
local curve="" ossl_tls13_supported_curves local curve="" ossl_tls13_supported_curves
local ossl_line1="" yr="" local ossl_line1="" yr=""
@ -20831,7 +20885,7 @@ find_openssl_binary() {
HAS_AES256_GCM=false HAS_AES256_GCM=false
HAS_ZLIB=false HAS_ZLIB=false
HAS_UDS=false HAS_UDS=false
HAS_UDS2=false HAS2_UDS=false
TRUSTED1ST="" TRUSTED1ST=""
HAS_ENABLE_PHA=false HAS_ENABLE_PHA=false
@ -20868,16 +20922,20 @@ find_openssl_binary() {
$OPENSSL s_client -tls1_3 -sigalgs PSS+SHA256:PSS+SHA384 $NXCONNECT </dev/null 2>&1 | grep -aiq "unknown option" || HAS_SIGALGS=true $OPENSSL s_client -tls1_3 -sigalgs PSS+SHA256:PSS+SHA384 $NXCONNECT </dev/null 2>&1 | grep -aiq "unknown option" || HAS_SIGALGS=true
fi fi
if [[ -x $OPENSSL2 ]] && OPENSSL_CONF='' $OPENSSL2 s_client -quic 2>&1 | grep -qi 'QUIC requires ALPN'; then
HAS2_QUIC="true"
elif OPENSSL_CONF='' $OPENSSL s_client -quic 2>&1 | grep -qi 'QUIC requires ALPN'; then
HAS_QUIC="true"
fi
$OPENSSL s_client -noservername </dev/null 2>&1 | grep -aiq "unknown option" || HAS_NOSERVERNAME=true $OPENSSL s_client -noservername </dev/null 2>&1 | grep -aiq "unknown option" || HAS_NOSERVERNAME=true
$OPENSSL s_client -ciphersuites </dev/null 2>&1 | grep -aiq "unknown option" || HAS_CIPHERSUITES=true $OPENSSL s_client -ciphersuites </dev/null 2>&1 | grep -aiq "unknown option" || HAS_CIPHERSUITES=true
$OPENSSL ciphers @SECLEVEL=0:ALL > /dev/null 2> /dev/null && HAS_SECLEVEL=true
$OPENSSL s_client -comp </dev/null 2>&1 | grep -aiq "unknown option" || HAS_COMP=true $OPENSSL s_client -comp </dev/null 2>&1 | grep -aiq "unknown option" || HAS_COMP=true
$OPENSSL s_client -no_comp </dev/null 2>&1 | grep -aiq "unknown option" || HAS_NO_COMP=true $OPENSSL s_client -no_comp </dev/null 2>&1 | grep -aiq "unknown option" || HAS_NO_COMP=true
OPENSSL_NR_CIPHERS=$(count_ciphers "$(actually_supported_osslciphers 'ALL:COMPLEMENTOFALL' 'ALL')") $OPENSSL ciphers @SECLEVEL=0:ALL > /dev/null 2> /dev/null && HAS_SECLEVEL=true
OPENSSL_NR_CIPHERS=$(count_ciphers "$(actually_supported_osslciphers 'ALL:COMPLEMENTOFALL' 'ALL')")
if [[ $OPENSSL_NR_CIPHERS -le 140 ]]; then if [[ $OPENSSL_NR_CIPHERS -le 140 ]]; then
[[ "$OSSL_NAME" =~ LibreSSL ]] && [[ ${OSSL_VER//./} -ge 210 ]] && HAS_DH_BITS=true [[ "$OSSL_NAME" =~ LibreSSL ]] && [[ ${OSSL_VER//./} -ge 210 ]] && HAS_DH_BITS=true
if "$SSL_NATIVE"; then if "$SSL_NATIVE"; then
@ -20981,9 +21039,9 @@ find_openssl_binary() {
# We also check, whether there's $OPENSSL2 which has TLS 1.3 # We also check, whether there's $OPENSSL2 which has TLS 1.3
if [[ ! "$OSSL_NAME" =~ LibreSSL ]] && [[ ! $OSSL_VER =~ 1.1.1 ]] && [[ $OSSL_VER_MAJOR -lt 3 ]]; then if [[ ! "$OSSL_NAME" =~ LibreSSL ]] && [[ ! $OSSL_VER =~ 1.1.1 ]] && [[ $OSSL_VER_MAJOR -lt 3 ]]; then
OPENSSL_CONF='' $OPENSSL2 s_client -help 2>$s_client_has2 OPENSSL_CONF='' $OPENSSL2 s_client -help 2>$s_client_has2
OPENSSL_CONF='' $OPENSSL2 s_client -starttls foo 2>$s_client_starttls_has2 OPENSSL_CONF='' $OPENSSL2 s_client -starttls foo 2>$s_client2_starttls_has
grep -q 'Unix-domain socket' $s_client_has2 && HAS_UDS2=true grep -q 'Unix-domain socket' $s_client_has2 && HAS2_UDS=true
grep -q 'xmpp-server' $s_client_starttls_has2 && HAS_XMPP_SERVER2=true grep -q 'xmpp-server' $s_client2_starttls_has && HAS_XMPP_SERVER2=true
# Likely we don't need the following second check here, see 6 lines above # Likely we don't need the following second check here, see 6 lines above
if grep -wq 'tls1_3' $s_client_has2; then if grep -wq 'tls1_3' $s_client_has2; then
OPENSSL_CONF='' OPENSSL2_HAS_TLS_1_3=true OPENSSL_CONF='' OPENSSL2_HAS_TLS_1_3=true
@ -21179,7 +21237,7 @@ single check as <options> ("$PROG_NAME URI" does everything except -E and -g):
-E, --cipher-per-proto checks those per protocol -E, --cipher-per-proto checks those per protocol
-s, --std, --categories tests standard cipher categories by strength -s, --std, --categories tests standard cipher categories by strength
-f, --fs, --forward-secrecy checks forward secrecy settings -f, --fs, --forward-secrecy checks forward secrecy settings
-p, --protocols checks TLS/SSL protocols (including SPDY/HTTP2) -p, --protocols checks TLS/SSL protocols, for HTTP: including QUIC/HTTP/3 and ALPN/HTTP2 (and SPDY)
-g, --grease tests several server implementation bugs like GREASE and size limitations -g, --grease tests several server implementation bugs like GREASE and size limitations
-S, --server-defaults displays the server's default picks and certificate info -S, --server-defaults displays the server's default picks and certificate info
-P, --server-preference displays the server's picks: protocol+cipher -P, --server-preference displays the server's picks: protocol+cipher
@ -21339,6 +21397,8 @@ HAS_TLS1: $HAS_TLS1
HAS_TLS11: $HAS_TLS11 HAS_TLS11: $HAS_TLS11
HAS_TLS12: $HAS_TLS12 HAS_TLS12: $HAS_TLS12
HAS_TLS13: $HAS_TLS13 HAS_TLS13: $HAS_TLS13
HAS_QUIC: $HAS_QUIC
HAS2_QUIC: $HAS2_QUIC
HAS_X448: $HAS_X448 HAS_X448: $HAS_X448
HAS_X25519: $HAS_X25519 HAS_X25519: $HAS_X25519
HAS_SIGALGS: $HAS_SIGALGS HAS_SIGALGS: $HAS_SIGALGS
@ -21363,7 +21423,7 @@ HAS_SIEVE: $HAS_SIEVE
HAS_NNTP: $HAS_NNTP HAS_NNTP: $HAS_NNTP
HAS_IRC: $HAS_IRC HAS_IRC: $HAS_IRC
HAS_UDS: $HAS_UDS HAS_UDS: $HAS_UDS
HAS_UDS2: $HAS_UDS2 HAS2_UDS: $HAS2_UDS
HAS_ENABLE_PHA: $HAS_ENABLE_PHA HAS_ENABLE_PHA: $HAS_ENABLE_PHA
HAS_DIG: $HAS_DIG HAS_DIG: $HAS_DIG