diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b2a34b..31f0325 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ * Several display/output fixes * Security fix: DNS input * Don't use external pwd anymore -* Rating (via SSL Labs) +* Rating (SSL Labs, not complete) ### Features implemented / improvements in 3.0 diff --git a/Readme.md b/Readme.md index 7454f79..071828e 100644 --- a/Readme.md +++ b/Readme.md @@ -30,9 +30,12 @@ cryptographic flaws. ### License This software is free. You can use it under the terms of GPLv2, see LICENSE. -In addition starting from version 3.0rc1 if you're offering a scanner based on testssl.sh -as a public and / or paid service in the internet you need to mention to your audience that you're using -this program and where to get this program from. + +Attribution is important for the future of this project -- also in the +internet. Thus if you're offering a scanner based on testssl.sh as a public and/or +paid service in the internet you are strongly encouraged to mention to your audience +that you're using this program and where to get this program from. That helps us +to get bugfixes, other feedback and more contributions. ### Compatibility diff --git a/doc/testssl.1 b/doc/testssl.1 index a8e8626..eb98d31 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "TESTSSL" "1" "January 2020" "" "" +.TH "TESTSSL" "1" "May 2020" "" "" . .SH "NAME" \fBtestssl\fR @@ -43,13 +43,13 @@ Any OpenSSL or LibreSSL version is needed as a helper\. Unlike previous versions 1) SSL/TLS protocol check . .P -2) standard cipher categories to give you upfront an idea for the ciphers supported +2) standard cipher categories . .P -3) checks forward secrecy: ciphers and elliptical curves +3) server\'s cipher preferences (server order?) . .P -4) server preferences (server order) +4) forward secrecy: ciphers and elliptical curves . .P 5) server defaults (certificate info, TLS extensions, session information) @@ -64,7 +64,10 @@ Any OpenSSL or LibreSSL version is needed as a helper\. Unlike previous versions 8) testing each of 370 preconfigured ciphers . .P -9) client simulation +8) client simulation +. +.P +9) rating . .SH "OPTIONS AND PARAMETERS" Options are either short or long options\. Any long or short option requiring a value can be called with or without an equal sign\. E\.g\. \fBtestssl\.sh \-t=smtp \-\-wide \-\-openssl=/usr/bin/openssl <URI>\fR (short options with equal sign) is equivalent to \fBtestssl\.sh \-\-starttls smtp \-\-wide \-\-openssl /usr/bin/openssl <URI>\fR (long option without equal sign)\. Some command line options can also be preset via ENV variables\. \fBWIDE=true OPENSSL=/usr/bin/openssl testssl\.sh \-\-starttls=smtp <URI>\fR would be the equivalent to the aforementioned examples\. Preference has the command line over any environment variables\. @@ -238,8 +241,7 @@ session resumption capabilities, Time skew relative to localhost (most server implementations return random values)\. . .IP "\(bu" 4 -Several certificate information -.RS +. .IP "\(bu" 4 signature algorithm, . @@ -275,7 +277,7 @@ displaying DNS Certification Authority Authorization resource record . .IP "\(bu" 4 Certificate Transparency info (if provided by server)\. -.RE +. .IP "" 0 . @@ -409,7 +411,7 @@ Security headers (X\-Frame\-Options, X\-XSS\-Protection, Expect\-CT,\.\.\. , CSP .IP "" 0 . .P -Please note that in testssl\.sh 3,0 you can still use \fBrfc\fR instead of \fBiana\fR and \fBno\-rfc\fR instead of \fBno\-iana\fR but it\'ll disappear after 3\.0\. +Please note that in testssl\.sh 3\.0 you can still use \fBrfc\fR instead of \fBiana\fR and \fBno\-rfc\fR instead of \fBno\-iana\fR but it\'ll disappear after 3\.0\. . .P \fB\-\-show\-each\fR This is an option for all wide modes only: it displays all ciphers tested \-\- not only succeeded ones\. \fBSHOW_EACH_C\fR is your friend if you prefer to set this via the shell environment\. @@ -443,6 +445,9 @@ whole 9 yards . .IP "" 0 . +.P +\fB\-\-disable\-rating\fR disables rating\. Rating automatically gets disabled, to not give a wrong or misleading grade, when not all required functions are executed (e\.g when checking for a single vulnerabilities)\. +. .SS "FILE OUTPUT OPTIONS" \fB\-\-log, \-\-logging\fR Logs stdout also to \fB${NODE}\-p${port}${YYYYMMDD\-HHMM}\.log\fR in current working directory of the shell\. Depending on the color output option (see above) the output file will contain color and other markup escape codes, unless you specify \fB\-\-color 0\fR too\. \fBcat\fR and \-\- if properly configured \fBless\fR \-\- will show the output properly formatted on your terminal\. The output shows a banner with the almost the same information as on the screen\. In addition it shows the command line of the testssl\.sh instance\. Please note that the resulting log file is formatted according to the width of your screen while running testssl\.sh\. You can override the width with the environment variable TERM_WIDTH\. . @@ -626,6 +631,30 @@ MAX_HEADER_FAIL: A number which tells testssl\.sh how often a HTTP GET request o . .IP "" 0 . +.SS "RATING" +This program has a near\-complete implementation of SSL Labs\'s \'SSL Server Rating Guide \fIhttps://github\.com/ssllabs/research/wiki/SSL\-Server\-Rating\-Guide\fR\'\. +. +.P +This is \fInot\fR a 100% reimplementation of the SSL Lab\'s SSL Server Test \fIhttps://www\.ssllabs\.com/ssltest/analyze\.html\fR, but an implementation of the above rating specification, slight discrepancies may occur\. Please note that for now we stick to the SSL Labs rating as good as possible\. We are not responsible for their rating\. Before filing issues please inspect their Rating Guide\. +. +.P +Disclaimer: Having a good grade is \fBNOT\fR necessarily equal to having good security! Don\'t start a competition for the best grade, at least not without monitoring the client handshakes and not without adding a portion of good sense to it\. +. +.P +As of writing, these checks are missing: * GOLDENDOODLE \- should be graded \fBF\fR if vulnerable * Insecure renegotiation \- should be graded \fBF\fR if vulnerable * Padding oracle in AES\-NI CBC MAC check (CVE\-2016\-2107) \- should be graded \fBF\fR if vulnerable * Sleeping POODLE \- should be graded \fBF\fR if vulnerable * Zero Length Padding Oracle (CVE\-2019\-1559) \- should be graded \fBF\fR if vulnerable * Zombie POODLE \- should be graded \fBF\fR if vulnerable * All remaining old Symantec PKI certificates are distrusted \- should be graded \fBT\fR * Symantec certificates issued before June 2016 are distrusted \- should be graded \fBT\fR * ! A reading of DH params \- should give correct points in \fBset_key_str_score()\fR * Anonymous key exchange \- should give \fB0\fR points in \fBset_key_str_score()\fR * Exportable key exchange \- should give \fB40\fR points in \fBset_key_str_score()\fR * Weak key (Debian OpenSSL Flaw) \- should give \fB0\fR points in \fBset_key_str_score()\fR +. +.P +To implement a new grading cap, simply call the \fBset_grade_cap()\fR function, with the grade and a reason: \fBbash set_grade_cap "D" "Vulnerable to documentation"\fR To implement a new grade warning, simply call the \fBset_grade_warning()\fR function, with a message: \fBbash set_grade_warning "Documentation is always right"\fR +. +.P +When implementing a new check (be it vulnerability or not) that sets grade caps, the \fBset_rating_state()\fR has to be updated (i\.e\. the \fB$do_mycheck\fR variable\-name has to be added to the loop, and \fB$nr_enabled\fR if\-statement has to be incremented) +. +.P +The \fBset_rating_state()\fR automatically disables ratinng, if all the required checks are \fInot\fR enabled\. This is to prevent giving out a misleading or wrong grade\. +. +.P +When a new revision of the rating specification comes around, the following has to be done: * New grade caps has to be either: 1\. Added to the script wherever relevant, or 2\. Added to the above list of missing checks (if \fIi\.\fR is not possible) * New grade warnings has to be added wherever relevant * The revision output in \fBrun_rating()\fR function has to updated +. .SH "EXAMPLES" . .nf @@ -635,7 +664,7 @@ MAX_HEADER_FAIL: A number which tells testssl\.sh how often a HTTP GET request o .fi . .P -does a default run on https://testssl\.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation\. +does a default run on https://testssl\.sh (protocols, standard cipher lists, server\'s cipher preferences, forward secrecy, server defaults, vulnerabilities, client simulation, and rating\. . .IP "" 4 . diff --git a/doc/testssl.1.html b/doc/testssl.1.html index 5a6c392..662cfae 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -121,11 +121,11 @@ linked OpenSSL binaries for major operating systems are supplied in <code>./bin/ <p>1) SSL/TLS protocol check</p> -<p>2) standard cipher categories to give you upfront an idea for the ciphers supported</p> +<p>2) standard cipher categories</p> -<p>3) checks forward secrecy: ciphers and elliptical curves</p> +<p>3) server's cipher preferences (server order?)</p> -<p>4) server preferences (server order)</p> +<p>4) forward secrecy: ciphers and elliptical curves</p> <p>5) server defaults (certificate info, TLS extensions, session information)</p> @@ -133,9 +133,9 @@ linked OpenSSL binaries for major operating systems are supplied in <code>./bin/ <p>7) vulnerabilities</p> -<p>8) testing each of 370 preconfigured ciphers</p> +<p>8) client simulation</p> -<p>9) client simulation</p> +<p>9) rating</p> <h2 id="OPTIONS-AND-PARAMETERS">OPTIONS AND PARAMETERS</h2> @@ -185,7 +185,7 @@ The same can be achieved by setting the environment variable <code>WARNINGS</cod <p><code>--openssl-timeout <seconds></code> This is especially useful for all connects using openssl and practically useful for mass testing. It avoids the openssl connect to hang for ~2 minutes. The expected parameter <code>seconds</code> instructs testssl.sh to wait before the openssl connect will be terminated. The option is only available if your OS has a timeout binary installed. As there are different implementations of <code>timeout</code>: It automatically calls the binary with the right parameters. OPENSSL_TIMEOUT is the equivalent environment variable.</p> -<p><code>--basicauth <user:pass></code> This can be set to provide HTTP basic auth credentials which are used during checks for security headers. BASICAUTH is the ENV variable you can use instead.</p> +<p><code>--basicauth <user:pass></code> This can be set to provide HTTP basic auth credentials which are used during checks for security headers. BASICAUTH is the ENV variable you can use instead.</p> <h3 id="SPECIAL-INVOCATIONS">SPECIAL INVOCATIONS</h3> @@ -357,7 +357,7 @@ Also for multiple server certificates are being checked for as well as for the c </ul> -<p>Please note that in testssl.sh 3,0 you can still use <code>rfc</code> instead of <code>iana</code> and <code>no-rfc</code> instead of <code>no-iana</code> but it'll disappear after 3.0.</p> +<p>Please note that in testssl.sh 3.0 you can still use <code>rfc</code> instead of <code>iana</code> and <code>no-rfc</code> instead of <code>no-iana</code> but it'll disappear after 3.0.</p> <p><code>--show-each</code> This is an option for all wide modes only: it displays all ciphers tested -- not only succeeded ones. <code>SHOW_EACH_C</code> is your friend if you prefer to set this via the shell environment.</p> @@ -377,6 +377,9 @@ Also for multiple server certificates are being checked for as well as for the c </ol> +<p><code>--disable-rating</code> disables rating. +Rating automatically gets disabled, to not give a wrong or misleading grade, when not all required functions are executed (e.g when checking for a single vulnerabilities).</p> + <h3 id="FILE-OUTPUT-OPTIONS">FILE OUTPUT OPTIONS</h3> <p><code>--log, --logging</code> Logs stdout also to <code>${NODE}-p${port}${YYYYMMDD-HHMM}.log</code> in current working directory of the shell. Depending on the color output option (see above) the output file will contain color and other markup escape codes, unless you specify <code>--color 0</code> too. <code>cat</code> and -- if properly configured <code>less</code> -- will show the output properly formatted on your terminal. The output shows a banner with the almost the same information as on the screen. In addition it shows the command line of the testssl.sh instance. Please note that the resulting log file is formatted according to the width of your screen while running testssl.sh. You can override the width with the environment variable TERM_WIDTH.</p> @@ -475,12 +478,61 @@ Also for multiple server certificates are being checked for as well as for the c </ul> +<h3 id="RATING">RATING</h3> + +<p>This program has a near-complete implementation of SSL Labs's '<a href="https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide">SSL Server Rating Guide</a>'.</p> + +<p>This is <em>not</em> a 100% reimplementation of the <a href="https://www.ssllabs.com/ssltest/analyze.html">SSL Lab's SSL Server Test</a>, but an implementation of the above rating specification, slight discrepancies may occur. Please note that for now we stick to the SSL Labs rating as good as possible. We are not responsible for their rating. Before filing issues please inspect their Rating Guide.</p> + +<p>Disclaimer: Having a good grade is <strong>NOT</strong> necessarily equal to having good security! Don't start a competition for the best grade, at least not without monitoring the client handshakes and not without adding a portion of good sense to it.</p> + +<p>As of writing, these checks are missing: +* GOLDENDOODLE - should be graded <strong>F</strong> if vulnerable +* Insecure renegotiation - should be graded <strong>F</strong> if vulnerable +* Padding oracle in AES-NI CBC MAC check (CVE-2016-2107) - should be graded <strong>F</strong> if vulnerable +* Sleeping POODLE - should be graded <strong>F</strong> if vulnerable +* Zero Length Padding Oracle (CVE-2019-1559) - should be graded <strong>F</strong> if vulnerable +* Zombie POODLE - should be graded <strong>F</strong> if vulnerable +* All remaining old Symantec PKI certificates are distrusted - should be graded <strong>T</strong> +* Symantec certificates issued before June 2016 are distrusted - should be graded <strong>T</strong> +* ! A reading of DH params - should give correct points in <code>set_key_str_score()</code> +* Anonymous key exchange - should give <strong>0</strong> points in <code>set_key_str_score()</code> +* Exportable key exchange - should give <strong>40</strong> points in <code>set_key_str_score()</code> +* Weak key (Debian OpenSSL Flaw) - should give <strong>0</strong> points in <code>set_key_str_score()</code></p> + +<h4 id="Implementing-new-grades-caps-or-warnings">Implementing new grades caps or -warnings</h4> + +<p>To implement a new grading cap, simply call the <code>set_grade_cap()</code> function, with the grade and a reason: +<code>bash +set_grade_cap "D" "Vulnerable to documentation" +</code> +To implement a new grade warning, simply call the <code>set_grade_warning()</code> function, with a message: +<code>bash +set_grade_warning "Documentation is always right" +</code></p> + +<h4 id="Implementing-a-new-check-which-contains-grade-caps">Implementing a new check which contains grade caps</h4> + +<p>When implementing a new check (be it vulnerability or not) that sets grade caps, the <code>set_rating_state()</code> has to be updated (i.e. the <code>$do_mycheck</code> variable-name has to be added to the loop, and <code>$nr_enabled</code> if-statement has to be incremented)</p> + +<p>The <code>set_rating_state()</code> automatically disables ratinng, if all the required checks are <em>not</em> enabled. +This is to prevent giving out a misleading or wrong grade.</p> + +<h4 id="Implementing-a-new-revision">Implementing a new revision</h4> + +<p>When a new revision of the rating specification comes around, the following has to be done: +* New grade caps has to be either: + 1. Added to the script wherever relevant, or + 2. Added to the above list of missing checks (if <em>i.</em> is not possible) +* New grade warnings has to be added wherever relevant +* The revision output in <code>run_rating()</code> function has to updated</p> + <h2 id="EXAMPLES">EXAMPLES</h2> <pre><code> testssl.sh testssl.sh </code></pre> -<p>does a default run on https://testssl.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation.</p> +<p>does a default run on https://testssl.sh (protocols, standard cipher lists, server's cipher preferences, forward secrecy, server defaults, vulnerabilities, client simulation, and rating.</p> <pre><code> testssl.sh testssl.net:443 </code></pre> @@ -606,7 +658,7 @@ where to get this program from.</p> <ol class='man-decor man-foot man foot'> <li class='tl'></li> - <li class='tc'>January 2020</li> + <li class='tc'>May 2020</li> <li class='tr'>testssl(1)</li> </ol> diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 1f5be5d..4cce65b 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -40,11 +40,11 @@ linked OpenSSL binaries for major operating systems are supplied in `./bin/`. 1) SSL/TLS protocol check -2) checks forward secrecy: ciphers and elliptical curves +2) standard cipher categories -3) standard cipher categories to give you upfront an idea for the ciphers supported +3) server's cipher preferences (server order?) -4) server preferences (server order) +4) forward secrecy: ciphers and elliptical curves 5) server defaults (certificate info, TLS extensions, session information) @@ -56,7 +56,8 @@ linked OpenSSL binaries for major operating systems are supplied in `./bin/`. 8) client simulation -9) Result of script in form of a grade +9) rating + ## OPTIONS AND PARAMETERS @@ -269,7 +270,7 @@ Also for multiple server certificates are being checked for as well as for the c * `no-openssl`: don't display the OpenSSL cipher suite name, display IANA names only. * `no-iana`: don't display the IANA cipher suite name, display OpenSSL names only. -Please note that in testssl.sh 3,0 you can still use `rfc` instead of `iana` and `no-rfc` instead of `no-iana` but it'll disappear after 3.0. +Please note that in testssl.sh 3.0 you can still use `rfc` instead of `iana` and `no-rfc` instead of `no-iana` but it'll disappear after 3.0. `--show-each` This is an option for all wide modes only: it displays all ciphers tested -- not only succeeded ones. `SHOW_EACH_C` is your friend if you prefer to set this via the shell environment. @@ -288,8 +289,8 @@ Please note that in testssl.sh 3,0 you can still use `rfc` instead of `iana` and 5. display bytes received via sockets 6. whole 9 yards -`--disable-rating` disables rating explicitly. -Grading automatically gets disabled, to not give a wrong or misleading grade, when not all required functions are executed (e.g when checking for a single vulnerabilities). +`--disable-rating` disables rating. +Rating automatically gets disabled, to not give a wrong or misleading grade, when not all required functions are executed (e.g when checking for a single vulnerabilities). ### FILE OUTPUT OPTIONS @@ -435,7 +436,7 @@ When a new revision of the rating specification comes around, the following has testssl.sh testssl.sh -does a default run on https://testssl.sh (protocols, standard cipher lists, server's cipher preferences, FS, server defaults, vulnerabilities, client simulation, and rating. +does a default run on https://testssl.sh (protocols, standard cipher lists, server's cipher preferences, forward secrecy, server defaults, vulnerabilities, client simulation, and rating. testssl.sh testssl.net:443 diff --git a/testssl.sh b/testssl.sh index 3971364..bae36ec 100755 --- a/testssl.sh +++ b/testssl.sh @@ -260,6 +260,7 @@ APP_TRAF_KEY_INFO="" # Information about the application traf TLS13_ONLY=false # Does the server support TLS 1.3 ONLY? OSSL_SHORTCUT=${OSSL_SHORTCUT:-false} # Hack: if during the scan turns out the OpenSSL binary suports TLS 1.3 would be a better choice, this enables it. TLS_EXTENSIONS="" +V2_HELLO_CIPHERSPEC_LENGTH=0 declare -r NPN_PROTOs="spdy/4a2,spdy/3,spdy/3.1,spdy/2,spdy/1,http/1.1" # alpn_protos needs to be space-separated, not comma-seperated, including odd ones observed @ facebook and others, old ones like h2-17 omitted as they could not be found declare -r ALPN_PROTOs="h2 spdy/3.1 http/1.1 grpc-exp h2-fb spdy/1 spdy/2 spdy/3 stun.turn stun.nat-discovery webrtc c-webrtc ftp" @@ -297,6 +298,7 @@ OSSL_VER_MINOR=0 OSSL_VER_APPENDIX="none" CLIENT_PROB_NO=1 HAS_DH_BITS=${HAS_DH_BITS:-false} # initialize openssl variables +HAS_CURVES=false OSSL_SUPPORTED_CURVES="" HAS_SSL2=false HAS_SSL3=false @@ -2137,6 +2139,7 @@ s_client_options() { # (e.g. client simulations) we replace it with the name which OpenSSL understands # This shouldn't be needed. We have this here as a last resort if [[ "$1" =~ " -curves " ]]; then + ! "$HAS_CURVES" && options="${options// -curves / -groups }" [[ "$1" =~ secp192r1 ]] && options="${options//secp192r1/prime192v1}" [[ "$1" =~ secp256r1 ]] && options="${options//secp256r1/prime256v1}" fi @@ -3690,7 +3693,7 @@ run_cipher_match(){ tls13_ciphers_to_test="" for (( i=bundle*bundle_size; i < end_of_bundle; i++ )); do if ! "${ciphers_found2[i]}"; then - if [[ "${ciph2[i]}" == TLS13* ]] || [[ "${ciph2[i]}" == TLS_* ]]; then + if [[ "${ciph2[i]}" == TLS13* ]] || [[ "${ciph2[i]}" == TLS_* ]] || [[ "${ciph2[i]}" == AEAD-* ]]; then tls13_ciphers_to_test+=":${ciph2[i]}" else ciphers_to_test+=":${ciph2[i]}" @@ -3708,7 +3711,7 @@ run_cipher_match(){ [[ $i -eq $end_of_bundle ]] && break i=${index[i]} ciphers_found[i]=true - if [[ "$cipher" == TLS13* ]] || [[ "$cipher" == TLS_* ]]; then + if [[ "$cipher" == TLS13* ]] || [[ "$cipher" == TLS_* ]] || [[ "$cipher" == AEAD-* ]]; then kx[i]="$(read_dhtype_from_file $TMPFILE)" fi if [[ ${kx[i]} == "Kx=ECDH" ]] || [[ ${kx[i]} == "Kx=DH" ]] || [[ ${kx[i]} == "Kx=EDH" ]]; then @@ -3964,7 +3967,7 @@ run_allciphers() { tls13_ciphers_to_test="" for (( i=bundle*bundle_size; i < end_of_bundle; i++ )); do if ! "${ciphers_found2[i]}"; then - if [[ "${ciph2[i]}" == TLS13* ]] || [[ "${ciph2[i]}" == TLS_* ]]; then + if [[ "${ciph2[i]}" == TLS13* ]] || [[ "${ciph2[i]}" == TLS_* ]] || [[ "${ciph2[i]}" == AEAD-* ]]; then tls13_ciphers_to_test+=":${ciph2[i]}" else ciphers_to_test+=":${ciph2[i]}" @@ -3982,7 +3985,7 @@ run_allciphers() { [[ $i -eq $end_of_bundle ]] && break i=${index[i]} ciphers_found[i]=true - if [[ "$cipher" == TLS13* ]] || [[ "$cipher" == TLS_* ]]; then + if [[ "$cipher" == TLS13* ]] || [[ "$cipher" == TLS_* ]] || [[ "$cipher" == AEAD-* ]]; then kx[i]="$(read_dhtype_from_file $TMPFILE)" fi if [[ ${kx[i]} == Kx=ECDH ]] || [[ ${kx[i]} == Kx=DH ]] || [[ ${kx[i]} == Kx=EDH ]]; then @@ -4086,7 +4089,7 @@ run_allciphers() { # are good or bad) and list them in order to encryption strength. ciphers_by_strength() { local proto="$1" proto_hex="$2" proto_text="$3" - local using_sockets="$4" + local using_sockets="$4" wide="$5" local ossl_ciphers_proto local -i nr_ciphers nr_ossl_ciphers nr_nonossl_ciphers success local n sslvers auth mac hexc sslv2_ciphers="" cipher @@ -4099,15 +4102,18 @@ ciphers_by_strength() { local id local has_dh_bits="$HAS_DH_BITS" - pr_underline "$(printf -- "%b" "$proto_text")" # for local problem if it happens - out " " + "$wide" || out " " if ! "$using_sockets" && ! locally_supported "$proto"; then + pr_local_problem "Your $OPENSSL does not support $proto" + "$wide" && outln return 0 fi - outln - [[ $(has_server_protocol "${proto:1}") -eq 1 ]] && return 0 + if [[ $(has_server_protocol "${proto:1}") -eq 1 ]]; then + "$wide" && outln " - " + return 0 + fi # get a list of all the cipher suites to test nr_ciphers=0 @@ -4122,7 +4128,8 @@ ciphers_by_strength() { ciphers_found[nr_ciphers]=false sigalg[nr_ciphers]="" ossl_supported[nr_ciphers]=${TLS_CIPHER_OSSL_SUPPORTED[i]} - if "$using_sockets" && ! "$has_dh_bits" && ( [[ ${kx[nr_ciphers]} == "Kx=ECDH" ]] || [[ ${kx[nr_ciphers]} == "Kx=DH" ]] || [[ ${kx[nr_ciphers]} == "Kx=EDH" ]] ); then + if "$using_sockets" && "$wide" && ! "$has_dh_bits" && \ + ( [[ ${kx[nr_ciphers]} == "Kx=ECDH" ]] || [[ ${kx[nr_ciphers]} == "Kx=DH" ]] || [[ ${kx[nr_ciphers]} == "Kx=EDH" ]] ); then ossl_supported[nr_ciphers]=false fi if [[ ${#hexc} -eq 9 ]]; then @@ -4138,16 +4145,16 @@ ciphers_by_strength() { normalized_hexcode[nr_ciphers]="x${hexc:2:2}${hexc:7:2}${hexc:12:2}" fi if ( "$using_sockets" || "${TLS_CIPHER_OSSL_SUPPORTED[i]}" ); then - if [[ ${#hexc} -eq 9 ]] && [[ "$proto_text" != SSLv2 ]]; then - if [[ "$proto_text" == TLS\ 1.3 ]]; then + if [[ ${#hexc} -eq 9 ]] && [[ "$proto" != -ssl2 ]]; then + if [[ "$proto" == -tls1_3 ]]; then [[ "${hexc:2:2}" == 13 ]] && nr_ciphers+=1 - elif [[ "$proto_text" == TLS\ 1.2 ]]; then + elif [[ "$proto" == -tls1_2 ]]; then [[ "${hexc:2:2}" != 13 ]] && nr_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ [[ "${TLS_CIPHER_RFC_NAME[i]}" != *_CCM ]] && [[ "${TLS_CIPHER_RFC_NAME[i]}" != *_CCM_8 ]]; then nr_ciphers+=1 fi - elif [[ ${#hexc} -eq 14 ]] && [[ "$proto_text" == SSLv2 ]]; then + elif [[ ${#hexc} -eq 14 ]] && [[ "$proto" == -ssl2 ]]; then sslv2_ciphers+=", ${hexcode[nr_ciphers]}" nr_ciphers+=1 fi @@ -4157,7 +4164,7 @@ ciphers_by_strength() { # The OpenSSL ciphers function, prior to version 1.1.0, could only understand -ssl2, -ssl3, and -tls1. if [[ "$OSSL_NAME" =~ LibreSSL ]]; then ossl_ciphers_proto="" - elif [[ "$proto" == -ssl2 ]] || [[ "$proto" == -ssl3 ]] || \ + elif [[ $proto == -ssl2 ]] || [[ $proto == -ssl3 ]] || \ [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 1.1.0* ]] || [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 1.1.1* ]] || \ [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 3.0.0* ]]; then ossl_ciphers_proto="$proto" @@ -4165,10 +4172,10 @@ ciphers_by_strength() { ossl_ciphers_proto="-tls1" fi while read hexc n ciph[nr_ciphers] sslvers kx[nr_ciphers] auth enc[nr_ciphers] mac export2[nr_ciphers]; do - if [[ "$proto_text" == TLS\ 1.3 ]]; then - [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]] || continue - elif [[ "$proto_text" == "TLS 1.2" ]]; then - if [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]]; then + if [[ "$proto" == -tls1_3 ]]; then + [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]] || [[ "${ciph[nr_ciphers]}" == AEAD-* ]] || continue + elif [[ "$proto" == -tls1_2 ]]; then + if [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]] || [[ "${ciph[nr_ciphers]}" == AEAD-* ]]; then continue fi elif [[ "${ciph[nr_ciphers]}" == *-SHA256 ]] || [[ "${ciph[nr_ciphers]}" == *-SHA384 ]] || \ @@ -4184,31 +4191,43 @@ ciphers_by_strength() { done < <(actually_supported_osslciphers 'ALL:COMPLEMENTOFALL:@STRENGTH' 'ALL' "$ossl_ciphers_proto -V") fi - if [[ "$proto" == -ssl2 ]]; then + if [[ $proto == -ssl2 ]]; then if "$using_sockets"; then sslv2_sockets "${sslv2_ciphers:2}" "true" - if [[ $? -eq 3 ]] && [[ "$V2_HELLO_CIPHERSPEC_LENGTH" -ne 0 ]]; then - supported_sslv2_ciphers="$(grep "Supported cipher: " "$TEMPDIR/$NODEIP.parse_sslv2_serverhello.txt")" - "$SHOW_SIGALGO" && s="$(read_sigalg_from_file "$HOSTCERT")" - for (( i=0 ; i<nr_ciphers; i++ )); do - if [[ "$supported_sslv2_ciphers" =~ ${normalized_hexcode[i]} ]]; then - ciphers_found[i]=true - "$SHOW_SIGALGO" && sigalg[i]="$s" - fi - done + if [[ $? -eq 3 ]] ; then + add_proto_offered ssl2 yes + if [[ "$V2_HELLO_CIPHERSPEC_LENGTH" -ne 0 ]]; then + supported_sslv2_ciphers="$(grep "Supported cipher: " "$TEMPDIR/$NODEIP.parse_sslv2_serverhello.txt")" + "$wide" && "$SHOW_SIGALGO" && s="$(read_sigalg_from_file "$HOSTCERT")" + for (( i=0 ; i<nr_ciphers; i++ )); do + if [[ "$supported_sslv2_ciphers" =~ ${normalized_hexcode[i]} ]]; then + ciphers_found[i]=true + "$wide" && "$SHOW_SIGALGO" && sigalg[i]="$s" + fi + done + else + outln " protocol supported with no cipher " + fi + else + add_proto_offered ssl2 no + "$wide" && outln " - " fi else $OPENSSL s_client $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY -ssl2 >$TMPFILE 2>$ERRFILE </dev/null sclient_connect_successful $? "$TMPFILE" if [[ $? -eq 0 ]]; then + add_proto_offered ssl2 yes supported_sslv2_ciphers="$(grep -A 4 "Ciphers common between both SSL endpoints:" $TMPFILE)" - "$SHOW_SIGALGO" && s="$(read_sigalg_from_file "$TMPFILE")" + "$wide" && "$SHOW_SIGALGO" && s="$(read_sigalg_from_file "$TMPFILE")" for (( i=0 ; i<nr_ciphers; i++ )); do if [[ "$supported_sslv2_ciphers" =~ ${ciph[i]} ]]; then ciphers_found[i]=true - "$SHOW_SIGALGO" && sigalg[i]="$s" + "$wide" && "$SHOW_SIGALGO" && sigalg[i]="$s" fi done + else + add_proto_offered ssl2 no + "$wide" && outln " - " fi fi else # no SSLv2 @@ -4263,12 +4282,12 @@ ciphers_by_strength() { done i=${index[i]} ciphers_found[i]=true - [[ "$proto_text" == TLS\ 1.3 ]] && kx[i]="$(read_dhtype_from_file $TMPFILE)" - if [[ ${kx[i]} == Kx=ECDH ]] || [[ ${kx[i]} == Kx=DH ]] || [[ ${kx[i]} == Kx=EDH ]]; then + "$wide" && [[ "$proto" == -tls1_3 ]] && kx[i]="$(read_dhtype_from_file $TMPFILE)" + if "$wide" && ( [[ ${kx[i]} == Kx=ECDH ]] || [[ ${kx[i]} == Kx=DH ]] || [[ ${kx[i]} == Kx=EDH ]] ); then dhlen=$(read_dhbits_from_file "$TMPFILE" quiet) kx[i]="${kx[i]} $dhlen" fi - "$SHOW_SIGALGO" && grep -q "\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-" $TMPFILE && \ + "$wide" && "$SHOW_SIGALGO" && grep -q "\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-" $TMPFILE && \ sigalg[i]="$(read_sigalg_from_file "$TMPFILE")" fi fi @@ -4311,7 +4330,7 @@ ciphers_by_strength() { done success=1 if [[ -n "$ciphers_to_test" ]]; then - if "$SHOW_SIGALGO"; then + if "$wide" && "$SHOW_SIGALGO"; then tls_sockets "$proto_hex" "${ciphers_to_test:2}, 00,ff" "all" else tls_sockets "$proto_hex" "${ciphers_to_test:2}, 00,ff" "ephemeralkey" @@ -4324,12 +4343,12 @@ ciphers_by_strength() { done i=${index[i]} ciphers_found[i]=true - [[ "$proto_text" == TLS\ 1.3 ]] && kx[i]="$(read_dhtype_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" - if [[ ${kx[i]} == Kx=ECDH ]] || [[ ${kx[i]} == Kx=DH ]] || [[ ${kx[i]} == Kx=EDH ]]; then + "$wide" && [[ "$proto" == -tls1_3 ]] && kx[i]="$(read_dhtype_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" + if "$wide" && ( [[ ${kx[i]} == Kx=ECDH ]] || [[ ${kx[i]} == Kx=DH ]] || [[ ${kx[i]} == Kx=EDH ]] ); then dhlen=$(read_dhbits_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" quiet) kx[i]="${kx[i]} $dhlen" fi - "$SHOW_SIGALGO" && [[ -r "$HOSTCERT" ]] && \ + "$wide" && "$SHOW_SIGALGO" && [[ -r "$HOSTCERT" ]] && \ sigalg[i]="$(read_sigalg_from_file "$HOSTCERT")" fi fi @@ -4337,8 +4356,16 @@ ciphers_by_strength() { done fi + cipher="" for (( i=0 ; i<nr_ciphers; i++ )); do - if "${ciphers_found[i]}" || "$SHOW_EACH_C"; then + if "${ciphers_found[i]}"; then + if [[ "$DISPLAY_CIPHERNAMES" =~ rfc ]] && [[ "${rfc_ciph[i]}" != - ]]; then + cipher+="${rfc_ciph[i]} " + else + cipher+="${ciph[i]} " + fi + fi + if "$wide" && ( "${ciphers_found[i]}" || "$SHOW_EACH_C" ); then normalized_hexcode[i]="$(tolower "${normalized_hexcode[i]}")" neat_list "${normalized_hexcode[i]}" "${ciph[i]}" "${kx[i]}" "${enc[i]}" "${export2[i]}" "${ciphers_found[i]}" available="" @@ -4358,6 +4385,26 @@ ciphers_by_strength() { fi done + if [[ $proto != -ssl2 ]]; then + # We handled SSLv2 above already + if [[ -n "$cipher" ]]; then + add_proto_offered $proto yes + else + add_proto_offered $proto no + "$wide" && outln " -" + fi + fi + if ! "$wide" && [[ -n "$cipher" ]]; then + outln + out "$(printf " %-10s " "$proto_text: ")" + if [[ "$COLOR" -le 2 ]]; then + out "$(out_row_aligned_max_width "$cipher" " " $TERM_WIDTH)" + else + out_row_aligned_max_width_by_entry "$cipher" " " $TERM_WIDTH pr_cipher_quality + fi + fi + [[ -n "$cipher" ]] && fileout "supportedciphers_${proto_text//./_}" "INFO" "$cipher" + "$using_sockets" && HAS_DH_BITS="$has_dh_bits" tmpfile_handle ${FUNCNAME[0]}${proto}.txt return 0 @@ -4388,7 +4435,8 @@ run_cipher_per_proto() { outln neat_header while read proto proto_hex proto_text; do - ciphers_by_strength "$proto" "$proto_hex" "$proto_text" "$using_sockets" + prln_underline "$(printf -- "%b" "$proto_text")" + ciphers_by_strength "$proto" "$proto_hex" "$proto_text" "$using_sockets" "true" done <<< "$(tm_out " -ssl2 22 SSLv2\n -ssl3 00 SSLv3\n -tls1 01 TLS 1\n -tls1_1 02 TLS 1.1\n -tls1_2 03 TLS 1.2\n -tls1_3 04 TLS 1.3")" return 0 #FIXME: no error condition @@ -4910,7 +4958,7 @@ run_client_simulation() { # locally_supported() { [[ -n "$2" ]] && out "$2 " - if $OPENSSL s_client "$1" -connect invalid. 2>&1 | grep -aq "unknown option"; then + if $OPENSSL s_client "$1" -connect invalid. 2>&1 | grep -aiq "unknown option"; then prln_local_problem "$OPENSSL doesn't support \"s_client $1\"" return 7 fi @@ -4932,7 +4980,7 @@ run_prototest_openssl() { local protos proto # check whether the protocol being tested is supported by $OPENSSL - $OPENSSL s_client "$1" -connect invalid. 2>&1 | grep -aq "unknown option" && return 7 + $OPENSSL s_client "$1" -connect invalid. 2>&1 | grep -aiq "unknown option" && return 7 case "$1" in -ssl2) protos="-ssl2" ;; -ssl3) protos="-ssl3" ;; @@ -4988,7 +5036,7 @@ run_prototest_openssl() { # # arg1: protocol # arg2: available (yes) or not (no) -add_tls_offered() { +add_proto_offered() { if [[ "$PROTOS_OFFERED" =~ $1: ]]; then # the ":" is mandatory here (and @ other places), otherwise e.g. tls1 will match tls1_2 : @@ -4997,7 +5045,7 @@ add_tls_offered() { fi } -# function which checks whether SSLv2 - TLS 1.2 is being offered, see add_tls_offered() +# function which checks whether SSLv2 - TLS 1.2 is being offered, see add_proto_offered() # 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 # return value is always zero @@ -5084,16 +5132,16 @@ run_protocols() { 1) # no sslv2 server hello returned, like in openlitespeed which returns HTTP! prln_svrty_best "not offered (OK)" fileout "$jsonID" "OK" "not offered" - add_tls_offered ssl2 no + add_proto_offered ssl2 no ;; 0) # reset prln_svrty_best "not offered (OK)" fileout "$jsonID" "OK" "not offered" - add_tls_offered ssl2 no + add_proto_offered ssl2 no ;; 4) out "likely "; pr_svrty_best "not offered (OK), " fileout "$jsonID" "OK" "likely not offered" - add_tls_offered ssl2 no + add_proto_offered ssl2 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; @@ -5101,7 +5149,7 @@ run_protocols() { [[ "$DEBUG" -ge 2 ]] && tm_out " ($lines lines) " if [[ "$lines" -gt 1 ]]; then nr_ciphers_detected=$((V2_HELLO_CIPHERSPEC_LENGTH / 3)) - add_tls_offered ssl2 yes + add_proto_offered ssl2 yes set_grade_cap "F" "SSLv2 is offered" if [[ 0 -eq "$nr_ciphers_detected" ]]; then prln_svrty_high "supported but couldn't detect a cipher and vulnerable to CVE-2015-3197 "; @@ -5123,15 +5171,16 @@ run_protocols() { case $? in 0) prln_svrty_critical "offered (NOT ok)" fileout "$jsonID" "CRITICAL" "offered" - add_tls_offered ssl2 yes + add_proto_offered ssl2 yes set_grade_cap "F" "SSLv2 is offered" ;; 1) prln_svrty_best "not offered (OK)" fileout "$jsonID" "OK" "not offered" - add_tls_offered ssl2 no + add_proto_offered ssl2 no ;; 5) prln_svrty_high "CVE-2015-3197: $supported_no_ciph2"; fileout "$jsonID" "HIGH" "offered, no cipher" "CVE-2015-3197" "CWE-310" + add_proto_offered ssl2 yes add_tls_offered ssl2 yes set_grade_cap "F" "SSLv2 is offered" ;; @@ -5160,12 +5209,13 @@ run_protocols() { latest_supported="0300" latest_supported_string="SSLv3" fi + add_proto_offered ssl3 yes add_tls_offered ssl3 yes set_grade_cap "B" "SSLv3 is offered" ;; 1) prln_svrty_best "not offered (OK)" fileout "$jsonID" "OK" "not offered" - add_tls_offered ssl3 no + add_proto_offered ssl3 no ;; 2) if [[ "$DETECTED_TLS_VERSION" == 03* ]]; then detected_version_string="TLSv1.$((0x$DETECTED_TLS_VERSION-0x0301))" @@ -5184,19 +5234,19 @@ run_protocols() { ;; 3) pr_svrty_best "not offered (OK), " fileout "$jsonID" "OK" "not offered" - add_tls_offered ssl3 no + add_proto_offered ssl3 no pr_warning "SSL downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "SSL downgraded to STARTTLS plaintext" ;; 4) out "likely "; pr_svrty_best "not offered (OK), " fileout "$jsonID" "OK" "not offered" - add_tls_offered ssl3 no + add_proto_offered ssl3 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) pr_svrty_high "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "HIGH" "$supported_no_ciph1" - add_tls_offered ssl3 yes + add_proto_offered ssl3 yes set_grade_cap "B" "SSLv3 is offered" ;; 7) if "$using_sockets" ; then @@ -5228,11 +5278,11 @@ run_protocols() { fileout "$jsonID" "LOW" "offered (deprecated)" latest_supported="0301" latest_supported_string="TLSv1.0" - add_tls_offered tls1 yes + add_proto_offered tls1 yes set_grade_cap "B" "TLS 1.0 offered" ;; # nothing wrong with it -- per se 1) out "not offered" - add_tls_offered tls1 no + add_proto_offered tls1 no if [[ -z $latest_supported ]]; then outln fileout "$jsonID" "INFO" "not offered" # neither good or bad @@ -5242,7 +5292,7 @@ run_protocols() { fi ;; 2) pr_svrty_medium "not offered" - add_tls_offered tls1 no + add_proto_offered tls1 no if [[ "$DETECTED_TLS_VERSION" == 0300 ]]; then [[ $DEBUG -ge 1 ]] && tm_out " -- downgraded" outln @@ -5263,19 +5313,19 @@ run_protocols() { ;; 3) out "not offered, " fileout "$jsonID" "OK" "not offered" - add_tls_offered tls1 no + add_proto_offered tls1 no pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" ;; 4) out "likely not offered, " fileout "$jsonID" "INFO" "likely not offered" - add_tls_offered tls1 no + add_proto_offered tls1 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" - add_tls_offered tls1 yes + add_proto_offered tls1 yes set_grade_cap "B" "TLS 1.0 offered" ;; 7) if "$using_sockets" ; then @@ -5308,11 +5358,11 @@ run_protocols() { fileout "$jsonID" "LOW" "offered (deprecated)" latest_supported="0302" latest_supported_string="TLSv1.1" - add_tls_offered tls1_1 yes + add_proto_offered tls1_1 yes set_grade_cap "B" "TLS 1.1 offered" ;; # nothing wrong with it 1) out "not offered" - add_tls_offered tls1_1 no + add_proto_offered tls1_1 no if [[ -z $latest_supported ]]; then outln fileout "$jsonID" "INFO" "is not offered" # neither good or bad @@ -5322,7 +5372,7 @@ run_protocols() { fi ;; 2) out "not offered" - add_tls_offered tls1_1 no + add_proto_offered tls1_1 no if [[ "$DETECTED_TLS_VERSION" == "$latest_supported" ]]; then [[ $DEBUG -ge 1 ]] && tm_out " -- downgraded" outln @@ -5346,19 +5396,19 @@ run_protocols() { ;; 3) out "not offered, " fileout "$jsonID" "OK" "not offered" - add_tls_offered tls1_1 no + add_proto_offered tls1_1 no pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" ;; 4) out "likely not offered, " fileout "$jsonID" "INFO" "is not offered" - add_tls_offered tls1_1 no + add_proto_offered tls1_1 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" - add_tls_offered tls1_1 yes + add_proto_offered tls1_1 yes set_grade_cap "B" "TLS 1.1 offered" ;; 7) if "$using_sockets" ; then @@ -5421,9 +5471,9 @@ run_protocols() { fileout "$jsonID" "OK" "offered" latest_supported="0303" latest_supported_string="TLSv1.2" - add_tls_offered tls1_2 yes + add_proto_offered tls1_2 yes ;; # GCM cipher in TLS 1.2: very good! - 1) add_tls_offered tls1_2 no + 1) add_proto_offered tls1_2 no if "$offers_tls13"; then out "not offered" else @@ -5442,7 +5492,7 @@ run_protocols() { fileout "$jsonID" "CRITICAL" "connection failed rather than downgrading to $latest_supported_string" fi ;; - 2) add_tls_offered tls1_2 no + 2) add_proto_offered tls1_2 no set_grade_cap "C" "TLS 1.2 is not offered" pr_svrty_medium "not offered and downgraded to a weaker protocol" if [[ "$tls12_detected_version" == 0300 ]]; then @@ -5471,22 +5521,21 @@ run_protocols() { ;; 3) out "not offered, " fileout "$jsonID" "INFO" "not offered" - add_tls_offered tls1_2 no + add_proto_offered tls1_2 no set_grade_cap "C" "TLS 1.2 is not offered" pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" - set_grade_cap "C" "TLS 1.2 is not offered" ;; 4) out "likely "; pr_svrty_medium "not offered, " fileout "$jsonID" "MEDIUM" "not offered" - add_tls_offered tls1_2 no + add_proto_offered tls1_2 no set_grade_cap "C" "TLS 1.2 is not offered" pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) outln "$supported_no_ciph1" # protocol detected, but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" - add_tls_offered tls1_2 yes + add_proto_offered tls1_2 yes ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5580,7 +5629,7 @@ run_protocols() { fi latest_supported="0304" latest_supported_string="TLSv1.3" - add_tls_offered tls1_3 yes + add_proto_offered tls1_3 yes ;; 1) pr_svrty_low "not offered" if [[ -z $latest_supported ]]; then @@ -5590,7 +5639,7 @@ run_protocols() { prln_svrty_critical " -- connection failed rather than downgrading to $latest_supported_string" fileout "$jsonID" "CRITICAL" "connection failed rather than downgrading to $latest_supported_string" fi - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no ;; 2) if [[ "$DETECTED_TLS_VERSION" == 0300 ]]; then detected_version_string="SSLv3" @@ -5613,23 +5662,23 @@ run_protocols() { prln_svrty_critical " -- server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2}" fileout "$jsonID" "CRITICAL" "server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2}" fi - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no ;; 3) out "not offered " fileout "$jsonID" "INFO" "not offered" - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" ;; 4) out "likely not offered, " fileout "$jsonID" "INFO" "not offered" - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" - add_tls_offered tls1_3 yes + add_proto_offered tls1_3 yes ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5704,7 +5753,7 @@ sub_cipherlists() { local cve="${9}" local cwe="${10}" - pr_bold "$3 " + pr_bold "$3 " [[ "$OPTIMAL_PROTO" == -ssl2 ]] && proto="$OPTIMAL_PROTO" jsonID="${jsonID}_$5" @@ -5877,7 +5926,7 @@ run_cipherlists() { local ossl_exp_ciphers exp_ciphers sslv2_exp_ciphers local ossl_low_ciphers low_ciphers sslv2_low_ciphers local ossl_tdes_ciphers tdes_ciphers sslv2_tdes_cipher - local ossl_average_ciphers average_ciphers + local ossl_obsoleted_ciphers obsoleted_ciphers local strong_ciphers local cwe="CWE-327" local cwe2="CWE-310" @@ -5914,13 +5963,14 @@ run_cipherlists() { tdes_ciphers="00,07, 00,0A, 00,0D, 00,10, 00,13, 00,16, 00,1F, 00,21, 00,8B, 00,8F, 00,93, C0,03, C0,08, C0,0D, C0,12, C0,1A, C0,1B, C0,1C, C0,34, FE,FF, FF,E0, 00,FF" sslv2_tdes_ciphers="07,01,c0" - ossl_average_ciphers='HIGH:MEDIUM:AES:CAMELLIA:ARIA:!IDEA:!CHACHA20:!3DES:!RC2:!RC4:!AESCCM8:!AESCCM:!AESGCM:!ARIAGCM:!aNULL:!MD5' + # # Now all AES, CAMELLIA, ARIA and SEED CBC ciphers plus GOST + ossl_obsoleted_ciphers='HIGH:MEDIUM:AES:CAMELLIA:ARIA:!IDEA:!CHACHA20:!3DES:!RC2:!RC4:!AESCCM8:!AESCCM:!AESGCM:!ARIAGCM:!aNULL:!MD5' # egrep -w "256|128" etc/cipher-mapping.txt | egrep -v "Au=None|AEAD|RC2|RC4|IDEA|MD5" - average_ciphers="00,2F, 00,30, 00,31, 00,32, 00,33, 00,35, 00,36, 00,37, 00,38, 00,39, 00,3C, 00,3D, 00,3E, 00,3F, 00,40, 00,41, 00,42, 00,43, 00,44, 00,45, 00,67, 00,68, 00,69, 00,6A, 00,6B, 00,84, 00,85, 00,86, 00,87, 00,88, 00,8C, 00,8D, 00,90, 00,91, 00,94, 00,95, 00,96, 00,97, 00,98, 00,99, 00,9A, 00,AE, 00,AF, 00,B2, 00,B3, 00,B6, 00,B7, 00,BA, 00,BB, 00,BC, 00,BD, 00,BE, 00,C0, 00,C1, 00,C2, 00,C3, 00,C4, C0,04, C0,05, C0,09, C0,0A, C0,0E, C0,0F, C0,13, C0,14, C0,1D, C0,1E, C0,1F, C0,20, C0,21, C0,22, C0,23, C0,24, C0,25, C0,26, C0,27, C0,28, C0,29, C0,2A, C0,35, C0,36, C0,37, C0,38, C0,3C, C0,3D, C0,3E, C0,3F, C0,40, C0,41, C0,42, C0,43, C0,44, C0,45, C0,48, C0,49, C0,4A, C0,4B, C0,4C, C0,4D, C0,4E, C0,4F, C0,64, C0,65, C0,66, C0,67, C0,68, C0,69, C0,70, C0,71, C0,72, C0,73, C0,74, C0,75, C0,76, C0,77, C0,78, C0,79, C0,94, C0,95, C0,96, C0,97, C0,98, C0,99, C0,9A, C0,9B" + obsoleted_ciphers="00,2F, 00,30, 00,31, 00,32, 00,33, 00,35, 00,36, 00,37, 00,38, 00,39, 00,3C, 00,3D, 00,3E, 00,3F, 00,40, 00,41, 00,42, 00,43, 00,44, 00,45, 00,67, 00,68, 00,69, 00,6A, 00,6B, 00,84, 00,85, 00,86, 00,87, 00,88, 00,8C, 00,8D, 00,90, 00,91, 00,94, 00,95, 00,96, 00,97, 00,98, 00,99, 00,9A, 00,AE, 00,AF, 00,B2, 00,B3, 00,B6, 00,B7, 00,BA, 00,BB, 00,BC, 00,BD, 00,BE, 00,C0, 00,C1, 00,C2, 00,C3, 00,C4, C0,04, C0,05, C0,09, C0,0A, C0,0E, C0,0F, C0,13, C0,14, C0,1D, C0,1E, C0,1F, C0,20, C0,21, C0,22, C0,23, C0,24, C0,25, C0,26, C0,27, C0,28, C0,29, C0,2A, C0,35, C0,36, C0,37, C0,38, C0,3C, C0,3D, C0,3E, C0,3F, C0,40, C0,41, C0,42, C0,43, C0,44, C0,45, C0,48, C0,49, C0,4A, C0,4B, C0,4C, C0,4D, C0,4E, C0,4F, C0,64, C0,65, C0,66, C0,67, C0,68, C0,69, C0,70, C0,71, C0,72, C0,73, C0,74, C0,75, C0,76, C0,77, C0,78, C0,79, C0,94, C0,95, C0,96, C0,97, C0,98, C0,99, C0,9A, C0,9B" # Workaround: If we use sockets and in order not to hit 131+1 ciphers we omit the GOST ciphers if SERVER_SIZE_LIMIT_BUG is true. # This won't be supported by Cisco ACE anyway. - "$SERVER_SIZE_LIMIT_BUG" || average_ciphers="${average_ciphers}, 00,80, 00,81, FF,01, FF,02, FF,03, FF,85" - average_ciphers="${average_ciphers}, 00,FF" + "$SERVER_SIZE_LIMIT_BUG" || obsoleted_ciphers="${obsoleted_ciphers}, 00,80, 00,81, FF,01, FF,02, FF,03, FF,85" + obsoleted_ciphers="${obsoleted_ciphers}, 00,FF" ossl_good_ciphers='AESGCM:CHACHA20:CamelliaGCM:AESCCM:ARIAGCM:!kEECDH:!kEDH:!kDHE:!kDHEPSK:!kECDHEPSK:!aNULL' # grep AEAD etc/cipher-mapping.txt | egrep -v 'Au=None|TLS_ECDHE|TLS_DHE|TLS_PSK_DHE|TLSv1.3' @@ -5941,21 +5991,21 @@ run_cipherlists() { # argv[9]: CVE # argv[10]: CWE - sub_cipherlists "$ossl_null_ciphers" "" " NULL ciphers (no encryption) " 1 "NULL" "$null_ciphers" "$sslv2_null_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_null_ciphers" "" " NULL ciphers (no encryption) " 1 "NULL" "$null_ciphers" "$sslv2_null_ciphers" "$using_sockets" "$cve" "$cwe" ret=$? - sub_cipherlists "$ossl_anon_ciphers" "" " Anonymous NULL Ciphers (no authentication) " 1 "aNULL" "$anon_ciphers" "$sslv2_anon_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_anon_ciphers" "" " Anonymous NULL Ciphers (no authentication) " 1 "aNULL" "$anon_ciphers" "$sslv2_anon_ciphers" "$using_sockets" "$cve" "$cwe" ret=$((ret + $?)) - sub_cipherlists "$ossl_exp_ciphers" "" " Export ciphers (w/o ADH+NULL) " 1 "EXPORT" "$exp_ciphers" "$sslv2_exp_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_exp_ciphers" "" " Export ciphers (w/o ADH+NULL) " 1 "EXPORT" "$exp_ciphers" "$sslv2_exp_ciphers" "$using_sockets" "$cve" "$cwe" ret=$((ret + $?)) - sub_cipherlists "$ossl_low_ciphers" "" " LOW: 64 Bit + DES, RC[2,4], MD5 (w/o export) " 2 "LOW" "$low_ciphers" "$sslv2_low_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_low_ciphers" "" " LOW: 64 Bit + DES, RC[2,4], MD5 (w/o export) " 2 "LOW" "$low_ciphers" "$sslv2_low_ciphers" "$using_sockets" "$cve" "$cwe" ret=$((ret + $?)) - sub_cipherlists "$ossl_tdes_ciphers" "" " Triple DES Ciphers / IDEA " 3 "3DES_IDEA" "$tdes_ciphers" "$sslv2_tdes_ciphers" "$using_sockets" "$cve" "$cwe2" + sub_cipherlists "$ossl_tdes_ciphers" "" " Triple DES Ciphers / IDEA " 3 "3DES_IDEA" "$tdes_ciphers" "$sslv2_tdes_ciphers" "$using_sockets" "$cve" "$cwe2" ret=$((ret + $?)) - sub_cipherlists "$ossl_average_ciphers" "" " Obsolete: SEED + 128+256 Bit CBC cipher " 4 "AVERAGE" "$average_ciphers" "" "$using_sockets" "$cve" "$cwe2" + sub_cipherlists "$ossl_obsoleted_ciphers" "" " Obsoleted CBC ciphers (AES, ARIA etc.) " 4 "AVERAGE" "$obsoleted_ciphers" "" "$using_sockets" "$cve" "$cwe2" ret=$((ret + $?)) - sub_cipherlists "$ossl_good_ciphers" "" " non-FS Strong encryption (AEAD ciphers) " 6 "GOOD" "$good_ciphers" "" "$using_sockets" "" "" + sub_cipherlists "$ossl_good_ciphers" "" " Strong encryption (AEAD ciphers) with no FS " 6 "GOOD" "$good_ciphers" "" "$using_sockets" "" "" ret=$((ret + $?)) - sub_cipherlists "$ossl_strong_ciphers" ALL " FS + Strong encryption (AEAD ciphers) " 7 "STRONG" "$strong_ciphers" "" "$using_sockets" "" "" + sub_cipherlists "$ossl_strong_ciphers" 'ALL' " Forward Secrecy strong encryption (AEAD ciphers)" 7 "STRONG" "$strong_ciphers" "" "$using_sockets" "" "" ret=$((ret + $?)) outln @@ -6290,7 +6340,7 @@ sub_session_resumption() { fi fi "$CLIENT_AUTH" && return 6 - if "$HAS_NO_SSL2"; then + if ! "$HAS_TLS13" && "$HAS_NO_SSL2"; then addcmd+=" -no_ssl2" else protocol=${protocol/\./_} @@ -6371,7 +6421,7 @@ sub_session_resumption() { run_server_preference() { local cipher1="" cipher2="" tls13_cipher1="" tls13_cipher2="" default_proto="" - local prev_cipher="" default_cipher="" + local default_cipher="" local limitedsense="" supported_sslv2_ciphers local -a offered_cipher offered_proto local proto_ossl proto_txt proto_hex cipherlist i @@ -6392,7 +6442,7 @@ run_server_preference() { "$SSL_NATIVE" && using_sockets=false outln - pr_headlineln " Testing server preferences " + pr_headlineln " Testing server's cipher preferences " outln pr_bold " Has server cipher order? " @@ -6417,14 +6467,14 @@ run_server_preference() { "ephemeralkey" sclient_success=$? if [[ $sclient_success -eq 0 ]]; then - add_tls_offered tls1_3 yes + add_proto_offered tls1_3 yes elif [[ $sclient_success -eq 2 ]]; then sclient_success=0 # 2: downgraded case $DETECTED_TLS_VERSION in - 0303) add_tls_offered tls1_2 yes ;; - 0302) add_tls_offered tls1_1 yes ;; - 0301) add_tls_offered tls1 yes ;; - 0300) add_tls_offered ssl3 yes ;; + 0303) add_proto_offered tls1_2 yes ;; + 0302) add_proto_offered tls1_1 yes ;; + 0301) add_proto_offered tls1 yes ;; + 0300) add_proto_offered ssl3 yes ;; esac fi if [[ $sclient_success -eq 0 ]] ; then @@ -6552,12 +6602,12 @@ run_server_preference() { fileout "$jsonID" "OK" "Default protocol TLS1.2" ;; *TLSv1.1) - prln_svrty_good $default_proto - fileout "$jsonID" "OK" "Default protocol TLS1.1" + prln_svrty_low $default_proto + fileout "$jsonID" "LOW" "Default protocol TLS1.1" ;; *TLSv1) - outln $default_proto - fileout "$jsonID" "INFO" "Default protocol TLS1.0" + prln_svrty_low $default_proto + fileout "$jsonID" "LOW" "Default protocol TLS1.0" ;; *SSLv2) prln_svrty_critical $default_proto @@ -6637,131 +6687,41 @@ run_server_preference() { outln "$limitedsense" fi - if "$has_cipher_order"; then - "$FAST" && using_sockets=false - [[ $TLS_NR_CIPHERS == 0 ]] && using_sockets=false + "$FAST" && using_sockets=false + [[ $TLS_NR_CIPHERS == 0 ]] && using_sockets=false - pr_bold " Cipher order" - "$WIDE" && outln && neat_header - while read proto_ossl proto_hex proto_txt; do - [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" && continue - "$WIDE" && pr_underline "$proto_txt" - cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" - done <<< "$(tm_out " ssl3 00 SSLv3\n tls1 01 TLSv1\n tls1_1 02 TLSv1.1\n tls1_2 03 TLSv1.2\n tls1_3 04 TLSv1.3\n")" - outln - outln - else - pr_bold " Negotiated cipher per proto"; outln " $limitedsense" - i=1 - for proto_ossl in ssl2 ssl3 tls1 tls1_1 tls1_2 tls1_3; do - if [[ $proto_ossl == ssl2 ]] && ! "$HAS_SSL2"; then - if ! "$using_sockets" || [[ $TLS_NR_CIPHERS -eq 0 ]]; then - out " (SSLv2: "; pr_local_problem "$OPENSSL doesn't support \"s_client -ssl2\""; outln ")"; - continue - else - sslv2_sockets "" "true" - if [[ $? -eq 3 ]] && [[ "$V2_HELLO_CIPHERSPEC_LENGTH" -ne 0 ]]; then - # Just arbitrarily pick the first cipher in the cipher-mapping.txt list. - offered_proto[i]="SSLv2" - supported_sslv2_ciphers="$(grep "Supported cipher: " "$TEMPDIR/$NODEIP.parse_sslv2_serverhello.txt")" - for (( j=0; j < TLS_NR_CIPHERS; j++ )); do - if [[ "${TLS_CIPHER_SSLVERS[j]}" == "SSLv2" ]]; then - cipher1="${TLS_CIPHER_HEXCODE[j]}" - cipher1="$(tolower "x${cipher1:2:2}${cipher1:7:2}${cipher1:12:2}")" - if [[ "$supported_sslv2_ciphers" =~ $cipher1 ]]; then - if ( [[ "$DISPLAY_CIPHERNAMES" =~ openssl ]] && [[ "${TLS_CIPHER_OSSL_NAME[j]}" != "-" ]] ) || [[ "${TLS_CIPHER_RFC_NAME[j]}" == "-" ]]; then - offered_cipher[i]="${TLS_CIPHER_OSSL_NAME[j]}" - else - offered_cipher[i]="${TLS_CIPHER_RFC_NAME[j]}" - fi - break - fi - fi - done - [[ $DEBUG -ge 2 ]] && tmln_out "Default cipher for ${offered_proto[i]}: ${offered_cipher[i]}" - else - offered_proto[i]="" - offered_cipher[i]="" - fi - fi - elif ( [[ $proto_ossl == ssl3 ]] && ! "$HAS_SSL3" ) || ( [[ $proto_ossl == tls1_3 ]] && ! "$HAS_TLS13" ); then - if [[ $proto_ossl == ssl3 ]]; then - proto_txt="SSLv3" ; proto_hex="00" ; cipherlist="$TLS_CIPHER" - else - proto_txt="TLSv1.3" ; proto_hex="04" ; cipherlist="$TLS13_CIPHER" - fi - if ! "$using_sockets"; then - out " ($proto_txt: "; pr_local_problem "$OPENSSL doesn't support \"s_client -$proto_ossl\"" ; outln ")"; - continue - else - tls_sockets "$proto_hex" "$cipherlist" - if [[ $? -eq 0 ]]; then - offered_proto[i]="$proto_txt" - cipher1=$(get_cipher "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt") - offered_cipher[i]="$cipher1" - if [[ "$DISPLAY_CIPHERNAMES" =~ openssl ]] && [[ $TLS_NR_CIPHERS -ne 0 ]]; then - offered_cipher[i]="$(rfc2openssl "$cipher1")" - [[ -z "${offered_cipher[i]}" ]] && offered_cipher[i]="$cipher1" - fi - [[ $DEBUG -ge 2 ]] && tmln_out "Default cipher for ${offered_proto[i]}: ${offered_cipher[i]}" - else - offered_proto[i]="" - offered_cipher[i]="" - fi - fi + pr_bold " Cipher per protocol" + outln "\n" && neat_header + while read proto_ossl proto_hex proto_txt; do + pr_underline "$(printf -- "%b" "$proto_txt")" + # TODO: If there's no cipher we should consider not displaying the text in the round brackets) + # the following takes care of that but only if we know the protocol is not supported + if [[ $(has_server_protocol "$proto_ossl") -eq 1 ]]; then + outln "\n - " + continue + fi + # TODO: Also the fact that a protocol is not supported seems not to be saved by cipher_pref_check() + # (./testssl.sh --wide -p -P -E vs ./testssl.sh --wide -P -E ) + if [[ $proto_ossl == ssl2 ]] || \ + ( [[ $proto_ossl != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ + ( [[ $proto_ossl == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then + if [[ $proto_ossl == ssl2 ]]; then + outln " (listed by strength)" + elif [[ $proto_ossl == tls1_3 ]]; then + outln " (no server order, thus listed by strength)" else - $OPENSSL s_client $(s_client_options "$STARTTLS -"$proto_ossl" $BUGS -connect $NODEIP:$PORT $PROXY $SNI") </dev/null 2>>$ERRFILE >$TMPFILE - if sclient_connect_successful $? $TMPFILE; then - offered_proto[i]=$(get_protocol $TMPFILE) - offered_cipher[i]=$(get_cipher $TMPFILE) - [[ ${offered_cipher[i]} == "0000" ]] && offered_cipher[i]="" # Hack! - if [[ "$DISPLAY_CIPHERNAMES" =~ rfc ]] && [[ -n "${offered_cipher[i]}" ]]; then - offered_cipher[i]="$(openssl2rfc "${offered_cipher[i]}")" - [[ -z "${offered_cipher[i]}" ]] && offered_cipher[i]=$(get_cipher $TMPFILE) - fi - [[ $DEBUG -ge 2 ]] && tmln_out "Default cipher for ${offered_proto[i]}: ${offered_cipher[i]}" - else - offered_proto[i]="" - offered_cipher[i]="" - fi + prln_svrty_high " (no server order, thus listed by strength)" fi - [[ -n "${offered_cipher[i]}" ]] && add_tls_offered "$proto_ossl" yes - i=$((i + 1)) - done + ciphers_by_strength "-$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "true" + else + outln " (server order)" + cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "true" + fi + done <<< "$(tm_out " ssl2 22 SSLv2\n ssl3 00 SSLv3\n tls1 01 TLSv1\n tls1_1 02 TLSv1.1\n tls1_2 03 TLSv1.2\n tls1_3 04 TLSv1.3\n")" + outln - for i in 1 2 3 4 5 6; do - if [[ -n "${offered_cipher[i]}" ]]; then # cipher not empty - if [[ -z "$prev_cipher" ]] || [[ "$prev_cipher" != "${offered_cipher[i]}" ]]; then - [[ -n "$prev_cipher" ]] && outln - str_len=${#offered_cipher[i]} - out " " - if [[ "$COLOR" -le 2 ]]; then - out "${offered_cipher[i]}" - else - pr_cipher_quality "${offered_cipher[i]}" - fi - out ":" - if [[ "$DISPLAY_CIPHERNAMES" =~ openssl ]]; then - for (( 1; str_len < 30; str_len++ )); do - out " " - done - else - for (( 1; str_len < 51; str_len++ )); do - out " " - done - fi - else - out ", " # same cipher --> only print out protocol behind it - fi - out "${offered_proto[i]}" - prev_cipher="${offered_cipher[i]}" - fi - fileout "cipher_order_${offered_proto[i]}" "INFO" "${offered_cipher[i]} at ${offered_proto[i]} $limitedsense" - done - outln "\n No further cipher order check has been done as order is determined by the client" - outln - fi return $ret + # end of run_server_preference() } # arg1: true if the list that is returned does not need to be ordered by preference. @@ -6842,10 +6802,11 @@ check_tls12_pref() { return 0 } - +# At the moment only called from run_server_preference() cipher_pref_check() { - local p="$1" proto_hex="$2" proto="$3" + local proto="$1" proto_hex="$2" proto_text="$3" local using_sockets="$4" + local wide="$5" # at the moment always = true local tested_cipher cipher order rfc_cipher rfc_order local overflow_probe_cipherlist="ALL:-ECDHE-RSA-AES256-GCM-SHA384:-AES128-SHA:-DES-CBC3-SHA" local -i i nr_ciphers nr_nonossl_ciphers num_bundles bundle_size bundle end_of_bundle success @@ -6857,27 +6818,25 @@ cipher_pref_check() { local ciphers_found_with_sockets order=""; ciphers_found_with_sockets=false - if [[ $p == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then + if [[ $proto == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then out "\n SSLv3: "; pr_local_problem "$OPENSSL doesn't support \"s_client -ssl3\""; return 0 fi - if [[ $p == tls1_3 ]] && ! "$HAS_TLS13" && ! "$using_sockets"; then + if [[ $proto == tls1_3 ]] && ! "$HAS_TLS13" && ! "$using_sockets"; then out "\n TLSv1.3 "; pr_local_problem "$OPENSSL doesn't support \"s_client -tls1_3\""; return 0 fi - [[ $(has_server_protocol "$p") -eq 1 ]] && return 0 - - if ( [[ $p != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $p != ssl3 ]] || "$HAS_SSL3" ); then - if [[ $p == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then - order="$(check_tls12_pref "$WIDE")" + if ( [[ $proto != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $proto != ssl3 ]] || "$HAS_SSL3" ); then + if [[ $proto == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then + order="$(check_tls12_pref "$wide")" [[ "${order:0:1}" == \ ]] && order="${order:1}" ciphers_found="$order" fi - if "$WIDE" || [[ -z "$order" ]]; then + if "$wide" || [[ -z "$order" ]]; then tested_cipher=""; order=""; nr_ciphers_found=0 while true; do - if [[ $p != tls1_3 ]]; then + if [[ $proto != tls1_3 ]]; then if [[ -n "$ciphers_found" ]]; then ciphers_to_test="" for cipher in $ciphers_found; do @@ -6896,14 +6855,14 @@ cipher_pref_check() { [[ -z "$ciphers_to_test" ]] && break ciphers_to_test="-ciphersuites ${ciphers_to_test:1}" fi - $OPENSSL s_client $(s_client_options "$STARTTLS -"$p" $BUGS $ciphers_to_test -connect $NODEIP:$PORT $PROXY $SNI") </dev/null 2>>$ERRFILE >$TMPFILE + $OPENSSL s_client $(s_client_options "$STARTTLS -"$proto" $BUGS $ciphers_to_test -connect $NODEIP:$PORT $PROXY $SNI") </dev/null 2>>$ERRFILE >$TMPFILE sclient_connect_successful $? $TMPFILE || break cipher=$(get_cipher $TMPFILE) [[ -z "$cipher" ]] && break order+="$cipher " tested_cipher+=":-"$cipher "$FAST" && break - if "$WIDE"; then + if "$wide"; then for (( i=0; i < TLS_NR_CIPHERS; i++ )); do [[ "$cipher" == ${TLS_CIPHER_OSSL_NAME[i]} ]] && break done @@ -6911,7 +6870,7 @@ cipher_pref_check() { normalized_hexcode[nr_ciphers_found]="$(normalize_ciphercode "${TLS_CIPHER_HEXCODE[i]}")" ciph[nr_ciphers_found]="${TLS_CIPHER_OSSL_NAME[i]}" kx[nr_ciphers_found]="${TLS_CIPHER_KX[i]}" - [[ "$p" == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file $TMPFILE)" + [[ $proto == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file $TMPFILE)" if ( [[ ${kx[nr_ciphers_found]} == Kx=ECDH ]] || [[ ${kx[nr_ciphers_found]} == Kx=DH ]] || [[ ${kx[nr_ciphers_found]} == Kx=EDH ]] ); then kx[nr_ciphers_found]+=" $(read_dhbits_from_file "$TMPFILE" quiet)" fi @@ -6940,9 +6899,9 @@ cipher_pref_check() { rfc_ciph[nr_nonossl_ciphers]="${TLS_CIPHER_RFC_NAME[i]}" index[nr_nonossl_ciphers]=$i # Only test ciphers that are relevant to the protocol. - if [[ "$p" == tls1_3 ]]; then - [[ "${hexc:2:2}" == "13" ]] && nr_nonossl_ciphers+=1 - elif [[ "$p" == tls1_2 ]]; then + if [[ $proto == tls1_3 ]]; then + [[ "${hexc:2:2}" == 13 ]] && nr_nonossl_ciphers+=1 + elif [[ $proto == tls1_2 ]]; then [[ "${hexc:2:2}" != 13 ]] && nr_nonossl_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && \ [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ @@ -6957,7 +6916,7 @@ cipher_pref_check() { if [[ $nr_nonossl_ciphers -eq 0 ]]; then num_bundles=0 - elif [[ $p != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then + elif [[ $proto != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then num_bundles=1 bundle_size=$nr_nonossl_ciphers else @@ -6986,7 +6945,7 @@ cipher_pref_check() { i=${index[i]} ciphers_found[i]=true ciphers_found_with_sockets=true - if [[ $p != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then + if [[ $proto != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then # Throw out the results found so far and start over using just sockets bundle=$num_bundles for (( i=0; i < TLS_NR_CIPHERS; i++ )); do @@ -7009,9 +6968,9 @@ cipher_pref_check() { ciphers_found2[nr_ciphers]=false hexcode[nr_ciphers]="${hexc:2:2},${hexc:7:2}" rfc_ciph[nr_ciphers]="${TLS_CIPHER_RFC_NAME[i]}" - if [[ "$p" == "tls1_3" ]]; then + if [[ $proto == tls1_3 ]]; then [[ "${hexc:2:2}" == "13" ]] && nr_ciphers+=1 - elif [[ "$p" == "tls1_2" ]]; then + elif [[ $proto == tls1_2 ]]; then [[ "${hexc:2:2}" != "13" ]] && nr_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && \ [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ @@ -7033,7 +6992,7 @@ cipher_pref_check() { for (( i=0; i < nr_ciphers; i++ )); do [[ "$cipher" == ${rfc_ciph[i]} ]] && ciphers_found2[i]=true && break done - if "$WIDE"; then + if "$wide"; then for (( i=0; i < TLS_NR_CIPHERS; i++ )); do [[ "$cipher" == ${TLS_CIPHER_RFC_NAME[i]} ]] && break done @@ -7041,7 +7000,7 @@ cipher_pref_check() { normalized_hexcode[nr_ciphers_found]="$(normalize_ciphercode "${TLS_CIPHER_HEXCODE[i]}")" ciph[nr_ciphers_found]="${TLS_CIPHER_OSSL_NAME[i]}" kx[nr_ciphers_found]="${TLS_CIPHER_KX[i]}" - [[ "$p" == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" + [[ $proto == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" if ( [[ ${kx[nr_ciphers_found]} == Kx=ECDH ]] || [[ ${kx[nr_ciphers_found]} == Kx=DH ]] || [[ ${kx[nr_ciphers_found]} == Kx=EDH ]] ); then kx[nr_ciphers_found]+=" $(read_dhbits_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" quiet)" fi @@ -7072,29 +7031,32 @@ cipher_pref_check() { order="$rfc_order" fi - "$WIDE" && outln if [[ -n "$order" ]]; then - add_tls_offered "$p" yes - if "$WIDE"; then + add_proto_offered "$proto" yes + if "$wide"; then for (( i=0 ; i<nr_ciphers_found; i++ )); do neat_list "${normalized_hexcode[i]}" "${ciph[i]}" "${kx[i]}" "${enc[i]}" "${export2[i]}" "true" outln "${sigalg[i]}" - id="cipher-${p}_${normalized_hexcode[i]}" - fileout "$id" "INFO" "$proto $(neat_list "${normalized_hexcode[i]}" "${ciph[i]}" "${kx[i]}" "${enc[i]}" "${export2[i]}")" + id="cipher-${proto}_${normalized_hexcode[i]}" + fileout "$id" "INFO" "$proto_text $(neat_list "${normalized_hexcode[i]}" "${ciph[i]}" "${kx[i]}" "${enc[i]}" "${export2[i]}")" done else outln - out "$(printf " %-10s " "$proto: ")" + out "$(printf " %-10s " "$proto_text: ")" if [[ "$COLOR" -le 2 ]]; then out "$(out_row_aligned_max_width "$order" " " $TERM_WIDTH)" else out_row_aligned_max_width_by_entry "$order" " " $TERM_WIDTH pr_cipher_quality fi fi - fileout "cipherorder_${proto//./_}" "INFO" "$order" + fileout "cipherorder_${proto_text//./_}" "INFO" "$order" + else + # Order doesn't contain any ciphers, so we can safely unset the protocol and put a dash out + add_proto_offered "$proto" no + outln " -" fi - tmpfile_handle ${FUNCNAME[0]}-$p.txt + tmpfile_handle ${FUNCNAME[0]}-$proto.txt return 0 } @@ -7602,7 +7564,7 @@ get_server_certificate() { [[ $success -eq 0 ]] || return 1 cp "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" $TMPFILE fi - [[ $success -eq 0 ]] && add_tls_offered tls1_3 yes + [[ $success -eq 0 ]] && add_proto_offered tls1_3 yes extract_new_tls_extensions $TMPFILE tmpfile_handle ${FUNCNAME[0]}.txt return $success @@ -9779,7 +9741,7 @@ run_fs() { tls13_ciphers_to_test="" for (( i=0; i < nr_supported_ciphers; i++ )); do if ! "${ciphers_found[i]}" && "${ossl_supported[i]}"; then - if [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]]; then + if [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || [[ "${ciph[i]}" == AEAD-* ]]; then tls13_ciphers_to_test+=":${ciph[i]}" else ciphers_to_test+=":${ciph[i]}" @@ -9796,7 +9758,7 @@ run_fs() { done [[ $i -eq $nr_supported_ciphers ]] && break ciphers_found[i]=true - if [[ "$fs_cipher" == TLS13* ]] || [[ "$fs_cipher" == TLS_* ]]; then + if [[ "$fs_cipher" == TLS13* ]] || [[ "$fs_cipher" == TLS_* ]] || [[ "$fs_cipher" == AEAD-* ]]; then fs_tls13_offered=true "$WIDE" && kx[i]="$(read_dhtype_from_file $TMPFILE)" fi @@ -9855,11 +9817,12 @@ run_fs() { fi fs_ciphers+="$fs_cipher " - if [[ "${ciph[i]}" == ECDHE-* ]] || [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || ( "$using_sockets" && [[ "${rfc_ciph[i]}" == TLS_ECDHE_* ]] ); then + if [[ "${ciph[i]}" == ECDHE-* ]] || [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || \ + [[ "${ciph[i]}" == AEAD-* ]] || ( "$using_sockets" && [[ "${rfc_ciph[i]}" == TLS_ECDHE_* ]] ); then ecdhe_offered=true ecdhe_cipher_list_hex+=", ${hexcode[i]}" if [[ "${ciph[i]}" != "-" ]]; then - if [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]]; then + if [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || [[ "${ciph[i]}" == AEAD-* ]]; then tls13_cipher_list+=":$fs_cipher" else ecdhe_cipher_list+=":$fs_cipher" @@ -9869,7 +9832,7 @@ run_fs() { if [[ "${ciph[i]}" == "DHE-"* ]] || ( "$using_sockets" && [[ "${rfc_ciph[i]}" == "TLS_DHE_"* ]] ); then ffdhe_offered=true ffdhe_cipher_list_hex+=", ${hexcode[i]}" - elif [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]]; then + elif [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || [[ "${ciph[i]}" == AEAD-* ]]; then ffdhe_cipher_list_hex+=", ${hexcode[i]}" fi fi @@ -13835,8 +13798,8 @@ parse_tls_serverhello() { esac [[ -z "$key_bitstring" ]] && named_curve=0 && named_curve_str="" if "$HAS_PKEY" && [[ $named_curve -ne 0 ]] && [[ "${TLS13_KEY_SHARES[named_curve]}" =~ BEGIN ]]; then - ephemeral_param="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | grep -A 1000 "prime:")" - rfc7919_param="$($OPENSSL pkey -text -noout 2>>$ERRFILE <<< "${TLS13_KEY_SHARES[named_curve]}" | grep -A 1000 "prime:")" + ephemeral_param="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | grep -EA 1000 "prime:|prime P:")" + rfc7919_param="$($OPENSSL pkey -text -noout 2>>$ERRFILE <<< "${TLS13_KEY_SHARES[named_curve]}" | grep -EA 1000 "prime:|prime P:")" [[ "$ephemeral_param" != "$rfc7919_param" ]] && named_curve_str="" fi @@ -14028,26 +13991,13 @@ generate_key_share_extension() { # with X25519 keys, so don't include the X25519 key share # if the server's response needs to be decrypted and an # older version of OpenSSL is being used. - if [[ $i -gt 12 ]] && [[ $group -eq 29 ]] && [[ "$2" == all ]]; then - [[ "$OSSL_NAME" =~ LibreSSL ]] && continue - if [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 1.1.0* ]] && \ - [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 1.1.1* ]] && \ - [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 3.0.0* ]]; then - continue - fi - fi + [[ $i -gt 12 ]] && [[ $group -eq 29 ]] && [[ "$2" == all ]] && ! "$HAS_X25519" && continue # Versions of OpenSSL prior to 1.1.1 cannot perform operations # with X448 keys, so don't include the X448 key share # if the server's response needs to be decrypted and an # older version of OpenSSL is being used. - if [[ $i -gt 12 ]] && [[ $group -eq 30 ]] && [[ "$2" == all ]]; then - [[ "$OSSL_NAME" =~ LibreSSL ]] && continue - if [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 1.1.1* ]] && \ - [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 3.0.0* ]]; then - continue - fi - fi + [[ $i -gt 12 ]] && [[ $group -eq 30 ]] && [[ "$2" == all ]] && ! "$HAS_X448" && continue # NOTE: The public keys could be extracted from the private keys # (TLS13_KEY_SHARES) using $OPENSSL, but only OpenSSL 1.1.0 and newer can @@ -14209,9 +14159,7 @@ prepare_tls_clienthello() { 00, 01, 00, 02, 00, 03, 00, 0f, 00, 10, 00, 11" elif [[ 0x$tls_low_byte -gt 0x03 ]]; then # Supported Groups Extension - if [[ ! "$process_full" =~ all ]] || ( [[ ! "$OSSL_NAME" =~ LibreSSL ]] && \ - ( [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 1.1.1* ]] || \ - [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 3.0.0* ]] ) ); then + if [[ ! "$process_full" =~ all ]] || ( "$HAS_X25519" && "$HAS_X448" ); then extension_supported_groups=" 00,0a, # Type: Supported Groups, see RFC 8446 00,10, 00,0e, # lengths @@ -14220,13 +14168,13 @@ prepare_tls_clienthello() { # OpenSSL prior to 1.1.1 does not support X448, so list it as the least # preferred option if the response needs to be decrypted, and do not # list it at all if the response MUST be decrypted. - elif [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 1.1.0* ]] && [[ "$process_full" == all+ ]]; then + elif "$HAS_X25519" && [[ "$process_full" == all+ ]]; then extension_supported_groups=" 00,0a, # Type: Supported Groups, see RFC 8446 00,0e, 00,0c, # lengths 00,1d, 00,17, 00,18, 00,19, 01,00, 01,01" - elif [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == "1.1.0"* ]]; then + elif "$HAS_X25519"; then extension_supported_groups=" 00,0a, # Type: Supported Groups, see RFC 8446 00,10, 00,0e, # lengths @@ -15074,10 +15022,10 @@ run_heartbleed(){ else # no protocol for some reason defined, determine TLS versions offered with a new handshake $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY") >$TMPFILE 2>$ERRFILE </dev/null case "$(get_protocol $TMPFILE)" in - *1.2) tls_hexcode="x03, x03" ; add_tls_offered tls1_2 yes ;; - *1.1) tls_hexcode="x03, x02" ; add_tls_offered tls1_1 yes ;; - TLSv1) tls_hexcode="x03, x01" ; add_tls_offered tls1 yes ;; - SSLv3) tls_hexcode="x03, x00" ; add_tls_offered ssl3 yes ;; + *1.2) tls_hexcode="x03, x03" ; add_proto_offered tls1_2 yes ;; + *1.1) tls_hexcode="x03, x02" ; add_proto_offered tls1_1 yes ;; + TLSv1) tls_hexcode="x03, x01" ; add_proto_offered tls1 yes ;; + SSLv3) tls_hexcode="x03, x00" ; add_proto_offered ssl3 yes ;; esac fi debugme echo "using protocol $tls_hexcode" @@ -15176,10 +15124,10 @@ run_ccs_injection(){ else # no protocol for some reason defined, determine TLS versions offered with a new handshake $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY") >$TMPFILE 2>$ERRFILE </dev/null case "$(get_protocol $TMPFILE)" in - *1.2) tls_hexcode="x03, x03" ; add_tls_offered tls1_2 yes ;; - *1.1) tls_hexcode="x03, x02" ; add_tls_offered tls1_1 yes ;; - TLSv1) tls_hexcode="x03, x01" ; add_tls_offered tls1 yes ;; - SSLv3) tls_hexcode="x03, x00" ; add_tls_offered ssl3 yes ;; + *1.2) tls_hexcode="x03, x03" ; add_proto_offered tls1_2 yes ;; + *1.1) tls_hexcode="x03, x02" ; add_proto_offered tls1_1 yes ;; + TLSv1) tls_hexcode="x03, x01" ; add_proto_offered tls1 yes ;; + SSLv3) tls_hexcode="x03, x00" ; add_proto_offered ssl3 yes ;; esac fi debugme echo "using protocol $tls_hexcode" @@ -15329,13 +15277,14 @@ run_ccs_injection(){ } sub_session_ticket_tls() { + local tls_proto="$1" local sessticket_tls="" #FIXME: we likely have done this already before (either @ run_server_defaults() or at least the output # from a previous handshake) --> would save 1x connect. We have TLS_TICKET but not yet the ticket itself #FIXME #ATTENTION: we DO NOT use SNI here as we assume ticketbleed is a vulnerability of the TLS stack. If we'd do SNI here, we'd also need # it in the ClientHello of run_ticketbleed() otherwise the ticket will be different and the whole thing won't work! # - sessticket_tls="$($OPENSSL s_client $(s_client_options "$BUGS $OPTIMAL_PROTO $PROXY -connect $NODEIP:$PORT") </dev/null 2>$ERRFILE | awk '/TLS session ticket:/,/^$/' | awk '!/TLS session ticket/')" + sessticket_tls="$($OPENSSL s_client $(s_client_options "$BUGS $tls_proto $PROXY -connect $NODEIP:$PORT") </dev/null 2>$ERRFILE | awk '/TLS session ticket:/,/^$/' | awk '!/TLS session ticket/')" sessticket_tls="$(sed -e 's/^.* - /x/g' -e 's/ .*$//g' <<< "$sessticket_tls" | tr '\n' ',')" sed -e 's/ /,x/g' -e 's/-/,x/g' <<< "$sessticket_tls" @@ -15344,6 +15293,7 @@ sub_session_ticket_tls() { # see https://blog.filippo.io/finding-ticketbleed/ | https://filippo.io/ticketbleed/ run_ticketbleed() { + local tls_hexcode tls_proto="" local session_tckt_tls="" local -i len_ch=300 # fixed len of prepared clienthello below local sid="x00,x0B,xAD,xC0,xDE,x00," # some abitratry bytes @@ -15381,25 +15331,26 @@ run_ticketbleed() { fi if [[ 0 -eq $(has_server_protocol tls1) ]]; then - tls_hexcode="x03, x01" + tls_hexcode="x03, x01"; tls_proto="-tls1" elif [[ 0 -eq $(has_server_protocol tls1_1) ]]; then - tls_hexcode="x03, x02" + tls_hexcode="x03, x02"; tls_proto="-tls1_1" elif [[ 0 -eq $(has_server_protocol tls1_2) ]]; then - tls_hexcode="x03, x03" + tls_hexcode="x03, x03"; tls_proto="-tls1_2" elif [[ 0 -eq $(has_server_protocol ssl3) ]]; then - tls_hexcode="x03, x00" + tls_hexcode="x03, x00"; tls_proto="-ssl3" else # no protocol for some reason defined, determine TLS versions offered with a new handshake - $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY") >$TMPFILE 2>$ERRFILE </dev/null + "$HAS_TLS13" && tls_proto="-no_tls1_3" + $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS $tls_proto -connect $NODEIP:$PORT $PROXY") >$TMPFILE 2>$ERRFILE </dev/null case "$(get_protocol $TMPFILE)" in - *1.2) tls_hexcode="x03, x03" ; add_tls_offered tls1_2 yes ;; - *1.1) tls_hexcode="x03, x02" ; add_tls_offered tls1_1 yes ;; - TLSv1) tls_hexcode="x03, x01" ; add_tls_offered tls1 yes ;; - SSLv3) tls_hexcode="x03, x00" ; add_tls_offered ssl3 yes ;; + *1.2) tls_hexcode="x03, x03"; tls_proto="-tls1_2" ; add_proto_offered tls1_2 yes ;; + *1.1) tls_hexcode="x03, x02"; tls_proto="-tls1_1" ; add_proto_offered tls1_1 yes ;; + TLSv1) tls_hexcode="x03, x01"; tls_proto="-tls1" ; add_proto_offered tls1 yes ;; + SSLv3) tls_hexcode="x03, x00"; tls_proto="-ssl3" ; add_proto_offered ssl3 yes ;; esac fi debugme echo "using protocol $tls_hexcode" - session_tckt_tls="$(sub_session_ticket_tls)" + session_tckt_tls="$(sub_session_ticket_tls "$tls_proto")" if [[ "$session_tckt_tls" == "," ]]; then pr_svrty_best "not vulnerable (OK)" outln ", no session tickets" @@ -15986,7 +15937,7 @@ run_sweet32() { sslv2_sockets "$ssl2_sweet32_ciphers_hex" case $? in 3) ssl2_sweet=true - add_tls_offered ssl2 yes ;; + add_proto_offered ssl2 yes ;; 0) ;; # ssl2_sweet=false 1|4|6|7) debugme "${FUNCNAME[0]}: test problem we don't handle here" ;; @@ -16027,7 +15978,7 @@ run_sweet32() { sclient_connect_successful $? $TMPFILE if [[ $? -eq 0 ]]; then ssl2_sweet=true - add_tls_offered ssl2 yes + add_proto_offered ssl2 yes fi fi else @@ -16468,7 +16419,7 @@ get_common_prime() { local -i lineno_matched=0 "$HAS_PKEY" || return 2 - dh_p="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | awk '/prime:/,/generator:/' | grep -Ev "prime|generator")" + dh_p="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | awk '/prime:|prime P:/,/generator:|generator G:/' | grep -Ev "prime|generator")" dh_p="$(strip_spaces "$(colon_to_spaces "$(newline_to_spaces "$dh_p")")")" [[ "${dh_p:0:2}" == "00" ]] && dh_p="${dh_p:2}" DH_GROUP_LEN_P="$((4*${#dh_p}))" @@ -16579,15 +16530,13 @@ run_logjam() { tls_sockets "03" "$exportdh_cipher_list_hex, 00,ff" sclient_success=$? [[ $sclient_success -eq 2 ]] && sclient_success=0 + [[ $sclient_success -eq 0 ]] && vuln_exportdh_ciphers=true elif [[ $nr_supported_ciphers -ne 0 ]]; then $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -cipher $exportdh_cipher_list -connect $NODEIP:$PORT $PROXY $SNI") >$TMPFILE 2>$ERRFILE </dev/null sclient_connect_successful $? $TMPFILE - sclient_success=$? + [[ $? -eq 0 ]] && vuln_exportdh_ciphers=true debugme grep -Ea "error|failure" $ERRFILE | grep -Eav "unable to get local|verify error" fi - [[ $sclient_success -eq 0 ]] && \ - vuln_exportdh_ciphers=true || \ - vuln_exportdh_ciphers=false if [[ $DEBUG -ge 2 ]]; then if "$using_sockets"; then @@ -16765,7 +16714,7 @@ run_drown() { 3) # vulnerable, [[ -n "$cert_fingerprint_sha2" ]] test is not needed as we should have RSA certificate here lines=$(count_lines "$(hexdump -C "$TEMPDIR/$NODEIP.sslv2_sockets.dd" 2>/dev/null)") debugme tm_out " ($lines lines) " - add_tls_offered ssl2 yes + add_proto_offered ssl2 yes if [[ "$lines" -gt 1 ]]; then nr_ciphers_detected=$((V2_HELLO_CIPHERSPEC_LENGTH / 3)) if [[ 0 -eq "$nr_ciphers_detected" ]]; then @@ -16895,7 +16844,7 @@ run_beast(){ $OPENSSL s_client $(s_client_options "-state -"${proto}" $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI") 2>>$ERRFILE >$TMPFILE </dev/null if sclient_connect_successful $? $TMPFILE; then higher_proto_supported+=" $(get_protocol $TMPFILE)" - add_tls_offered "$proto" yes + add_proto_offered "$proto" yes fi fi done @@ -16934,7 +16883,7 @@ run_beast(){ continue # protocol not supported, so we do not need to check each cipher with that protocol fi fi # protocol succeeded - add_tls_offered "$proto" yes + add_proto_offered "$proto" yes # now we test in one shot with the precompiled ciphers if "$using_sockets"; then @@ -18253,6 +18202,7 @@ find_openssl_binary() { HAS_CIPHERSUITES=false HAS_COMP=false HAS_NO_COMP=false + HAS_CURVES=false OSSL_SUPPORTED_CURVES="" HAS_PKEY=false HAS_PKUTIL=false @@ -18271,47 +18221,55 @@ find_openssl_binary() { HAS_AES256_GCM=false HAS_ZLIB=false - $OPENSSL ciphers -s 2>&1 | grep -aq "unknown option" || \ + $OPENSSL ciphers -s 2>&1 | grep -aiq "unknown option" || \ OSSL_CIPHERS_S="-s" # This and all other occurences we do a little trick using "invalid." to avoid plain and # link level DNS lookups. See issue #1418 and https://tools.ietf.org/html/rfc6761#section-6.4 - $OPENSSL s_client -ssl2 -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -ssl2 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_SSL2=true - $OPENSSL s_client -ssl3 -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -ssl3 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_SSL3=true - $OPENSSL s_client -tls1_3 -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -tls1_3 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_TLS13=true - $OPENSSL genpkey -algorithm X448 -out - 2>&1 | grep -aq "not found" || \ + $OPENSSL genpkey -algorithm X448 2>&1 | grep -aq "not found" || \ HAS_X448=true - $OPENSSL genpkey -algorithm X25519 -out - 2>&1 | grep -aq "not found" || \ + $OPENSSL genpkey -algorithm X25519 2>&1 | grep -aq "not found" || \ HAS_X25519=true - $OPENSSL s_client -no_ssl2 -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -no_ssl2 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_NO_SSL2=true - $OPENSSL s_client -noservername -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -noservername -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_NOSERVERNAME=true - $OPENSSL s_client -ciphersuites -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -ciphersuites -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_CIPHERSUITES=true - $OPENSSL s_client -comp -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -comp -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_COMP=true - $OPENSSL s_client -no_comp -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -no_comp -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_NO_COMP=true OPENSSL_NR_CIPHERS=$(count_ciphers "$(actually_supported_osslciphers 'ALL:COMPLEMENTOFALL' 'ALL')") - for curve in "${curves_ossl[@]}"; do - $OPENSSL s_client -curves $curve -connect invalid. 2>&1 | grep -Eiaq "Error with command|unknown option" - [[ $? -ne 0 ]] && OSSL_SUPPORTED_CURVES+=" $curve " - done + if $OPENSSL s_client -curves "${curves_ossl[0]}" -connect invalid. 2>&1 | grep -aiq "unknown option"; then + for curve in "${curves_ossl[@]}"; do + $OPENSSL s_client -groups $curve -connect invalid.:8443 2>&1 | grep -Eiaq "Error with command|unknown option|Failed to set groups" + [[ $? -ne 0 ]] && OSSL_SUPPORTED_CURVES+=" $curve " + done + else + HAS_CURVES=true + for curve in "${curves_ossl[@]}"; do + $OPENSSL s_client -curves $curve -connect invalid. 2>&1 | grep -Eiaq "Error with command|unknown option" + [[ $? -ne 0 ]] && OSSL_SUPPORTED_CURVES+=" $curve " + done + fi $OPENSSL pkey -help 2>&1 | grep -q Error || \ HAS_PKEY=true @@ -18642,6 +18600,7 @@ OSSL_VER_PLATFORM: $OSSL_VER_PLATFORM OPENSSL_NR_CIPHERS: $OPENSSL_NR_CIPHERS OPENSSL_CONF: $OPENSSL_CONF +HAS_CURVES: $HAS_CURVES OSSL_SUPPORTED_CURVES: $OSSL_SUPPORTED_CURVES HAS_IPv6: $HAS_IPv6 @@ -19533,16 +19492,16 @@ determine_optimal_sockets_params() { KEY_SHARE_EXTN_NR="33" tls_sockets "04" "$TLS13_CIPHER" "" "00, 2b, 00, 0f, 0e, 03,04, 7f,1c, 7f,1b, 7f,1a, 7f,19, 7f,18, 7f,17" if [[ $? -eq 0 ]]; then - add_tls_offered tls1_3 yes + add_proto_offered tls1_3 yes all_failed=false else KEY_SHARE_EXTN_NR="28" tls_sockets "04" "$TLS13_CIPHER" "" "00, 2b, 00, 0b, 0a, 7f,16, 7f,15, 7f,14, 7f,13, 7f,12" if [[ $? -eq 0 ]]; then - add_tls_offered tls1_3 yes + add_proto_offered tls1_3 yes all_failed=false else - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no KEY_SHARE_EXTN_NR="33" fi fi @@ -19552,11 +19511,11 @@ determine_optimal_sockets_params() { # drafts 22-28 and the final TLS 1.3 the ProtocolVersion field contains # 0303 and the actual version appears in the supported_versions extension. if [[ "${TLS_SERVER_HELLO:8:3}" == 7F1 ]]; then - add_tls_offered tls1_3_draft$(hex2dec "${TLS_SERVER_HELLO:10:2}") yes + add_proto_offered tls1_3_draft$(hex2dec "${TLS_SERVER_HELLO:10:2}") yes elif [[ "$TLS_SERVER_HELLO" =~ 002B00020304 ]]; then - add_tls_offered tls1_3_rfc8446 yes + add_proto_offered tls1_3_rfc8446 yes elif [[ "$TLS_SERVER_HELLO" =~ 002B00027F1[2-9A-C] ]]; then - add_tls_offered tls1_3_draft$(hex2dec "${BASH_REMATCH:10:2}") yes + add_proto_offered tls1_3_draft$(hex2dec "${BASH_REMATCH:10:2}") yes fi fi @@ -19567,10 +19526,10 @@ determine_optimal_sockets_params() { ret1=$? if [[ $ret1 -eq 0 ]] || [[ $ret1 -eq 2 ]]; then case $DETECTED_TLS_VERSION in - 0303) add_tls_offered tls1_2 yes ;; - 0302) add_tls_offered tls1_1 yes ;; - 0301) add_tls_offered tls1 yes ;; - 0300) add_tls_offered ssl3 yes ;; + 0303) add_proto_offered tls1_2 yes ;; + 0302) add_proto_offered tls1_1 yes ;; + 0301) add_proto_offered tls1 yes ;; + 0300) add_proto_offered ssl3 yes ;; esac all_failed=false fi @@ -19584,17 +19543,17 @@ determine_optimal_sockets_params() { tls_sockets "03" "$TLS12_CIPHER_2ND_TRY" ret2=$? if [[ $ret2 -eq 0 ]]; then - add_tls_offered tls1_2 yes + add_proto_offered tls1_2 yes TLS12_CIPHER="$TLS12_CIPHER_2ND_TRY" all_failed=false else - add_tls_offered tls1_2 no + add_proto_offered tls1_2 no fi if [[ $ret2 -eq 2 ]]; then case $DETECTED_TLS_VERSION in - 0302) add_tls_offered tls1_1 yes ;; - 0301) add_tls_offered tls1 yes ;; - 0300) add_tls_offered ssl3 yes ;; + 0302) add_proto_offered tls1_1 yes ;; + 0301) add_proto_offered tls1 yes ;; + 0300) add_proto_offered ssl3 yes ;; esac [[ $ret1 -ne 2 ]] && TLS12_CIPHER="$TLS12_CIPHER_2ND_TRY" all_failed=false @@ -19620,16 +19579,16 @@ determine_optimal_sockets_params() { ret1=$? if [[ $ret1 -ne 0 ]]; then case $proto in - 02) add_tls_offered tls1_1 no ;; - 01) add_tls_offered tls1 no ;; - 00) add_tls_offered ssl3 no ;; + 02) add_proto_offered tls1_1 no ;; + 01) add_proto_offered tls1 no ;; + 00) add_proto_offered ssl3 no ;; esac fi if [[ $ret1 -eq 0 ]] || [[ $ret1 -eq 2 ]]; then case $DETECTED_TLS_VERSION in - 0302) add_tls_offered tls1_1 yes ;; - 0301) add_tls_offered tls1 yes ;; - 0300) add_tls_offered ssl3 yes ;; + 0302) add_proto_offered tls1_1 yes ;; + 0301) add_proto_offered tls1 yes ;; + 0300) add_proto_offered ssl3 yes ;; esac OPTIMAL_SOCKETS_PROTO="$proto" all_failed=false @@ -19639,7 +19598,7 @@ determine_optimal_sockets_params() { fi if "$all_failed"; then sslv2_sockets - [[ $? -eq 3 ]] && all_failed=false && add_tls_offered ssl2 yes + [[ $? -eq 3 ]] && all_failed=false && add_proto_offered ssl2 yes fi ALL_FAILED_SOCKETS="$all_failed" return 0 @@ -19674,7 +19633,7 @@ determine_optimal_proto() { $OPENSSL s_client $(s_client_options "$STARTTLS_OPTIMAL_PROTO $BUGS -connect "$NODEIP:$PORT" $PROXY -msg $STARTTLS $SNI") </dev/null >$TMPFILE 2>>$ERRFILE if sclient_auth $? $TMPFILE; then all_failed=false - add_tls_offered "${proto/-/}" yes + add_proto_offered "${proto/-/}" yes break fi done @@ -19699,11 +19658,11 @@ determine_optimal_proto() { tmp=${tmp/\./_} tmp=${tmp/v/} tmp="$(tolower $tmp)" - add_tls_offered "${tmp}" yes + add_proto_offered "${tmp}" yes debugme echo "one proto determined: $tmp" OPTIMAL_PROTO="" else - add_tls_offered "${proto/-/}" yes + add_proto_offered "${proto/-/}" yes OPTIMAL_PROTO="$proto" fi all_failed=false @@ -19729,7 +19688,7 @@ determine_optimal_proto() { elif "$all_failed" && ! "$ALL_FAILED_SOCKETS"; then if ! "$HAS_TLS13" && "$TLS13_ONLY"; then pr_magenta " $NODE:$PORT appears to support TLS 1.3 ONLY. You better use --openssl=<path_to_openssl_supporting_TLS_1.3>" - if ! "$OSSL_SHORTCUT" || [[ ! -x /usr/bin/openssl ]] || /usr/bin/openssl s_client -tls1_3 -connect invalid. 2>&1 | grep -aq "unknown option"; then + if ! "$OSSL_SHORTCUT" || [[ ! -x /usr/bin/openssl ]] || /usr/bin/openssl s_client -tls1_3 -connect invalid. 2>&1 | grep -aiq "unknown option"; then outln ignore_no_or_lame " Type \"yes\" to proceed and accept all scan problems" "yes" [[ $? -ne 0 ]] && exit $ERR_CLUELESS @@ -20803,7 +20762,7 @@ initialize_globals() { # Set default scanning options for the boolean global do_* variables. set_scanning_defaults() { - do_allciphers=true + do_allciphers=false do_vulnerabilities=true do_beast=true do_lucky13=true @@ -21582,11 +21541,11 @@ lets_roll() { fileout_section_header $section_number true && ((section_number++)) "$do_cipherlists" && { run_cipherlists; ret=$(($? + ret)); stopwatch run_cipherlists; } - fileout_section_header $section_number true && ((section_number++)) - "$do_fs" && { run_fs; ret=$(($? + ret)); stopwatch run_fs; } + fileout_section_header $section_number true && ((section_number++)) + "$do_server_preference" && { run_server_preference; ret=$(($? + ret)); stopwatch run_server_preference; } fileout_section_header $section_number true && ((section_number++)) - "$do_server_preference" && { run_server_preference; ret=$(($? + ret)); stopwatch run_server_preference; } + "$do_fs" && { run_fs; ret=$(($? + ret)); stopwatch run_fs; } fileout_section_header $section_number true && ((section_number++)) "$do_server_defaults" && { run_server_defaults; ret=$(($? + ret)); stopwatch run_server_defaults; }