diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 3ea885a..a0891f9 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -7,8 +7,9 @@ assignees: ''
---
-_**Please don't remove this template. We would like to reproduce the bug and need concise information. **_
+_**Fee free to remove this line but please stick to this template. We would like to reproduce the bug and need concise information. **_
+**Please check this repo whether this is a known issue**
**Command line / docker command to reproduce**
In addition the target of your scan would be helpful. If you don't want to disclose it publicly: ``grep SWCONTACT testssl.sh``.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 750bbb4..3c5c862 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -16,12 +16,19 @@ assignees: ''
---
+
+**Please check this repo whether this is a known issue**
+Feel free to comment there
+
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
+**Which version are you referring to**
+3.0.x or 3.1dev? (please check also how old your version is compare to the ones provided here)
+
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
``
diff --git a/.github/ISSUE_TEMPLATE/other-issues---question.md b/.github/ISSUE_TEMPLATE/other-issues---question.md
index 898c032..4832b95 100644
--- a/.github/ISSUE_TEMPLATE/other-issues---question.md
+++ b/.github/ISSUE_TEMPLATE/other-issues---question.md
@@ -7,4 +7,5 @@ assignees: ''
---
-
+**Which version are you referring to**
+3.0.x or 3.1dev? (please check also how old your version is compare to the ones here)
diff --git a/.travis.yml b/.travis.yml
index 06f35cd..2e43e95 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,11 +8,13 @@ addons:
- dnsutils
- jsonlint
before_install:
- - if ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qvE '(.md)|(.pem)|(.pdf)|(.html)|^(LICENSE)|^(docs)|^(utils)|^(bin)|(Dockerfile)'
+ - |
+ echo "Checking if a CI run is needed post commit: ${TRAVIS_COMMIT_RANGE}"
+ if ! git diff --name-only ${TRAVIS_COMMIT_RANGE} | grep -qvE '(\.md$)|(\.pem$)|(\.pdf$)|(\.html$)|^(LICENSE)|^(docs)|^(utils)|^(bin)|(Dockerfile)'
then
- echo "no code was updated, not running the CI."
+ echo "No code was updated, not running the CI."
exit
- fi
+ fi
install:
- cpanm --notest Test::More
- cpanm --notest Data::Dumper
diff --git a/Dockerfile b/Dockerfile
index 019668a..9f0ea92 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,7 @@ FROM alpine:3.11
RUN apk update && \
apk upgrade && \
- apk add bash procps drill git coreutils libidn curl socat openssl && \
+ apk add bash procps drill git coreutils libidn curl socat openssl xxd && \
rm -rf /var/cache/apk/* && \
addgroup testssl && \
adduser -G testssl -g "testssl user" -s /bin/bash -D testssl && \
diff --git a/Readme.md b/Readme.md
index c8fbbf7..2a62e59 100644
--- a/Readme.md
+++ b/Readme.md
@@ -88,7 +88,7 @@ Support for 2.9.5 has been dropped. Supported is >= 3.0.x only.
### Contributing
-Contributions are welcome! See [CONTRIBUTING.md](https://github.com/drwetter/testssl.sh/blob/3.1dev/CONTRIBUTING.md) for details. Please also have a look at the [Coding Convention](https://github.com/drwetter/testssl.sh/blob/3.1dev/Coding_Convention.md}.
+Contributions are welcome! See [CONTRIBUTING.md](https://github.com/drwetter/testssl.sh/blob/3.1dev/CONTRIBUTING.md) for details. Please also have a look at the [Coding Convention](https://github.com/drwetter/testssl.sh/blob/3.1dev/Coding_Convention.md).
### Bug reports
@@ -106,6 +106,7 @@ You can also debug yourself, see [here](https://github.com/drwetter/testssl.sh/w
Please address questions not specifically to the code of testssl.sh to the respective projects below.
#### Web frontend
+* https://github.com/johannesschaefer/webnettools
* https://github.com/TKCERT/testssl.sh-webfrontend
#### Free to use Web frontend + commercial API
@@ -134,3 +135,6 @@ Please address questions not specifically to the code of testssl.sh to the respe
#### Daemon for batch processing of testssl.sh JSON result files for sending Slack alerts, reactive copying etc
* https://github.com/bitsofinfo/testssl.sh-alerts
+
+#### GitHub Actions
+* https://github.com/marketplace/actions/testssl-sh-scan
diff --git a/openssl-iana.mapping.html b/openssl-iana.mapping.html
index 4b35805..4315919 100644
--- a/openssl-iana.mapping.html
+++ b/openssl-iana.mapping.html
@@ -398,8 +398,8 @@ xB9 TLS_RSA_PSK_WITH_NULL_SHA384
[0xc097] | DHE-PSK-CAMELLIA256-SHA384 | PSK/DHE | CAMELLIA | 256 | TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 |
[0xc098] | RSA-PSK-CAMELLIA128-SHA256 | PSK/RSA | CAMELLIA | 128 | TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 |
[0xc099] | RSA-PSK-CAMELLIA256-SHA384 | PSK/RSA | CAMELLIA | 256 | TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 |
- [0xc09A] | ECDHE-PSK-CAMELLIA128-SHA25 | PSK/ECDHE | CAMELLIA | 128 | TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 |
- [0xc09B] | ECDHE-PSK-CAMELLIA256-SHA38 | PSK/ECDHE | CAMELLIA | 256 | TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 |
+ [0xc09A] | ECDHE-PSK-CAMELLIA128-SHA256 | PSK/ECDHE | CAMELLIA | 128 | TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 |
+ [0xc09B] | ECDHE-PSK-CAMELLIA256-SHA384 | PSK/ECDHE | CAMELLIA | 256 | TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 |
[0xc09c] | AES128-CCM | RSA | AESCCM | 128 | TLS_RSA_WITH_AES_128_CCM |
@@ -430,6 +430,15 @@ xB9 TLS_RSA_PSK_WITH_NULL_SHA384
[0xcc14] | ECDHE-ECDSA-CHACHA20-POLY1305-OLD | ECDH | ChaCha20-Poly1305 | | TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD |
[0xcc15] | DHE-RSA-CHACHA20-POLY1305-OLD | DH | ChaCha20-Poly1305 | | TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD |
+
+ [0xcca8] | ECDHE-RSA-CHACHA20-POLY1305 | ECDH | ChaCha20-Poly1305 | 256 | TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 |
+ [0xcca9] | ECDHE-ECDSA-CHACHA20-POLY1305 | ECDH | ChaCha20-Poly1305 | 256 | TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 |
+ [0xccaa] | DHE-RSA-CHACHA20-POLY1305 | DH | ChaCha20-Poly1305 | 256 | TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 |
+ [0xccab] | PSK-CHACHA20-POLY1305 | PSK | ChaCha20-Poly1305 | 256 | TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 |
+ [0xccac] | ECDHE-PSK-CHACHA20-POLY1305 | ECDH/PSK | ChaCha20-Poly1305 | 256 | TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 |
+ [0xccad] | DHE-PSK-CHACHA20-POLY1305 | DH/PSK | ChaCha20-Poly1305 | 256 | TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 |
+ [0xccae] | RSA-PSK-CHACHA20-POLY1305 | RSA/PSK | ChaCha20-Poly1305 | 256 | TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 |
+
[0xff00] | GOST-MD5 | RSA | GOST89 | 256 | TLS_GOSTR341094_RSA_WITH_28147_CNT_MD5 |
[0xff01] | GOST-GOST94 | RSA | GOST89 | 256 | TLS_RSA_WITH_28147_CNT_GOST94 |
[0xff02] | GOST-GOST89MAC | RSA | GOST89 | 256 |
diff --git a/t/61_diff_testsslsh.t b/t/61_diff_testsslsh.t
index f7afe79..694feef 100755
--- a/t/61_diff_testsslsh.t
+++ b/t/61_diff_testsslsh.t
@@ -42,12 +42,16 @@ $diff = diff $socket_csv, $master_socket_csv;
$socket_csv=`cat tmp.csv`;
$master_socket_csv=`cat $master_socket_csv`;
-# Filter, for now only HTTP_clock_skew
+# Filter for changes that are allowed to occur
$socket_csv=~ s/HTTP_clock_skew.*\n//g;
$master_socket_csv=~ s/HTTP_clock_skew.*\n//g;
+# DROWN
+$socket_csv=~ s/censys.io.*\n//g;
+$master_socket_csv=~ s/censys.io.*\n//g;
+
+
# Compare the differences to the master file -- and print differences if there were detected.
-# Filtering takes place later, so if there will be a difference detected it'll also show HTTP_clock_skew :-(
#
cmp_ok($socket_csv, "eq", $master_socket_csv, "Check whether CSV output matches master file from $uri") or
diag ("\n%s\n", "$diff");
diff --git a/t/baseline_data/default_testssl.csvfile b/t/baseline_data/default_testssl.csvfile
index 9c89f61..4afa2c6 100644
--- a/t/baseline_data/default_testssl.csvfile
+++ b/t/baseline_data/default_testssl.csvfile
@@ -75,8 +75,6 @@
"X-Frame-Options","testssl.sh/81.169.166.184","443","OK","DENY","",""
"X-Content-Type-Options","testssl.sh/81.169.166.184","443","OK","nosniff","",""
"Content-Security-Policy","testssl.sh/81.169.166.184","443","OK","script-src 'unsafe-inline'; style-src 'unsafe-inline' 'self'; default-src 'self' ; child-src 'none'; object-src 'self'; frame-ancestors 'self'; upgrade-insecure-requests","",""
-"Expect-CT","testssl.sh/81.169.166.184","443","OK","max-age=86400, enforce","",""
-"X-XSS-Protection","testssl.sh/81.169.166.184","443","INFO","1; mode=block","",""
"banner_reverseproxy","testssl.sh/81.169.166.184","443","INFO","--","","CWE-200"
"heartbleed","testssl.sh/81.169.166.184","443","OK","not vulnerable, no heartbeat extension","CVE-2014-0160","CWE-119"
"CCS","testssl.sh/81.169.166.184","443","OK","not vulnerable","CVE-2014-0224","CWE-310"
diff --git a/testssl.sh b/testssl.sh
index 951fc01..aa603b9 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -234,6 +234,7 @@ SSL_RENEG_ATTEMPTS=${SSL_RENEG_ATTEMPTS:-6} # number of times to check SSL
########### Initialization part, further global vars just being declared here
#
+LC_COLLATE=en_US.UTF-8 # ensures certain regex patterns work as expected and aren't localized, see #1860
SYSTEM2="" # currently only being used for WSL = bash on windows
PRINTF="" # which external printf to use. Empty presets the internal one, see #1130
CIPHERS_BY_STRENGTH_FILE=""
@@ -251,6 +252,10 @@ GIVE_HINTS=false # give an additional info to findings
SERVER_SIZE_LIMIT_BUG=false # Some servers have either a ClientHello total size limit or a 128 cipher limit (e.g. old ASAs)
MULTIPLE_CHECKS=false # need to know whether an MX record or a hostname resolves to multiple IPs to check
CHILD_MASS_TESTING=${CHILD_MASS_TESTING:-false}
+PARENT_LOGFILE="" # logfile if mass testing and all output sent to a single file
+PARENT_JSONFILE="" # jsonfile if mass testing and all output sent to a single file
+PARENT_CSVFILE="" # csvfile if mass testing and all output sent to a single file
+PARENT_HTMLFILE="" # HTML if mass testing and all output sent to a single file
TIMEOUT_CMD=""
HAD_SLEPT=0
NR_SOCKET_FAIL=0 # Counter for socket failures
@@ -273,7 +278,8 @@ declare -r ALPN_PROTOs="h2 spdy/3.1 http/1.1 grpc-exp h2-fb spdy/1 spdy/2 spdy/3
TEMPDIR=""
TMPFILE=""
ERRFILE=""
-CLIENT_AUTH=false
+CLIENT_AUTH="none"
+CLIENT_AUTH_CA_LIST=""
TLS_TICKETS=false
NO_SSL_SESSIONID=false
CERT_COMPRESSION=${CERT_COMPRESSION:-false} # secret flag to set in addition to --devel for certificate compression
@@ -352,6 +358,7 @@ HAS_IDN=false
HAS_IDN2=false
HAS_AVAHIRESOLVE=false
HAS_DIG_NOIDNOUT=false
+HAS_XXD=false
OSSL_CIPHERS_S=""
PORT=443 # unless otherwise auto-determined, see below
@@ -779,15 +786,6 @@ hex2dec() {
echo $((16#$1))
}
-# convert 414243 into ABC
-hex2ascii() {
- for (( i=0; i<${#1}; i+=2 )); do
- # 2>/dev/null added because 'warning: command substitution: ignored null byte in input'
- # --> didn't help though
- printf "\x${1:$i:2}" 2>/dev/null
- done
-}
-
# convert decimal number < 256 to hex
dec02hex() {
printf "x%02x" "$1"
@@ -1343,6 +1341,10 @@ json_header() {
local fname_prefix
local filename_provided=false
+ if [[ -n "$PARENT_JSONFILE" ]]; then
+ [[ -n "$JSONFILE" ]] && fatal "Can't write to both $PARENT_JSONFILE and $JSONFILE"
+ JSONFILE="$PARENT_JSONFILE"
+ fi
[[ -n "$JSONFILE" ]] && [[ ! -d "$JSONFILE" ]] && filename_provided=true
# Similar to HTML: Don't create headers and footers in the following scenarios:
# * no JSON/CSV output is being created.
@@ -1350,7 +1352,7 @@ json_header() {
# * this is an individual test within a mass test and all output is being placed in a single file.
! "$do_json" && ! "$do_pretty_json" && JSONHEADER=false && return 0
"$do_mass_testing" && ! "$filename_provided" && JSONHEADER=false && return 0
- "$CHILD_MASS_TESTING" && "$filename_provided" && JSONHEADER=false && return 0
+ "$CHILD_MASS_TESTING" && "$filename_provided" && [[ -n "$PARENT_JSONFILE" ]] && JSONHEADER=false && return 0
if "$do_display_only"; then
fname_prefix="local-ciphers"
@@ -1390,11 +1392,15 @@ csv_header() {
local fname_prefix
local filename_provided=false
+ if [[ -n "$PARENT_CSVFILE" ]]; then
+ [[ -n "$CSVFILE" ]] && fatal "Can't write to both $PARENT_CSVFILE and $CSVFILE"
+ CSVFILE="$PARENT_CSVFILE"
+ fi
[[ -n "$CSVFILE" ]] && [[ ! -d "$CSVFILE" ]] && filename_provided=true
# CSV similar to JSON
! "$do_csv" && CSVHEADER=false && return 0
"$do_mass_testing" && ! "$filename_provided" && CSVHEADER=false && return 0
- "$CHILD_MASS_TESTING" && "$filename_provided" && CSVHEADER=false && return 0
+ "$CHILD_MASS_TESTING" && "$filename_provided" && [[ -n "$PARENT_CSVFILE" ]] && CSVHEADER=false && return 0
if "$do_display_only"; then
fname_prefix="local-ciphers"
@@ -1440,6 +1446,10 @@ html_header() {
local fname_prefix
local filename_provided=false
+ if [[ -n "$PARENT_HTMLFILE" ]]; then
+ [[ -n "$HTMLFILE" ]] && fatal "Can't write to both $PARENT_HTMLFILE and $HTMLFILE"
+ HTMLFILE="$PARENT_HTMLFILE"
+ fi
[[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] && filename_provided=true
# Don't create HTML headers and footers in the following scenarios:
# * HTML output is not being created.
@@ -1447,7 +1457,7 @@ html_header() {
# * this is an individual test within a mass test and all HTML output is being placed in a single file.
! "$do_html" && HTMLHEADER=false && return 0
"$do_mass_testing" && ! "$filename_provided" && HTMLHEADER=false && return 0
- "$CHILD_MASS_TESTING" && "$filename_provided" && HTMLHEADER=false && return 0
+ "$CHILD_MASS_TESTING" && "$filename_provided" && [[ -n "$PARENT_HTMLFILE" ]] && HTMLHEADER=false && return 0
if "$do_display_only"; then
fname_prefix="local-ciphers"
@@ -1515,12 +1525,16 @@ prepare_logging() {
local fname_prefix="$1"
local filename_provided=false
+ if [[ -n "$PARENT_LOGFILE" ]]; then
+ [[ -n "$LOGFILE" ]] && fatal "Can't write to both $PARENT_LOGFILE and $LOGFILE"
+ LOGFILE="$PARENT_LOGFILE"
+ fi
[[ -n "$LOGFILE" ]] && [[ ! -d "$LOGFILE" ]] && filename_provided=true
# Similar to html_header():
! "$do_logging" && return 0
"$do_mass_testing" && ! "$filename_provided" && return 0
- "$CHILD_MASS_TESTING" && "$filename_provided" && return 0
+ "$CHILD_MASS_TESTING" && "$filename_provided" && [[ -n "$PARENT_LOGFILE" ]] && return 0
[[ -z "$fname_prefix" ]] && fname_prefix="${FNAME_PREFIX}${NODE}_p${PORT}"
@@ -1945,7 +1959,7 @@ check_revocation_ocsp() {
grep -q "\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-" $TEMPDIR/intermediatecerts.pem || return 0
tmpfile=$TEMPDIR/${NODE}-${NODEIP}.${uri##*\/} || exit $ERR_FCREATE
if [[ -n "$stapled_response" ]]; then
- asciihex_to_binary "$stapled_response" > "$TEMPDIR/stapled_ocsp_response.dd"
+ hex2binary "$stapled_response" > "$TEMPDIR/stapled_ocsp_response.dd"
$OPENSSL ocsp -no_nonce -respin "$TEMPDIR/stapled_ocsp_response.dd" \
-issuer $TEMPDIR/hostcert_issuer.pem -verify_other $TEMPDIR/intermediatecerts.pem \
-CAfile <(cat $ADDTL_CA_FILES "$GOOD_CA_BUNDLE") -cert $HOSTCERT -text &> "$tmpfile"
@@ -2056,35 +2070,41 @@ fi
# Print $arg1 in binary format. arg1: An ASCII-HEX string
-#
-asciihex_to_binary() {
- local string="$1"
- local -i len
- local -i i ip2 ip4 ip6 ip8 ip10 ip12 ip14
- local -i remainder
+# The string represented by $arg1 may be binary data (a certificate or public
+# key) or a text string (e.g., ASCII-encoded text).
+hex2binary() {
+ local s="$1"
+ local -i i len remainder
- len=${#string}
+ len=${#s}
[[ $len%2 -ne 0 ]] && return 1
- for (( i=0; i <= len-16 ; i+=16 )); do
- ip2=$((i+2)); ip4=$((i+4)); ip6=$((i+6)); ip8=$((i+8)); ip10=$((i+10)); ip12=$((i+12)); ip14=$((i+14))
- printf -- "\x${string:i:2}\x${string:ip2:2}\x${string:ip4:2}\x${string:ip6:2}\x${string:ip8:2}\x${string:ip10:2}\x${string:ip12:2}\x${string:ip14:2}"
- done
+ if "$HAS_XXD"; then
+ xxd -r -p <<< "$s"
+ else
+ for (( i=0; i <= len-16 ; i+=16 )); do
+ printf -- "\x${s:i:2}\x${s:$((i+2)):2}\x${s:$((i+4)):2}\x${s:$((i+6)):2}\x${s:$((i+8)):2}\x${s:$((i+10)):2}\x${s:$((i+12)):2}\x${s:$((i+14)):2}"
+ done
- ip2=$((i+2)); ip4=$((i+4)); ip6=$((i+6)); ip8=$((i+8)); ip10=$((i+10)); ip12=$((i+12)); ip14=$((i+14))
- remainder=$len-$i
- case $remainder in
- 2) printf -- "\x${string:i:2}" ;;
- 4) printf -- "\x${string:i:2}\x${string:ip2:2}" ;;
- 6) printf -- "\x${string:i:2}\x${string:ip2:2}\x${string:ip4:2}" ;;
- 8) printf -- "\x${string:i:2}\x${string:ip2:2}\x${string:ip4:2}\x${string:ip6:2}" ;;
- 10) printf -- "\x${string:i:2}\x${string:ip2:2}\x${string:ip4:2}\x${string:ip6:2}\x${string:ip8:2}" ;;
- 12) printf -- "\x${string:i:2}\x${string:ip2:2}\x${string:ip4:2}\x${string:ip6:2}\x${string:ip8:2}\x${string:ip10:2}" ;;
- 14) printf -- "\x${string:i:2}\x${string:ip2:2}\x${string:ip4:2}\x${string:ip6:2}\x${string:ip8:2}\x${string:ip10:2}\x${string:ip12:2}" ;;
- esac
+ remainder=$((len-i))
+ case $remainder in
+ 2) printf -- "\x${s:i:2}" ;;
+ 4) printf -- "\x${s:i:2}\x${s:$((i+2)):2}" ;;
+ 6) printf -- "\x${s:i:2}\x${s:$((i+2)):2}\x${s:$((i+4)):2}" ;;
+ 8) printf -- "\x${s:i:2}\x${s:$((i+2)):2}\x${s:$((i+4)):2}\x${s:$((i+6)):2}" ;;
+ 10) printf -- "\x${s:i:2}\x${s:$((i+2)):2}\x${s:$((i+4)):2}\x${s:$((i+6)):2}\x${s:$((i+8)):2}" ;;
+ 12) printf -- "\x${s:i:2}\x${s:$((i+2)):2}\x${s:$((i+4)):2}\x${s:$((i+6)):2}\x${s:$((i+8)):2}\x${s:$((i+10)):2}" ;;
+ 14) printf -- "\x${s:i:2}\x${s:$((i+2)):2}\x${s:$((i+4)):2}\x${s:$((i+6)):2}\x${s:$((i+8)):2}\x${s:$((i+10)):2}\x${s:$((i+12)):2}" ;;
+ esac
+ fi
return 0
}
+# convert 414243 into ABC
+hex2ascii() {
+ hex2binary $1
+}
+
# arg1: text string
# Output a comma-separated ASCII-HEX string representation of the input string.
string_to_asciihex() {
@@ -2210,7 +2230,7 @@ s_client_options() {
service_detection() {
local -i was_killed
- if ! "$CLIENT_AUTH"; then
+ if [[ "$CLIENT_AUTH" != required ]]; then
if ! "$HAS_TLS13" && "$TLS13_ONLY"; then
# Using sockets is a lot slower than using OpenSSL, and it is
# not as reliable, but if OpenSSL can't connect to the server,
@@ -2259,7 +2279,7 @@ service_detection() {
out " $SERVICE, thus skipping HTTP specific checks"
fileout "${jsonID}" "INFO" "$SERVICE, thus skipping HTTP specific checks"
;;
- *) if "$CLIENT_AUTH"; then
+ *) if [[ "$CLIENT_AUTH" == required ]]; then
out " certificate-based authentication => skipping all HTTP checks"
echo "certificate-based authentication => skipping all HTTP checks" >$TMPFILE
fileout "${jsonID}" "INFO" "certificate-based authentication => skipping all HTTP checks"
@@ -2481,7 +2501,7 @@ run_http_date() {
local spaces=" "
jsonID="HTTP_clock_skew"
- if [[ $SERVICE != HTTP ]] || "$CLIENT_AUTH"; then
+ if [[ $SERVICE != HTTP ]] || [[ "$CLIENT_AUTH" == required ]]; then
return 0
fi
if [[ ! -s $HEADERFILE ]]; then
@@ -3430,7 +3450,7 @@ rfc2hexcode() {
local -i i
for (( i=0; i < TLS_NR_CIPHERS; i++ )); do
- [[ "$1" == "${TLS_CIPHER_RFC_NAME[i]}" ]] && hexc="${TLS_CIPHER_HEXCODE[i]}" && break
+ [[ "$1" == ${TLS_CIPHER_RFC_NAME[i]} ]] && hexc="${TLS_CIPHER_HEXCODE[i]}" && break
done
[[ -z "$hexc" ]] && return 1
tm_out "$hexc"
@@ -6430,7 +6450,7 @@ sub_session_resumption() {
return 1
fi
fi
- "$CLIENT_AUTH" && return 6
+ [[ "$CLIENT_AUTH" == required ]] && return 6
if ! "$HAS_TLS13" && "$HAS_NO_SSL2"; then
addcmd+=" -no_ssl2"
else
@@ -7028,7 +7048,7 @@ cipher_pref_check() {
[[ $? -ne 0 ]] && break
cipher=$(get_cipher "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")
for (( i=bundle*bundle_size; i < end_of_bundle; i++ )); do
- [[ "$cipher" == "${rfc_ciph[i]}" ]] && ciphers_found2[i]=true && break
+ [[ "$cipher" == ${rfc_ciph[i]} ]] && ciphers_found2[i]=true && break
done
i=${index[i]}
ciphers_found[i]=true
@@ -8257,7 +8277,7 @@ compare_server_name_to_cert() {
j+=2
fi
if [[ $len1 -ne 0 ]]; then
- san="$(asciihex_to_binary "${dercert:j:len1}")"
+ san="$(hex2binary "${dercert:j:len1}")"
if [[ "${dercert:i:20}" == "06082B06010505070805" ]]; then
xmppaddr+="$san "
else
@@ -8423,7 +8443,7 @@ etsi_ets_visibility_info() {
# Next is the 10-byte fingerprint, encoded as an OCTET STRING (04)
[[ "${dercert:j:4}" == 040A ]] || continue
j+=4
- fingerprint[nr_visnames]="$(asciihex_to_binary "${dercert:j:20}")"
+ fingerprint[nr_visnames]="$(hex2binary "${dercert:j:20}")"
j+=20
# Finally comes the access description, encoded as a UTF8String (0C).
[[ "${dercert:j:2}" == 0C ]] || continue
@@ -8440,7 +8460,7 @@ etsi_ets_visibility_info() {
len1=2*0x${dercert:j:2}
j+=2
fi
- access_description[nr_visnames]=""$(asciihex_to_binary "${dercert:j:len1}")""
+ access_description[nr_visnames]=""$(hex2binary "${dercert:j:len1}")""
nr_visnames+=1
done
fi
@@ -8597,7 +8617,7 @@ certificate_transparency() {
fi
fi
- if [[ $SERVICE != HTTP ]] && ! "$CLIENT_AUTH"; then
+ if [[ $SERVICE != HTTP ]] && [[ "$CLIENT_AUTH" != required ]]; then
# At the moment Certificate Transparency only applies to HTTPS.
tm_out "N/A"
else
@@ -8992,7 +9012,7 @@ certificate_info() {
"DH") if [[ -s "$common_primes_file" ]]; then
cert_spki_info="${cert_txt##*Subject Public Key Info:}"
cert_spki_info="${cert_spki_info##*Public Key Algorithm:}"
- cert_spki_info="$(awk '/prime:|prime P:/,/generator:|generator G:/' <<< "$cert_spki_info" | grep -Ev "prime|generator")"
+ cert_spki_info="$(awk '/prime:|P:/,/generator:|G:/' <<< "$cert_spki_info" | grep -Ev "prime|P:|generator|G:")"
cert_spki_info="$(strip_spaces "$(colon_to_spaces "$(newline_to_spaces "$cert_spki_info")")")"
[[ "${cert_spki_info:0:2}" == 00 ]] && cert_spki_info="${cert_spki_info:2}"
cert_spki_info="$(toupper "$cert_spki_info")"
@@ -9320,7 +9340,7 @@ certificate_info() {
# We adjust the thresholds by %50 for LE certificates, relaxing warnings for those certificates.
# . instead of \' because it does not break syntax highlighting in vim
- if [[ "$issuer_CN" =~ ^Let.s\ Encrypt\ Authority ]] ; then
+ if [[ "$issuer_O" =~ ^Let.s\ Encrypt ]] ; then
days2warn2=$((days2warn2 / 2))
days2warn1=$((days2warn1 / 2))
fi
@@ -9725,7 +9745,7 @@ run_server_defaults() {
local -a ocsp_response_binary ocsp_response ocsp_response_status sni_used tls_version ct
local -a ciphers_to_test certificate_type
local -a -i success
- local cn_nosni cn_sni sans_nosni sans_sni san tls_extensions
+ local cn_nosni cn_sni sans_nosni sans_sni san tls_extensions client_auth_ca
local using_sockets=true
local spaces=" "
@@ -10077,6 +10097,25 @@ run_server_defaults() {
sub_mta_sts "$spaces"
sub_dane "$spaces"
+ jsonID="clientAuth"
+ pr_bold " Client Authentication "
+ outln "$CLIENT_AUTH"
+ fileout "$jsonID" "INFO" "$CLIENT_AUTH"
+ if [[ "$CLIENT_AUTH" != none ]]; then
+ jsonID="clientAuth_CA_list"
+ pr_bold " CA List for Client Auth "
+ out_row_aligned "$CLIENT_AUTH_CA_LIST" " "
+ if [[ "$CLIENT_AUTH_CA_LIST" == empty ]] || [[ $(count_lines "$CLIENT_AUTH_CA_LIST") -eq 1 ]]; then
+ fileout "$jsonID" "INFO" "$CLIENT_AUTH_CA_LIST"
+ else
+ i=1
+ while read client_auth_ca; do
+ fileout "$jsonID #$i" "INFO" "$client_auth_ca"
+ i+=1
+ done <<< "$CLIENT_AUTH_CA_LIST"
+ fi
+ fi
+
if [[ -n "$SNI" ]] && [[ $certs_found -ne 0 ]] && [[ ! -e $HOSTCERT.nosni ]]; then
# no cipher suites specified here. We just want the default vhost subject
if ! "$HAS_TLS13" && [[ $(has_server_protocol "tls1_3") -eq 0 ]]; then
@@ -10121,7 +10160,7 @@ run_server_defaults() {
if $TLS13_ONLY; then
generic_nonfatal "Client problem: We need openssl supporting TLS 1.3. We can't continue with \"server defaults\" as we cannot retrieve the certificate. "
else
- generic_nonfatal "Client problem, No server cerificate could be retrieved. Thus we can't continue with \"server defaults\"."
+ generic_nonfatal "Client problem: No server certificate could be retrieved. Thus we can't continue with \"server defaults\"."
fi
fi
[[ $DEBUG -ge 1 ]] && [[ -e $HOSTCERT.nosni ]] && $OPENSSL x509 -in $HOSTCERT.nosni -text -noout 2>>$ERRFILE > $HOSTCERT.nosni.txt
@@ -11284,7 +11323,7 @@ get_pub_key_size() {
"$HAS_PKEY" || return 1
# OpenSSL displays the number of bits for RSA and ECC
- pubkeybits=$($OPENSSL x509 -noout -pubkey -in $HOSTCERT 2>>$ERRFILE | $OPENSSL pkey -pubin -text 2>>$ERRFILE)
+ pubkeybits=$($OPENSSL x509 -noout -pubkey -in $HOSTCERT 2>>$ERRFILE | $OPENSSL pkey -pubin -text_pub 2>>$ERRFILE)
if [[ "$pubkeybits" =~ E[Dd]25519 ]]; then
echo "Server public key is 253 bit" >> $TMPFILE
return 0
@@ -11534,7 +11573,7 @@ get_dh_ephemeralkey() {
len1="82$(printf "%04x" $((i/2)))"
fi
key_bitstring="30${len1}${dh_param}${dh_y}"
- key_bitstring="$(asciihex_to_binary "$key_bitstring" | $OPENSSL pkey -pubin -inform DER 2> $ERRFILE)"
+ key_bitstring="$(hex2binary "$key_bitstring" | $OPENSSL pkey -pubin -inform DER 2> $ERRFILE)"
[[ -z "$key_bitstring" ]] && return 1
tm_out "$key_bitstring"
return 0
@@ -11639,7 +11678,7 @@ parse_sslv2_serverhello() {
certificate_len=2*$(hex2dec "$v2_hello_cert_length")
if [[ "$v2_cert_type" == "01" ]] && [[ "$v2_hello_cert_length" != "00" ]]; then
- asciihex_to_binary "${v2_hello_ascii:26:certificate_len}" | \
+ hex2binary "${v2_hello_ascii:26:certificate_len}" | \
$OPENSSL x509 -inform DER -outform PEM -out $HOSTCERT 2>$ERRFILE
if [[ $? -ne 0 ]]; then
debugme echo "Malformed certificate in ServerHello."
@@ -11672,11 +11711,11 @@ hmac() {
local -i ret
if [[ ! "$OSSL_NAME" =~ LibreSSL ]] && [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 3.0.0* ]]; then
- output="$(asciihex_to_binary "$text" | $OPENSSL mac -macopt digest:"${hash_fn/-/}" -macopt hexkey:"$key" HMAC 2>/dev/null)"
+ output="$(hex2binary "$text" | $OPENSSL mac -macopt digest:"${hash_fn/-/}" -macopt hexkey:"$key" HMAC 2>/dev/null)"
ret=$?
tm_out "$(strip_lf "$output")"
else
- output="$(asciihex_to_binary "$text" | $OPENSSL dgst "$hash_fn" -mac HMAC -macopt hexkey:"$key" 2>/dev/null)"
+ output="$(hex2binary "$text" | $OPENSSL dgst "$hash_fn" -mac HMAC -macopt hexkey:"$key" 2>/dev/null)"
ret=$?
tm_out "${output#*= }"
fi
@@ -11693,13 +11732,13 @@ hmac-transcript() {
local -i ret
if [[ ! "$OSSL_NAME" =~ LibreSSL ]] && [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 3.0.0* ]]; then
- output="$(asciihex_to_binary "$transcript" | \
+ output="$(hex2binary "$transcript" | \
$OPENSSL dgst "$hash_fn" -binary 2>/dev/null | \
$OPENSSL mac -macopt digest:"${hash_fn/-/}" -macopt hexkey:"$key" HMAC 2>/dev/null)"
ret=$?
tm_out "$(toupper "$(strip_lf "$output")")"
else
- output="$(asciihex_to_binary "$transcript" | \
+ output="$(hex2binary "$transcript" | \
$OPENSSL dgst "$hash_fn" -binary 2>/dev/null | \
$OPENSSL dgst "$hash_fn" -mac HMAC -macopt hexkey:"$key" 2>/dev/null)"
ret=$?
@@ -11793,7 +11832,7 @@ derive-secret() {
*) return 7
esac
- hash_messages="$(asciihex_to_binary "$messages" | $OPENSSL dgst "$hash_fn" 2>/dev/null)"
+ hash_messages="$(hex2binary "$messages" | $OPENSSL dgst "$hash_fn" 2>/dev/null)"
hash_messages="${hash_messages#*= }"
hkdf-expand-label "$hash_fn" "$secret" "$label" "$hash_messages" "$hash_len"
return $?
@@ -11838,7 +11877,7 @@ create-initial-transcript() {
else
return 1
fi
- hash_clienthello1="$(asciihex_to_binary "$clienthello1" | $OPENSSL dgst "$hash_fn" 2>/dev/null)"
+ hash_clienthello1="$(hex2binary "$clienthello1" | $OPENSSL dgst "$hash_fn" 2>/dev/null)"
hash_clienthello1="${hash_clienthello1#*= }"
msg_transcript="FE0000$(printf "%02x" $((${#hash_clienthello1}/2)))$hash_clienthello1$hrr$clienthello2$serverhello"
else
@@ -12233,7 +12272,7 @@ chacha20() {
local keystream plaintext=""
if "$HAS_CHACHA20"; then
- plaintext="$(asciihex_to_binary "$ciphertext" | \
+ plaintext="$(hex2binary "$ciphertext" | \
$OPENSSL enc -chacha20 -K "$key" -iv "01000000$nonce" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
tm_out "$(strip_spaces "$plaintext")"
return 0
@@ -12488,17 +12527,17 @@ chacha20_aead_encrypt() {
# See Section 6.1, Section 6.2, and Appendix A.3 of NIST SP 800-38C and
# Section 5.3 of RFC 5116.
generate-ccm-counter-blocks() {
- local ctr="02${1}000000" ctr_msb ctr_lsb1
+ local ctr="02${1}000000" ctr_msb blocks=""
local -i i ctr_lsb n="$2"
ctr_msb="${ctr:0:24}"
ctr_lsb=0x${ctr:24:8}
for (( i=0; i <= n; i+=1 )); do
- ctr_lsb1="$(printf "%08X" "$ctr_lsb")"
- printf "\x${ctr_msb:0:2}\x${ctr_msb:2:2}\x${ctr_msb:4:2}\x${ctr_msb:6:2}\x${ctr_msb:8:2}\x${ctr_msb:10:2}\x${ctr_msb:12:2}\x${ctr_msb:14:2}\x${ctr_msb:16:2}\x${ctr_msb:18:2}\x${ctr_msb:20:2}\x${ctr_msb:22:2}\x${ctr_lsb1:0:2}\x${ctr_lsb1:2:2}\x${ctr_lsb1:4:2}\x${ctr_lsb1:6:2}"
+ blocks+="${ctr_msb}$(printf "%08X" "$ctr_lsb")"
ctr_lsb+=1
done
+ hex2binary "$blocks"
return 0
}
@@ -12574,8 +12613,7 @@ ccm-compute-tag() {
[[ $i -ne 0 ]] &&
tag="$(printf "%08X%08X%08X%08X" "$((0x${b:0:8} ^ 0x${tag:0:8}))" "$((0x${b:8:8} ^ 0x${tag:8:8}))" "$((0x${b:16:8} ^ 0x${tag:16:8}))" "$((0x${b:24:8} ^ 0x${tag:24:8}))")"
- tag="$(printf "\x${tag:0:2}\x${tag:2:2}\x${tag:4:2}\x${tag:6:2}\x${tag:8:2}\x${tag:10:2}\x${tag:12:2}\x${tag:14:2}\x${tag:16:2}\x${tag:18:2}\x${tag:20:2}\x${tag:22:2}\x${tag:24:2}\x${tag:26:2}\x${tag:28:2}\x${tag:30:2}" | $OPENSSL enc "$cipher" -K "$key" -nopad 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
-
+ tag="$(hex2binary "$tag" | $OPENSSL enc "$cipher" -K "$key" -nopad 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
b="${b:32}"
done
@@ -12777,13 +12815,13 @@ gcm_mult() {
generate_gcm_ctr() {
local -i nr_blocks="$1"
local nonce="$2"
- local i
- local lsb ctr=""
+ local -i i
+ local ctr=""
for (( i=1; i <= nr_blocks; i++ )); do
- lsb="$(printf "%08X" "$i")"
- printf "\x${nonce:0:2}\x${nonce:2:2}\x${nonce:4:2}\x${nonce:6:2}\x${nonce:8:2}\x${nonce:10:2}\x${nonce:12:2}\x${nonce:14:2}\x${nonce:16:2}\x${nonce:18:2}\x${nonce:20:2}\x${nonce:22:2}\x${lsb:0:2}\x${lsb:2:2}\x${lsb:4:2}\x${lsb:6:2}"
+ ctr+="${nonce}$(printf "%08X" "$i")"
done
+ hex2binary "$ctr"
return 0
}
@@ -12942,12 +12980,12 @@ gcm-decrypt() {
[[ ${#nonce} -ne 24 ]] && return 7
if [[ "$cipher" == TLS_AES_128_GCM_SHA256 ]] && "$HAS_AES128_GCM" && ! "$compute_tag"; then
- plaintext="$(asciihex_to_binary "$ciphertext" | \
+ plaintext="$(hex2binary "$ciphertext" | \
$OPENSSL enc -aes-128-gcm -K "$key" -iv "$nonce" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
tm_out "$(strip_spaces "$plaintext")"
return 0
elif [[ "$cipher" == TLS_AES_256_GCM_SHA384 ]] && "$HAS_AES256_GCM" && ! "$compute_tag"; then
- plaintext="$(asciihex_to_binary "$ciphertext" | \
+ plaintext="$(hex2binary "$ciphertext" | \
$OPENSSL enc -aes-256-gcm -K "$key" -iv "$nonce" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
tm_out "$(strip_spaces "$plaintext")"
return 0
@@ -13629,7 +13667,7 @@ parse_tls_serverhello() {
tls_certificate_ascii_len=2*0x${tls_handshake_ascii:offset:6}
offset=$((i+16))
len1=$((msg_len-16))
- tls_certificate_ascii="$(asciihex_to_binary "${tls_handshake_ascii:offset:len1}" | $OPENSSL zlib -d 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
+ tls_certificate_ascii="$(hex2binary "${tls_handshake_ascii:offset:len1}" | $OPENSSL zlib -d 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
tls_certificate_ascii="${tls_certificate_ascii%%[!0-9A-F]*}"
if [[ ${#tls_certificate_ascii} -ne $tls_certificate_ascii_len ]]; then
debugme tmln_warning "Length of uncompressed certificates did not match specified length."
@@ -13817,7 +13855,7 @@ parse_tls_serverhello() {
return 1
fi
offset=$((offset+2))
- asciihex_to_binary "${tls_serverhello_ascii:offset:j}" >> "$TMPFILE"
+ hex2binary "${tls_serverhello_ascii:offset:j}" >> "$TMPFILE"
echo "" >> $TMPFILE
echo "===============================================================================" >> $TMPFILE
fi
@@ -13913,7 +13951,7 @@ parse_tls_serverhello() {
key_bitstring="3082${len1}$key_bitstring"
fi
if [[ -n "$key_bitstring" ]]; then
- key_bitstring="$(asciihex_to_binary "$key_bitstring" | $OPENSSL pkey -pubin -inform DER 2>$ERRFILE)"
+ key_bitstring="$(hex2binary "$key_bitstring" | $OPENSSL pkey -pubin -inform DER 2>$ERRFILE)"
if [[ -z "$key_bitstring" ]] && [[ $DEBUG -ge 2 ]]; then
if [[ -n "$named_curve_str" ]]; then
prln_warning "Your $OPENSSL doesn't support $named_curve_str"
@@ -13960,7 +13998,7 @@ parse_tls_serverhello() {
return 1
fi
offset=$((offset+2))
- asciihex_to_binary "${tls_serverhello_ascii:offset:protocol_len}" >> "$TMPFILE"
+ hex2binary "${tls_serverhello_ascii:offset:protocol_len}" >> "$TMPFILE"
offset=$((offset+protocol_len))
[[ $j+$protocol_len+2 -lt $extension_len ]] && echo -n ", " >> $TMPFILE
done
@@ -14217,7 +14255,7 @@ parse_tls_serverhello() {
tmpfile_handle ${FUNCNAME[0]}.txt
return 1
fi
- asciihex_to_binary "${tls_certificate_ascii:12:certificate_len}" | \
+ hex2binary "${tls_certificate_ascii:12:certificate_len}" | \
$OPENSSL x509 -inform DER -outform PEM -out "$HOSTCERT" 2>$ERRFILE
if [[ $? -ne 0 ]]; then
debugme echo "Malformed certificate in Certificate Handshake message in ServerHello."
@@ -14251,7 +14289,7 @@ parse_tls_serverhello() {
tmpfile_handle ${FUNCNAME[0]}.txt
return 1
fi
- pem_certificate="$(asciihex_to_binary "${tls_certificate_ascii:i:certificate_len}" | \
+ pem_certificate="$(hex2binary "${tls_certificate_ascii:i:certificate_len}" | \
$OPENSSL x509 -inform DER -outform PEM 2>$ERRFILE)"
if [[ $? -ne 0 ]]; then
debugme echo "Malformed certificate in Certificate Handshake message in ServerHello."
@@ -14319,10 +14357,10 @@ parse_tls_serverhello() {
echo "OCSP response:" >> $TMPFILE
echo "===============================================================================" >> $TMPFILE
if [[ -n "$hostcert_issuer" ]]; then
- asciihex_to_binary "$STAPLED_OCSP_RESPONSE" | \
+ hex2binary "$STAPLED_OCSP_RESPONSE" | \
$OPENSSL ocsp -no_nonce -CAfile $TEMPDIR/intermediatecerts.pem -issuer $hostcert_issuer -cert $HOSTCERT -respin /dev/stdin -resp_text >> $TMPFILE 2>$ERRFILE
else
- asciihex_to_binary "$STAPLED_OCSP_RESPONSE" | \
+ hex2binary "$STAPLED_OCSP_RESPONSE" | \
$OPENSSL ocsp -respin /dev/stdin -resp_text >> $TMPFILE 2>$ERRFILE
fi
echo "===============================================================================" >> $TMPFILE
@@ -14448,9 +14486,18 @@ 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 -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=""
+ ephemeral_param="$($OPENSSL pkey -pubin -text_pub -noout 2>>$ERRFILE <<< "$key_bitstring")"
+ # OpenSSL 3.0.0 outputs the group name rather than the actual parameter values for some named groups.
+ if [[ "$ephemeral_param" =~ GROUP: ]]; then
+ ephemeral_param="${ephemeral_param#*GROUP: }"
+ rfc7919_param="${named_curve_str# }"
+ rfc7919_param="${rfc7919_param%,}"
+ [[ "$ephemeral_param" =~ $rfc7919_param ]] || named_curve_str=""
+ else
+ ephemeral_param="$(grep -EA 1000 "prime:|P:" <<< "$ephemeral_param")"
+ rfc7919_param="$($OPENSSL pkey -text_pub -noout 2>>$ERRFILE <<< "${TLS13_KEY_SHARES[named_curve]}" | grep -EA 1000 "prime:|P:")"
+ [[ "$ephemeral_param" != "$rfc7919_param" ]] && named_curve_str=""
+ fi
fi
[[ $DEBUG -ge 3 ]] && [[ $dh_bits -ne 0 ]] && echo -e " dh_bits: DH,$named_curve_str $dh_bits bits"
@@ -15692,7 +15739,7 @@ receive_app_data() {
[[ -z "$ciphertext" ]] && break
done
APP_TRAF_KEY_INFO="$tls_version $cipher $server_key $server_iv $server_seq $client_key $client_iv $client_seq"
- asciihex_to_binary "$plaintext" > "$TMPFILE"
+ hex2binary "$plaintext" > "$TMPFILE"
return 0
}
@@ -15787,16 +15834,16 @@ run_heartbleed(){
out "likely "
pr_svrty_critical "VULNERABLE (NOT ok)"
[[ $DEBUG -lt 3 ]] && tm_out ", use debug >=3 to confirm"
- fileout "$jsonID" "CRITICAL" "VULNERABLE $cve" "$cwe" "$hint"
+ fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" "$hint"
fi
else
pr_svrty_critical "VULNERABLE (NOT ok)"
- fileout "$jsonID" "CRITICAL" "VULNERABLE $cve" "$cwe" "$hint"
+ fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" "$hint"
set_grade_cap "F" "Vulnerable to Heartbleed"
fi
else
pr_svrty_best "not vulnerable (OK)"
- fileout "$jsonID" "OK" "not vulnerable $cve" "$cwe"
+ fileout "$jsonID" "OK" "not vulnerable" "$cve" "$cwe"
fi
fi
outln
@@ -16034,7 +16081,7 @@ run_ticketbleed() {
[[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for Ticketbleed vulnerability " && outln
pr_bold " Ticketbleed"; out " ($cve), experiment. "
- if [[ "$SERVICE" != HTTP ]] && ! "$CLIENT_AUTH"; then
+ if [[ "$SERVICE" != HTTP ]] && [[ "$CLIENT_AUTH" != required ]]; then
outln "-- (applicable only for HTTPS)"
fileout "$jsonID" "INFO" "not applicable, not HTTP" "$cve" "$cwe"
return 0
@@ -16364,7 +16411,7 @@ run_renego() {
[[ $DEBUG -ge 1 ]] && out ", no renegotiation support in TLS 1.3 only servers"
outln
fileout "$jsonID" "OK" "not vulnerable, TLS 1.3 only" "$cve" "$cwe"
- elif "$CLIENT_AUTH"; then
+ elif [[ "$CLIENT_AUTH" == required ]]; then
prln_warning "client x509-based authentication prevents this from being tested"
fileout "$jsonID" "WARN" "client x509-based authentication prevents this from being tested"
sec_client_renego=1
@@ -16487,14 +16534,14 @@ run_crime() {
ret=1
elif grep -a Compression $TMPFILE | grep -aq NONE >/dev/null; then
pr_svrty_good "not vulnerable (OK)"
- if [[ $SERVICE != HTTP ]] && ! "$CLIENT_AUTH"; then
+ if [[ $SERVICE != HTTP ]] && [[ "$CLIENT_AUTH" != required ]]; then
out " (not using HTTP anyway)"
fileout "CRIME_TLS" "OK" "not vulnerable (not using HTTP anyway)" "$cve" "$cwe"
else
fileout "CRIME_TLS" "OK" "not vulnerable" "$cve" "$cwe"
fi
else
- if [[ $SERVICE == HTTP ]] || "$CLIENT_AUTH"; then
+ if [[ $SERVICE == HTTP ]] || [[ "$CLIENT_AUTH" == required ]]; then
pr_svrty_high "VULNERABLE (NOT ok)"
fileout "CRIME_TLS" "HIGH" "VULNERABLE" "$cve" "$cwe" "$hint"
else
@@ -16600,11 +16647,11 @@ run_breach() {
local detected_compression=""
local get_command=""
- [[ $SERVICE != HTTP ]] && ! "$CLIENT_AUTH" && return 7
+ [[ $SERVICE != HTTP ]] && [[ "$CLIENT_AUTH" != required ]] && return 7
[[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for BREACH (HTTP compression) vulnerability " && outln
pr_bold " BREACH"; out " ($cve) "
- if "$CLIENT_AUTH"; then
+ if [[ "$CLIENT_AUTH" == required ]]; then
outln "cannot be tested (server side requires x509 authentication)"
fileout "$jsonID" "INFO" "was not tested, server side requires x509 authentication" "$cve" "$cwe"
fi
@@ -17220,13 +17267,31 @@ get_common_prime() {
local jsonID2="$1"
local key_bitstring="$2"
local spaces="$3"
- local dh_p=""
+ local pubkey dh_p=""
local -i subret=0
local common_primes_file="$TESTSSL_INSTALL_DIR/etc/common-primes.txt"
local -i lineno_matched=0
"$HAS_PKEY" || return 2
- dh_p="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | awk '/prime:|prime P:/,/generator:|generator G:/' | grep -Ev "prime|generator")"
+ pubkey="$($OPENSSL pkey -pubin -text_pub -noout 2>>$ERRFILE <<< "$key_bitstring")"
+ if [[ "$pubkey" =~ GROUP: ]]; then
+ DH_GROUP_OFFERED="${pubkey#*GROUP: }"
+ case "$DH_GROUP_OFFERED" in
+ modp_1536) DH_GROUP_OFFERED="RFC3526/Oakley Group 5" ;;
+ modp_2048) DH_GROUP_OFFERED="RFC3526/Oakley Group 14" ;;
+ modp_3072) DH_GROUP_OFFERED="RFC3526/Oakley Group 15" ;;
+ modp_4096) DH_GROUP_OFFERED="RFC3526/Oakley Group 16" ;;
+ modp_6144) DH_GROUP_OFFERED="RFC3526/Oakley Group 17" ;;
+ modp_8192) DH_GROUP_OFFERED="RFC3526/Oakley Group 18" ;;
+ dh_1024_160) DH_GROUP_OFFERED="RFC5114/1024-bit DSA group with 160-bit prime order subgroup" ;;
+ dh_2048_224) DH_GROUP_OFFERED="RFC5114/2048-bit DSA group with 224-bit prime order subgroup" ;;
+ dh_2048_256) DH_GROUP_OFFERED="RFC5114/2048-bit DSA group with 256-bit prime order subgroup" ;;
+ esac
+ pubkey="$(awk -F'(' '/Public-Key/ { print $2 }' <<< "$pubkey")"
+ DH_GROUP_LEN_P="${pubkey%% bit*}"
+ return 0
+ fi
+ dh_p="$(awk '/prime:|P:/,/generator:|G:/' <<< "$pubkey" | grep -Ev "prime|P:|generator|G:")"
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}))"
@@ -18972,7 +19037,7 @@ run_robot() {
# should be a length that makes total length of $padded_pms
# the same as the length of the public key. should contain no 00 bytes.
pubkeybits="$($OPENSSL x509 -noout -pubkey -in $HOSTCERT 2>>$ERRFILE | \
- $OPENSSL pkey -pubin -text 2>>$ERRFILE | awk -F'(' '/Public-Key/ { print $2 }')"
+ $OPENSSL pkey -pubin -text_pub 2>>$ERRFILE | awk -F'(' '/Public-Key/ { print $2 }')"
pubkeybits="${pubkeybits%%bit*}"
pubkeybytes=$pubkeybits/8
[[ $((pubkeybits%8)) -ne 0 ]] && pubkeybytes+=1
@@ -18996,7 +19061,7 @@ run_robot() {
esac
# Encrypt the padded premaster secret using the server's public key.
- encrypted_pms="$(asciihex_to_binary "$padded_pms" | \
+ encrypted_pms="$(hex2binary "$padded_pms" | \
$OPENSSL pkeyutl -encrypt -certin -inkey $HOSTCERT -pkeyopt rsa_padding_mode:none 2>/dev/null | \
hexdump -v -e '16/1 "%02x"')"
if [[ -z "$encrypted_pms" ]]; then
@@ -19451,7 +19516,7 @@ find_socat() {
if [[ $? -ne 0 ]]; then
return 1
else
- if [[ -x $result ]] && $result -V | grep -iaq 'socat version' ; then
+ if [[ -x $result ]] && $result -V 2>&1 | grep -iaq 'socat version' ; then
SOCAT=$result
return 0
fi
@@ -19749,6 +19814,7 @@ HAS_IDN: $HAS_IDN
HAS_IDN2: $HAS_IDN2
HAS_AVAHIRESOLVE: $HAS_AVAHIRESOLVE
HAS_DIG_NOIDNOUT: $HAS_DIG_NOIDNOUT
+HAS_XXD: $HAS_XXD
PATH: $PATH
PROG_NAME: $PROG_NAME
@@ -20604,26 +20670,158 @@ check_proxy() {
fi
}
+# Given the ASCII-HEX of a DER-encoded distinguished name, return the string
+# representation of the name.
+print_dn() {
+ local dn="$1"
+ local cert name
+ local -i len
+
+ # Use $OPENSSL to print the DN by creating a certificate containing the DN
+ # as the issuer and then having $OPENSSL print the issuer field in the
+ # resulting certificate.
+
+ # Create the to-be-signed portion of the certificate: version || serialNumber || signature || issuer || validity || subject || subjectPublicKeyInfo
+ # with the DN to be printed being the issuer.
+ cert="A003020102020100300A06082A8648CE3D040302${dn}301E170D3139303830353038333030305A170D3139303830353038333030305A30003019301306072A8648CE3D020106082A8648CE3D030107030200FF"
+
+ # Make a SEQUENCE of the to-be-signed portion of the certificate.
+ len=$((${#cert}/2))
+ if [[ $len -lt 128 ]]; then
+ cert="30$(printf "%02x" $len)$cert"
+ elif [[ $len -lt 256 ]]; then
+ cert="3081$(printf "%02x" $len)$cert"
+ else
+ cert="3082$(printf "%04x" $len)$cert"
+ fi
+
+ # Append a signature algorithm and signature value to the end of the
+ # to-be-signed portion of the certificate and then make a SEQUENCE of
+ # the result.
+ cert+="300A06082A8648CE3D040302030200FF"
+ len=$((${#cert}/2))
+ if [[ $len -lt 128 ]]; then
+ cert="30$(printf "%02x" $len)$cert"
+ elif [[ $len -lt 256 ]]; then
+ cert="3081$(printf "%02x" $len)$cert"
+ else
+ cert="3082$(printf "%04x" $len)$cert"
+ fi
+ # Use the LDAP String Representation of Distinguished Names (RFC 2253),
+ # The current specification is in RFC 4514.
+ name="$(hex2binary "$cert" | $OPENSSL x509 -issuer -noout -inform DER -nameopt RFC2253 2>/dev/null)"
+ name="${name#issuer=}"
+ tm_out "$(strip_leading_space "$name")"
+ return 0
+}
+
+# Given the OpenSSL output of a response from a TLS server (with the -msg option)
+# in which the response includes a CertificateRequest message, return the list of
+# distinguished names that are in the CA list.
+extract_calist() {
+ local response="$1"
+ local is_tls13=false
+ local certreq calist="" certtypes sigalgs dn
+ local calist_string=""
+ local -i len type
+
+ # Determine whether this is a TLS 1.3 response, since the information
+ # is encoded in a different place for TLS 1.3.
+ [[ "$response" =~ \<\<\<\ TLS\ 1.3[\,]?\ Handshake\ \[length\ [0-9a-fA-F]*\]\,\ CertificateRequest ]] && is_tls13=true
+
+ # Extract just the CertificateRequest message as an ASCII-HEX string.
+ certreq="${response##*CertificateRequest}"
+ certreq="0d${certreq#*0d}"
+ certreq="${certreq%%<<<*}"
+ certreq="$(strip_spaces "$(newline_to_spaces "$certreq")")"
+ certreq="${certreq:8}"
+
+ # Get the list of DNs from the CertificateRequest message.
+ if "$is_tls13"; then
+ # struct {
+ # opaque certificate_request_context<0..2^8-1>;
+ # Extension extensions<2..2^16-1>;
+ # } CertificateRequest;
+ len=2*$(hex2dec "${certreq:0:2}")
+ certreq="${certreq:$((len+2))}"
+ len=2*$(hex2dec "${certreq:0:4}")
+ certreq="${certreq:4}"
+ while true; do
+ [[ -z "$certreq" ]] && break
+ type=$(hex2dec "${certreq:0:4}")
+ len=2*$(hex2dec "${certreq:4:4}")
+ if [[ $type -eq 47 ]]; then
+ # This is the certificate_authorities extension
+ calist="${certreq:8:len}"
+ len=2*$(hex2dec "${calist:0:4}")
+ calist="${calist:4:len}"
+ break
+ fi
+ certreq="${certreq:$((len+8))}"
+ done
+ else
+ # struct {
+ # ClientCertificateType certificate_types<1..2^8-1>;
+ # SignatureAndHashAlgorithm
+ # supported_signature_algorithms<2^16-1>;
+ # DistinguishedName certificate_authorities<0..2^16-1>;
+ # } CertificateRequest;
+ len=2*$(hex2dec "${certreq:0:2}")
+ certtypes="${certreq:2:len}"
+ certreq="${certreq:$((len+2))}"
+ len=2*$(hex2dec "${certreq:0:4}")
+ sigalgs="${certreq:4:len}"
+ certreq="${certreq:$((len+4))}"
+ len=2*$(hex2dec "${certreq:0:4}")
+ calist="${certreq:4:len}"
+ fi
+ # Convert each DN to a string.
+ while true; do
+ [[ -z "$calist" ]] && break
+ len=2*$(hex2dec "${calist:0:4}")
+ dn="${calist:4:len}"
+ calist_string+="$(print_dn "$dn")\n"
+ calist="${calist:$((len+4))}"
+ done
+ [[ -z "$calist_string" ]] && calist_string="empty"
+ tm_out "$calist_string"
+ return 0
+}
# this is only being called from determine_optimal_proto in order to check whether we have a server
# with client authentication, a server with no SSL session ID switched off
#
sclient_auth() {
- [[ $1 -eq 0 ]] && return 0 # no client auth (CLIENT_AUTH=false is preset globally)
- if [[ -n $(awk '/Master-Key: / { print $2 }' "$2") ]]; then # connect succeeded
- if grep -q '^<<< .*CertificateRequest' "$2"; then # CertificateRequest message in -msg
- CLIENT_AUTH=true
+ local server_hello="$(cat -v "$2")"
+ local re='Master-Key: ([^\
+]*)'
+ local connect_success=false
+
+ [[ $1 -eq 0 ]] && connect_success=true
+ ! "$connect_success" && [[ "$server_hello" =~ $re ]] && \
+ [[ -n "${BASH_REMATCH[1]}" ]] && connect_success=true
+ ! "$connect_success" && \
+ [[ "$server_hello" =~ (New|Reused)\,\ (SSLv[23]|TLSv1(\.[0-3])?(\/SSLv3)?)\,\ Cipher\ is\ ([A-Z0-9]+-[A-Za-z0-9\-]+|TLS_[A-Za-z0-9_]+) ]] && \
+ connect_success=true
+ if "$connect_success"; then
+ if [[ "$server_hello" =~ \<\<\<\ (SSL\ [23]|TLS\ 1)(\.[0-3])?[\,]?\ Handshake\ \[length\ [0-9a-fA-F]*\]\,\ CertificateRequest ]]; then
+ # CertificateRequest message in -msg
+ CLIENT_AUTH="required"
+ [[ $1 -eq 0 ]] && CLIENT_AUTH="optional"
+ CLIENT_AUTH_CA_LIST="$(extract_calist "$server_hello")"
return 0
fi
- if [[ -z $(awk '/Session-ID: / { print $2 }' "$2") ]]; then # probably no SSL session
- if [[ 2 -eq $(grep -c CERTIFICATE "$2") ]]; then # do another sanity check to be sure
- CLIENT_AUTH=false
- NO_SSL_SESSIONID=true # NO_SSL_SESSIONID is preset globally to false for all other cases
+ [[ $1 -eq 0 ]] && return 0
+ if [[ ! "$server_hello" =~ Session-ID:\ [a-fA-F0-9]{2,64} ]]; then # probably no SSL session
+ # do another sanity check to be sure
+ if [[ "$server_hello" =~ \-\-\-BEGIN\ CERTIFICATE\-\-\-.*\-\-\-END\ CERTIFICATE\-\-\- ]]; then
+ CLIENT_AUTH="none"
+ NO_SSL_SESSIONID=true # NO_SSL_SESSIONID is preset globally to false for all other cases
return 0
fi
fi
fi
- # what's left now is: master key empty, handshake returned not successful, session ID empty --> not successful
+ # what's left now is: no protocol and ciphersuite specified, handshake returned not successful, session ID empty --> not successful
return 1
}
@@ -21148,7 +21346,7 @@ run_mx_all_ips() {
word="the only"
fi
mxport=${2:-25}
- if [[ -n "$LOGFILE" ]]; then
+ if [[ -n "$LOGFILE" ]] || [[ -n "$PARENT_LOGFILE" ]]; then
prepare_logging
else
prepare_logging "${FNAME_PREFIX}mx-$1"
@@ -21234,35 +21432,53 @@ create_mass_testing_cmdline() {
elif [[ "$testing_type" == serial ]]; then
if "$JSONHEADER" && ( [[ "$cmd" =~ --jsonfile-pretty ]] || [[ "$cmd" =~ -oJ ]] ); then
>"$TEMPDIR/jsonfile_child.json"
- MASS_TESTING_CMDLINE[nr_cmds]="--jsonfile-pretty=$TEMPDIR/jsonfile_child.json"
+ MASS_TESTING_CMDLINE[nr_cmds]="--jsonfile-pretty-parent=$TEMPDIR/jsonfile_child.json"
# next is the jsonfile itself, as no '=' was supplied
[[ "$cmd" == --jsonfile-pretty ]] && skip_next=true
[[ "$cmd" == -oJ ]] && skip_next=true
elif "$JSONHEADER" && ( [[ "$cmd" =~ --jsonfile ]] || [[ "$cmd" =~ -oj ]] ); then
>"$TEMPDIR/jsonfile_child.json"
- MASS_TESTING_CMDLINE[nr_cmds]="--jsonfile=$TEMPDIR/jsonfile_child.json"
+ MASS_TESTING_CMDLINE[nr_cmds]="--jsonfile-parent=$TEMPDIR/jsonfile_child.json"
# next is the jsonfile itself, as no '=' was supplied
[[ "$cmd" == --jsonfile ]] && skip_next=true
[[ "$cmd" == -oj ]] && skip_next=true
+ elif "$CSVHEADER" && ( [[ "$cmd" =~ --csvfile ]] || [[ "$cmd" =~ -oC ]] ); then
+ outfile_arg="$(parse_opt_equal_sign "$cmd" "${CMDLINE_ARRAY[i+1]}")"
+ MASS_TESTING_CMDLINE[nr_cmds]="--csvfile-parent=$outfile_arg"
+ # next is the filename itself, as no '=' was supplied
+ [[ "$cmd" == --csvfile ]] && skip_next=true
+ [[ "$cmd" == -oC ]] && skip_next=true
+ elif "$HTMLHEADER" && ( [[ "$cmd" =~ --htmlfile ]] || [[ "$cmd" =~ -oH ]] ); then
+ outfile_arg="$(parse_opt_equal_sign "$cmd" "${CMDLINE_ARRAY[i+1]}")"
+ MASS_TESTING_CMDLINE[nr_cmds]="--htmlfile-parent=$outfile_arg"
+ # next is the filename itself, as no '=' was supplied
+ [[ "$cmd" == --htmlfile ]] && skip_next=true
+ [[ "$cmd" == -oH ]] && skip_next=true
+ elif ( [[ "$cmd" =~ --logfile ]] || [[ "$cmd" =~ -oL ]] ); then
+ outfile_arg="$(parse_opt_equal_sign "$cmd" "${CMDLINE_ARRAY[i+1]}")"
+ MASS_TESTING_CMDLINE[nr_cmds]="--logfile-parent=$outfile_arg"
+ # next is the filename itself, as no '=' was supplied
+ [[ "$cmd" == --logfile ]] && skip_next=true
+ [[ "$cmd" == -oL ]] && skip_next=true
elif "$JSONHEADER" && ( [[ "$cmd" =~ --outFile ]] || [[ "$cmd" =~ -oA ]] ); then
outfile_arg="$(parse_opt_equal_sign "$cmd" "${CMDLINE_ARRAY[i+1]}")"
>"$TEMPDIR/jsonfile_child.json"
- MASS_TESTING_CMDLINE[nr_cmds]="-oJ=$TEMPDIR/jsonfile_child.json"
+ MASS_TESTING_CMDLINE[nr_cmds]="--jsonfile-pretty-parent=$TEMPDIR/jsonfile_child.json"
nr_cmds+=1
- MASS_TESTING_CMDLINE[nr_cmds]="-oC=$outfile_arg.csv"
+ MASS_TESTING_CMDLINE[nr_cmds]="--csvfile-parent=$outfile_arg.csv"
nr_cmds+=1
- MASS_TESTING_CMDLINE[nr_cmds]="-oH=$outfile_arg.html"
+ MASS_TESTING_CMDLINE[nr_cmds]="--htmlfile-parent=$outfile_arg.html"
# next is the filename itself, as no '=' was supplied
[[ "$cmd" == --outFile ]] && skip_next=true
[[ "$cmd" == -oA ]] && skip_next=true
elif "$JSONHEADER" && ( [[ "$cmd" =~ --outfile ]] || [[ "$cmd" =~ -oa ]] ); then
outfile_arg="$(parse_opt_equal_sign "$cmd" "${CMDLINE_ARRAY[i+1]}")"
>"$TEMPDIR/jsonfile_child.json"
- MASS_TESTING_CMDLINE[nr_cmds]="-oj=$TEMPDIR/jsonfile_child.json"
+ MASS_TESTING_CMDLINE[nr_cmds]="--jsonfile-parent=$TEMPDIR/jsonfile_child.json"
nr_cmds+=1
- MASS_TESTING_CMDLINE[nr_cmds]="-oC=$outfile_arg.csv"
+ MASS_TESTING_CMDLINE[nr_cmds]="--csvfile-parent=$outfile_arg.csv"
nr_cmds+=1
- MASS_TESTING_CMDLINE[nr_cmds]="-oH=$outfile_arg.html"
+ MASS_TESTING_CMDLINE[nr_cmds]="--htmlfile-parent=$outfile_arg.html"
# next is the filename itself, as no '=' was supplied
[[ "$cmd" == --outfile ]] && skip_next=true
[[ "$cmd" == -oa ]] && skip_next=true
@@ -21277,7 +21493,7 @@ create_mass_testing_cmdline() {
# file name to each child process. If is a
# directory, then just pass it on to the child processes.
if "$JSONHEADER"; then
- MASS_TESTING_CMDLINE[nr_cmds]="--jsonfile=$TEMPDIR/jsonfile_${test_number}.json"
+ MASS_TESTING_CMDLINE[nr_cmds]="--jsonfile-parent=$TEMPDIR/jsonfile_${test_number}.json"
# next is the jsonfile itself, as no '=' was supplied
[[ "$cmd" == --jsonfile ]] && skip_next=true
[[ "$cmd" == -oj ]] && skip_next=true
@@ -21287,7 +21503,7 @@ create_mass_testing_cmdline() {
;;
--jsonfile-pretty|--jsonfile-pretty=*|-oJ|-oJ=*)
if "$JSONHEADER"; then
- MASS_TESTING_CMDLINE[nr_cmds]="--jsonfile-pretty=$TEMPDIR/jsonfile_${test_number}.json"
+ MASS_TESTING_CMDLINE[nr_cmds]="--jsonfile-pretty-parent=$TEMPDIR/jsonfile_${test_number}.json"
[[ "$cmd" == --jsonfile-pretty ]] && skip_next=true
[[ "$cmd" == -oJ ]] && skip_next=true
else
@@ -21296,7 +21512,7 @@ create_mass_testing_cmdline() {
;;
--csvfile|--csvfile=*|-oC|-oC=*)
if "$CSVHEADER"; then
- MASS_TESTING_CMDLINE[nr_cmds]="--csvfile=$TEMPDIR/csvfile_${test_number}.csv"
+ MASS_TESTING_CMDLINE[nr_cmds]="--csvfile-parent=$TEMPDIR/csvfile_${test_number}.csv"
[[ "$cmd" == --csvfile ]] && skip_next=true
[[ "$cmd" == -oC ]] && skip_next=true
else
@@ -21305,20 +21521,26 @@ create_mass_testing_cmdline() {
;;
--htmlfile|--htmlfile=*|-oH|-oH=*)
if "$HTMLHEADER"; then
- MASS_TESTING_CMDLINE[nr_cmds]="--htmlfile=$TEMPDIR/htmlfile_${test_number}.html"
+ MASS_TESTING_CMDLINE[nr_cmds]="--htmlfile-parent=$TEMPDIR/htmlfile_${test_number}.html"
[[ "$cmd" == --htmlfile ]] && skip_next=true
[[ "$cmd" == -oH ]] && skip_next=true
else
MASS_TESTING_CMDLINE[nr_cmds]="$cmd"
fi
;;
+ --logfile|--logfile=*|-oL|-oL=*)
+ outfile_arg="$(parse_opt_equal_sign "$cmd" "${CMDLINE_ARRAY[i+1]}")"
+ MASS_TESTING_CMDLINE[nr_cmds]="--logfile-parent=$outfile_arg"
+ [[ "$cmd" == --logfile ]] && skip_next=true
+ [[ "$cmd" == -oL ]] && skip_next=true
+ ;;
--outfile|--outfile=*|-oa|-oa=*)
if "$JSONHEADER"; then
- MASS_TESTING_CMDLINE[nr_cmds]="-oj=$TEMPDIR/jsonfile_${test_number}.json"
+ MASS_TESTING_CMDLINE[nr_cmds]="--jsonfile-parent=$TEMPDIR/jsonfile_${test_number}.json"
nr_cmds+=1
- MASS_TESTING_CMDLINE[nr_cmds]="-oC=$TEMPDIR/csvfile_${test_number}.csv"
+ MASS_TESTING_CMDLINE[nr_cmds]="--csvfile-parent=$TEMPDIR/csvfile_${test_number}.csv"
nr_cmds+=1
- MASS_TESTING_CMDLINE[nr_cmds]="-oH=$TEMPDIR/htmlfile_${test_number}.html"
+ MASS_TESTING_CMDLINE[nr_cmds]="--htmlfile-parent=$TEMPDIR/htmlfile_${test_number}.html"
# next is the filename itself, as no '=' was supplied
[[ "$cmd" == --outfile ]] && skip_next=true
[[ "$cmd" == -oa ]] && skip_next=true
@@ -21328,11 +21550,11 @@ create_mass_testing_cmdline() {
;;
--outFile|--outFile=*|-oA|-oA=*)
if "$JSONHEADER"; then
- MASS_TESTING_CMDLINE[nr_cmds]="-oJ=$TEMPDIR/jsonfile_${test_number}.json"
+ MASS_TESTING_CMDLINE[nr_cmds]="--jsonfile-pretty-parent=$TEMPDIR/jsonfile_${test_number}.json"
nr_cmds+=1
- MASS_TESTING_CMDLINE[nr_cmds]="-oC=$TEMPDIR/csvfile_${test_number}.csv"
+ MASS_TESTING_CMDLINE[nr_cmds]="--csvfile-parent=$TEMPDIR/csvfile_${test_number}.csv"
nr_cmds+=1
- MASS_TESTING_CMDLINE[nr_cmds]="-oH=$TEMPDIR/htmlfile_${test_number}.html"
+ MASS_TESTING_CMDLINE[nr_cmds]="--htmlfile-parent=$TEMPDIR/htmlfile_${test_number}.html"
# next is the filename itself, as no '=' was supplied
[[ "$cmd" == --outFile ]] && skip_next=true
[[ "$cmd" == -oA ]] && skip_next=true
@@ -22082,6 +22304,11 @@ check_base_requirements() {
fatal "${binary} is from busybox. Please install a regular binary" $ERR_RESOURCE
fi
done
+ # testssl.sh works without xxd, but using xxd is faster. The following checks that the xxd
+ # binary is available and (just to be safe) that "xxd -r -p" works as expected.
+ if type -p xxd &> /dev/null && [[ "$(xxd -r -p <<< "30313233343536373839" 2>/dev/null)" == 0123456789 ]]; then
+ HAS_XXD=true
+ fi
}
parse_cmd_line() {
@@ -22429,6 +22656,15 @@ parse_cmd_line() {
[[ $? -eq 0 ]] && shift
do_logging=true
;;
+ --logfile-parent|--logfile-parent=*)
+ if ! "$CHILD_MASS_TESTING"; then
+ tmln_warning "$0: unrecognized option \"$1\"" 1>&2;
+ help 1
+ fi
+ PARENT_LOGFILE="$(parse_opt_equal_sign "$1" "$2")"
+ [[ $? -eq 0 ]] && shift
+ do_logging=true
+ ;;
--json)
"$do_pretty_json" && fatal "flat and pretty JSON output are mutually exclusive" $ERR_CMDLINE
"$do_json" && fatal "--json and --jsonfile are mutually exclusive" $ERR_CMDLINE
@@ -22445,6 +22681,15 @@ parse_cmd_line() {
[[ $? -eq 0 ]] && shift
do_json=true
;;
+ --jsonfile-parent|--jsonfile-parent=*)
+ if ! "$CHILD_MASS_TESTING"; then
+ tmln_warning "$0: unrecognized option \"$1\"" 1>&2;
+ help 1
+ fi
+ PARENT_JSONFILE="$(parse_opt_equal_sign "$1" "$2")"
+ [[ $? -eq 0 ]] && shift
+ do_json=true
+ ;;
--json-pretty)
"$do_json" && fatal "flat and pretty JSON output are mutually exclusive" $ERR_CMDLINE
"$do_pretty_json" && fatal "--json-pretty and --jsonfile-pretty are mutually exclusive" $ERR_CMDLINE
@@ -22460,6 +22705,15 @@ parse_cmd_line() {
[[ $? -eq 0 ]] && shift
do_pretty_json=true
;;
+ --jsonfile-pretty-parent|--jsonfile-pretty-parent=*)
+ if ! "$CHILD_MASS_TESTING"; then
+ tmln_warning "$0: unrecognized option \"$1\"" 1>&2;
+ help 1
+ fi
+ PARENT_JSONFILE="$(parse_opt_equal_sign "$1" "$2")"
+ [[ $? -eq 0 ]] && shift
+ do_pretty_json=true
+ ;;
--severity|--severity=*)
set_severity_level "$(parse_opt_equal_sign "$1" "$2")"
[[ $? -eq 0 ]] && shift
@@ -22481,6 +22735,15 @@ parse_cmd_line() {
[[ $? -eq 0 ]] && shift
do_csv=true
;;
+ --csvfile-parent|--csvfile-parent=*)
+ if ! "$CHILD_MASS_TESTING"; then
+ tmln_warning "$0: unrecognized option \"$1\"" 1>&2;
+ help 1
+ fi
+ PARENT_CSVFILE="$(parse_opt_equal_sign "$1" "$2")"
+ [[ $? -eq 0 ]] && shift
+ do_csv=true
+ ;;
--html)
"$do_html" && fatal "two --html* arguments" $ERR_CMDLINE
if [[ "$2" =~ \.(htm|html|HTM|HTML)$ ]]; then
@@ -22495,6 +22758,15 @@ parse_cmd_line() {
[[ $? -eq 0 ]] && shift
do_html=true
;;
+ --htmlfile-parent|--htmlfile-parent=*)
+ if ! "$CHILD_MASS_TESTING"; then
+ tmln_warning "$0: unrecognized option \"$1\"" 1>&2;
+ help 1
+ fi
+ PARENT_HTMLFILE="$(parse_opt_equal_sign "$1" "$2")"
+ [[ $? -eq 0 ]] && shift
+ do_html=true
+ ;;
--outfile|--outfile=*|-oa|-oa=*)
( "$do_html" || "$do_json" || "$do_pretty_json" || "$do_csv" || "$do_logging" ) && fatal "check your arguments four multiple file output options" $ERR_CMDLINE
outfile_arg="$(parse_opt_equal_sign "$1" "$2")"