mirror of
https://github.com/drwetter/testssl.sh.git
synced 2024-12-28 12:29:44 +01:00
Reorder functions and some variables
For a fresh start it seemed a good idea to cleanup the order of functions and some variables so that those with the same functionality are somewhat grouped. Some of the functions have now a header and a foooter to make it easier to spot and use then. Also for added future functions the hope is that they will be put where they better fit
This commit is contained in:
parent
67598e824f
commit
d44a643fab
625
testssl.sh
625
testssl.sh
@ -51,6 +51,8 @@
|
||||
# cool and unique -- they are -- but probably you can achieve e.g. the same result with my favorite
|
||||
# interactive shell: zsh (zmodload zsh/net/socket -- checkout zsh/net/tcp) too! Oh, and btw.
|
||||
# ksh93 has socket support too.
|
||||
# Also bash is quite powerful if you use it appropriately: It can operate on patterns, process lines
|
||||
# and deal perfectly with regular expressions -- without external binaries.
|
||||
# /bin/bash though is way more often used within Linux and it's perfect for cross platform support.
|
||||
# MacOS X has it and also under Windows the MSYS2 extension or Cygwin as well as Bash on Windows (WSL)
|
||||
# has /bin/bash.
|
||||
@ -135,62 +137,17 @@ declare -r PROG_NAME="$(basename "$0")"
|
||||
declare -r RUN_DIR="$(dirname "$0")"
|
||||
declare -r SYSTEM="$(uname -s)"
|
||||
declare -r SYSTEMREV="$(uname -r)"
|
||||
SYSTEM2="" # currently only being used for WSL = bash on windows
|
||||
TESTSSL_INSTALL_DIR="${TESTSSL_INSTALL_DIR:-""}" # If you run testssl.sh and it doesn't find it necessary file automagically set TESTSSL_INSTALL_DIR
|
||||
CA_BUNDLES_PATH="${CA_BUNDLES_PATH:-""}" # You can have your stores some place else
|
||||
ADDITIONAL_CA_FILES="${ADDITIONAL_CA_FILES:-""}" # single file with a CA in PEM format or comma separated lists of them
|
||||
CIPHERS_BY_STRENGTH_FILE=""
|
||||
TLS_DATA_FILE="" # mandatory file for socket-based handshakes
|
||||
OPENSSL_LOCATION=""
|
||||
HNAME="$(hostname)"
|
||||
HNAME="${HNAME%%.*}"
|
||||
|
||||
declare CMDLINE
|
||||
CMDLINE_PARSED="" # This makes sure we don't let early fatal() write into files when files aren't created yet
|
||||
declare -r -a CMDLINE_ARRAY=("$@") # When performing mass testing, the child processes need to be sent the
|
||||
declare -a MASS_TESTING_CMDLINE # command line in the form of an array (see #702 and http://mywiki.wooledge.org/BashFAQ/050).
|
||||
|
||||
|
||||
########### Some predefinitions: date, sed (we always use test and NOT try to determine
|
||||
# capabilities by querying the OS)
|
||||
#
|
||||
HAS_GNUDATE=false
|
||||
HAS_FREEBSDDATE=false
|
||||
HAS_OPENBSDDATE=false
|
||||
if date -d @735275209 >/dev/null 2>&1; then
|
||||
if date -r @735275209 >/dev/null 2>&1; then
|
||||
# It can't do any conversion from a plain date output.
|
||||
HAS_OPENBSDDATE=true
|
||||
else
|
||||
HAS_GNUDATE=true
|
||||
fi
|
||||
fi
|
||||
# FreeBSD and OS X date(1) accept "-f inputformat", so do newer OpenBSD versions >~ 6.6.
|
||||
date -j -f '%s' 1234567 >/dev/null 2>&1 && \
|
||||
HAS_FREEBSDDATE=true
|
||||
|
||||
echo A | sed -E 's/A//' >/dev/null 2>&1 && \
|
||||
declare -r HAS_SED_E=true || \
|
||||
declare -r HAS_SED_E=false
|
||||
|
||||
########### Terminal defintions
|
||||
tty -s && \
|
||||
declare -r INTERACTIVE=true || \
|
||||
declare -r INTERACTIVE=false
|
||||
|
||||
if [[ -z $TERM_WIDTH ]]; then # no batch file and no otherwise predefined TERM_WIDTH
|
||||
if ! tput cols &>/dev/null || ! "$INTERACTIVE";then # Prevent tput errors if running non interactive
|
||||
export TERM_WIDTH=${COLUMNS:-80}
|
||||
else
|
||||
export TERM_WIDTH=${COLUMNS:-$(tput cols)} # for custom line wrapping and dashes
|
||||
fi
|
||||
fi
|
||||
TERM_CURRPOS=0 # custom line wrapping needs alter the current horizontal cursor pos
|
||||
|
||||
|
||||
########### Defining (and presetting) variables which can be changed
|
||||
#
|
||||
# Following variables make use of $ENV and can be used like "OPENSSL=<myprivate_path_to_openssl> ./testssl.sh <URI>"
|
||||
# Following variables make use of $ENV and can also be used like "<VAR>=<value> ./testssl.sh <URI>"
|
||||
declare -x OPENSSL
|
||||
OPENSSL_TIMEOUT=${OPENSSL_TIMEOUT:-""} # Default connect timeout with openssl before we call the server side unreachable
|
||||
CONNECT_TIMEOUT=${CONNECT_TIMEOUT:-""} # Default connect timeout with sockets before we call the server side unreachable
|
||||
@ -227,9 +184,12 @@ APPEND=${APPEND:-false} # append to csv/json file instead of ove
|
||||
HAS_IPv6=${HAS_IPv6:-false} # if you have OpenSSL with IPv6 support AND IPv6 networking set it to yes
|
||||
ALL_CLIENTS=${ALL_CLIENTS:-false} # do you want to run all client simulation form all clients supplied by SSLlabs?
|
||||
OFFENSIVE=${OFFENSIVE:-true} # do you want to include offensive vulnerability tests which may cause blocking by an IDS?
|
||||
ADDTL_CA_FILES="${ADDTL_CA_FILES:-""}" # single file with a CA in PEM format or comma separated lists of them
|
||||
|
||||
########### Tuning vars which cannot be set by a cmd line switch. Use instead e.g "HEADER_MAXSLEEP=10 ./testssl.sh <your_args_here>"
|
||||
#
|
||||
TESTSSL_INSTALL_DIR="${TESTSSL_INSTALL_DIR:-""}" # If you run testssl.sh and it doesn't find it necessary file automagically set TESTSSL_INSTALL_DIR
|
||||
CA_BUNDLES_PATH="${CA_BUNDLES_PATH:-""}" # You can have your CA stores some place else
|
||||
EXPERIMENTAL=${EXPERIMENTAL:-false} # a development hook which allows us to disable code
|
||||
PROXY_WAIT=${PROXY_WAIT:-20} # waiting at max 20 seconds for socket reply through proxy
|
||||
DNS_VIA_PROXY=${DNS_VIA_PROXY:-true} # do DNS lookups via proxy. --ip=proxy reverses this
|
||||
@ -273,7 +233,11 @@ declare -r UA_SNEAKY="Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Fi
|
||||
|
||||
########### Initialization part, further global vars just being declared here
|
||||
#
|
||||
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=""
|
||||
TLS_DATA_FILE="" # mandatory file for socket-based handshakes
|
||||
OPENSSL_LOCATION=""
|
||||
IKNOW_FNAME=false
|
||||
FIRST_FINDING=true # is this the first finding we are outputting to file?
|
||||
JSONHEADER=true # include JSON headers and footers in HTML file, if one is being created
|
||||
@ -438,6 +402,44 @@ declare TLS_CIPHER_EXPORT=()
|
||||
declare TLS_CIPHER_OSSL_SUPPORTED=()
|
||||
declare TLS13_OSSL_CIPHERS="TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256:TLS_AES_128_CCM_8_SHA256"
|
||||
|
||||
|
||||
########### Some predefinitions: date, sed (we always use tests for binaries and NOT try to determine
|
||||
# capabilities by querying the OS)
|
||||
#
|
||||
HAS_GNUDATE=false
|
||||
HAS_FREEBSDDATE=false
|
||||
HAS_OPENBSDDATE=false
|
||||
if date -d @735275209 >/dev/null 2>&1; then
|
||||
if date -r @735275209 >/dev/null 2>&1; then
|
||||
# It can't do any conversion from a plain date output.
|
||||
HAS_OPENBSDDATE=true
|
||||
else
|
||||
HAS_GNUDATE=true
|
||||
fi
|
||||
fi
|
||||
# FreeBSD and OS X date(1) accept "-f inputformat", so do newer OpenBSD versions >~ 6.6.
|
||||
date -j -f '%s' 1234567 >/dev/null 2>&1 && \
|
||||
HAS_FREEBSDDATE=true
|
||||
|
||||
echo A | sed -E 's/A//' >/dev/null 2>&1 && \
|
||||
declare -r HAS_SED_E=true || \
|
||||
declare -r HAS_SED_E=false
|
||||
|
||||
########### Terminal defintions
|
||||
tty -s && \
|
||||
declare -r INTERACTIVE=true || \
|
||||
declare -r INTERACTIVE=false
|
||||
|
||||
if [[ -z $TERM_WIDTH ]]; then # No batch file and no otherwise predefined TERM_WIDTH
|
||||
if ! tput cols &>/dev/null || ! "$INTERACTIVE";then # Prevent tput errors if running non interactive
|
||||
export TERM_WIDTH=${COLUMNS:-80}
|
||||
else
|
||||
export TERM_WIDTH=${COLUMNS:-$(tput cols)} # For custom line wrapping and dashes
|
||||
fi
|
||||
fi
|
||||
TERM_CURRPOS=0 # Custom line wrapping needs alter the current horizontal cursor pos
|
||||
|
||||
|
||||
########### Severity functions and globals
|
||||
#
|
||||
INFO=0
|
||||
@ -725,6 +727,94 @@ set_color_functions() {
|
||||
# FreeBSD 10 understands ESC codes like 'echo -e "\e[3mfoobar\e[23m"', but also no tput for italics
|
||||
}
|
||||
|
||||
###### START universal helper function definitions ######
|
||||
|
||||
if [[ "${BASH_VERSINFO[0]}" == 3 ]]; then
|
||||
# older bash can do this only (MacOS X), even SLES 11, see #697
|
||||
toupper() { tr 'a-z' 'A-Z' <<< "$1"; }
|
||||
tolower() { tr 'A-Z' 'a-z' <<< "$1"; }
|
||||
else
|
||||
toupper() { echo -n "${1^^}"; }
|
||||
tolower() { echo -n "${1,,}"; }
|
||||
fi
|
||||
|
||||
get_last_char() {
|
||||
echo "${1:~0}" # "${string: -1}" would work too (both also in bash 3.2)
|
||||
}
|
||||
# Checking for last char. If already a separator supplied, we don't need an additional one
|
||||
debugme() {
|
||||
[[ "$DEBUG" -ge 2 ]] && "$@"
|
||||
return 0
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
# convert decimal number between 256 and < 256*256 to hex
|
||||
dec04hex() {
|
||||
local a=$(printf "%04x" "$1")
|
||||
printf "x%02s, x%02s" "${a:0:2}" "${a:2:2}"
|
||||
}
|
||||
|
||||
|
||||
# trim spaces for BSD and old sed
|
||||
count_lines() {
|
||||
echo $(wc -l <<< "$1")
|
||||
}
|
||||
|
||||
count_words() {
|
||||
echo $(wc -w <<< "$1")
|
||||
}
|
||||
|
||||
count_ciphers() {
|
||||
echo $(wc -w <<< "${1//:/ }")
|
||||
}
|
||||
|
||||
newline_to_spaces() {
|
||||
tr '\n' ' ' <<< "$1" | sed 's/ $//'
|
||||
}
|
||||
|
||||
colon_to_spaces() {
|
||||
echo "${1//:/ }"
|
||||
}
|
||||
|
||||
strip_lf() {
|
||||
tr -d '\n' <<< "$1" | tr -d '\r'
|
||||
}
|
||||
|
||||
strip_spaces() {
|
||||
echo "${1// /}"
|
||||
}
|
||||
|
||||
# https://web.archive.org/web/20121022051228/http://codesnippets.joyent.com/posts/show/1816
|
||||
strip_leading_space() {
|
||||
printf "%s" "${1#"${1%%[![:space:]]*}"}"
|
||||
}
|
||||
strip_trailing_space() {
|
||||
printf "%s" "${1%"${1##*[![:space:]]}"}"
|
||||
}
|
||||
|
||||
is_number() {
|
||||
[[ "$1" =~ ^[1-9][0-9]*$ ]] && \
|
||||
return 0 || \
|
||||
return 1
|
||||
}
|
||||
|
||||
strip_quote() {
|
||||
# remove color codes (see http://www.commandlinefu.com/commands/view/3584/remove-color-codes-special-characters-with-sed)
|
||||
# \', leading and all trailing spaces
|
||||
@ -736,7 +826,171 @@ strip_quote() {
|
||||
|
||||
# " deconfuse vim\'s syntax highlighting ;-)
|
||||
|
||||
#################### JSON FILE FORMATTING ####################
|
||||
|
||||
is_ipv4addr() {
|
||||
local octet="(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])"
|
||||
local ipv4address="$octet\\.$octet\\.$octet\\.$octet"
|
||||
|
||||
[[ -z "$1" ]] && return 1
|
||||
# more than numbers, important for hosts like AAA.BBB.CCC.DDD.in-addr.arpa.DOMAIN.TLS
|
||||
[[ -n $(tr -d '0-9\.' <<< "$1") ]] && return 1
|
||||
|
||||
grep -Eq "$ipv4address" <<< "$1" && \
|
||||
return 0 || \
|
||||
return 1
|
||||
}
|
||||
|
||||
# a bit easier
|
||||
is_ipv6addr() {
|
||||
[[ -z "$1" ]] && return 1
|
||||
# less than 2x ":"
|
||||
[[ $(count_lines "$(tr ':' '\n' <<< "$1")") -le 1 ]] && \
|
||||
return 1
|
||||
# check which chars allowed:
|
||||
[[ -n "$(tr -d '0-9:a-fA-F ' <<< "$1" | sed -e '/^$/d')" ]] && \
|
||||
return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
###### END universal helper function definitions ######
|
||||
|
||||
###### START ServerHello/OpenSSL/F5 function definitions ######
|
||||
|
||||
#arg1: TLS 1.2 and below ciphers
|
||||
#arg2: TLS 1.3 ciphers
|
||||
#arg3: options (e.g., -V)
|
||||
actually_supported_osslciphers() {
|
||||
local tls13_ciphers="$TLS13_OSSL_CIPHERS"
|
||||
|
||||
[[ "$2" != ALL ]] && tls13_ciphers="$2"
|
||||
if "$HAS_CIPHERSUITES"; then
|
||||
$OPENSSL ciphers $3 $OSSL_CIPHERS_S -ciphersuites "$tls13_ciphers" "$1" 2>/dev/null || echo ""
|
||||
elif [[ -n "$tls13_ciphers" ]]; then
|
||||
$OPENSSL ciphers $3 $OSSL_CIPHERS_S "$tls13_ciphers:$1" 2>/dev/null || echo ""
|
||||
else
|
||||
$OPENSSL ciphers $OSSL_CIPHERS_S $3 "$1" 2>/dev/null || echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# Given a protocol (arg1) and a list of ciphers (arg2) that is formatted as
|
||||
# ", xx,xx, xx,xx, xx,xx, xx,xx" remove any TLSv1.3 ciphers if the protocol
|
||||
# is less than 04 and remove any TLSv1.2-only ciphers if the protocol is less
|
||||
# than 03.
|
||||
strip_inconsistent_ciphers() {
|
||||
local -i proto=0x$1
|
||||
local cipherlist="$2"
|
||||
|
||||
[[ $proto -lt 4 ]] && cipherlist="${cipherlist//, 13,0[0-9a-fA-F]/}"
|
||||
if [[ $proto -lt 3 ]]; then
|
||||
cipherlist="${cipherlist//, 00,3[b-fB-F]/}"
|
||||
cipherlist="${cipherlist//, 00,40/}"
|
||||
cipherlist="${cipherlist//, 00,6[7-9a-dA-D]/}"
|
||||
cipherlist="${cipherlist//, 00,9[c-fC-F]/}"
|
||||
cipherlist="${cipherlist//, 00,[abAB][0-9a-fA-F]/}"
|
||||
cipherlist="${cipherlist//, 00,[cC][0-5]/}"
|
||||
cipherlist="${cipherlist//, 16,[bB][7-9aA]/}"
|
||||
cipherlist="${cipherlist//, [cC]0,2[3-9a-fA-F]/}"
|
||||
cipherlist="${cipherlist//, [cC]0,3[01278a-fA-F]/}"
|
||||
cipherlist="${cipherlist//, [cC]0,[4-9aA][0-9a-fA-F]/}"
|
||||
cipherlist="${cipherlist//, [cC][cC],1[345]/}"
|
||||
cipherlist="${cipherlist//, [cC][cC],[aA][89a-eA-E]/}"
|
||||
fi
|
||||
echo "$cipherlist"
|
||||
return 0
|
||||
}
|
||||
|
||||
# retrieve cipher from ServerHello (via openssl)
|
||||
get_cipher() {
|
||||
local cipher=""
|
||||
local server_hello="$(cat -v "$1")"
|
||||
# This and two other following instances are not best practice and normally a useless use of "cat", see
|
||||
# https://web.archive.org/web/20160711205930/http://porkmail.org/era/unix/award.html#uucaletter
|
||||
# However there seem to be cases where the preferred $(< "$1") logic has a problem.
|
||||
# Esepcially with bash 3.2 (Mac OS X) and when on the server side binary chars
|
||||
# are returned, see https://stackoverflow.com/questions/7427262/how-to-read-a-file-into-a-variable-in-shell#22607352
|
||||
# and https://github.com/drwetter/testssl.sh/issues/1292
|
||||
# Performance measurements showed no to barely measureable penalty (1s displayed in 9 tries).
|
||||
|
||||
if [[ "$server_hello" =~ Cipher\ *:\ ([A-Z0-9]+-[A-Za-z0-9\-]+|TLS_[A-Za-z0-9_]+) ]]; then
|
||||
cipher="${BASH_REMATCH##* }"
|
||||
elif [[ "$server_hello" =~ (New|Reused)", "(SSLv[23]|TLSv1(\.[0-3])?(\/SSLv3)?)", Cipher is "([A-Z0-9]+-[A-Za-z0-9\-]+|TLS_[A-Za-z0-9_]+) ]]; then
|
||||
cipher="${BASH_REMATCH##* }"
|
||||
fi
|
||||
tm_out "$cipher"
|
||||
}
|
||||
|
||||
# retrieve protocol from ServerHello (via openssl)
|
||||
get_protocol() {
|
||||
local protocol=""
|
||||
local server_hello="$(cat -v "$1")"
|
||||
|
||||
if [[ "$server_hello" =~ Protocol\ *:\ (SSLv[23]|TLSv1(\.[0-3])?) ]]; then
|
||||
protocol="${BASH_REMATCH##* }"
|
||||
elif [[ "$server_hello" =~ (New|Reused)", TLSv1.3, Cipher is "TLS_[A-Z0-9_]+ ]]; then
|
||||
# Note: When OpenSSL prints "New, <protocol>, Cipher is <cipher>", <cipher> is the
|
||||
# negotiated cipher, but <protocol> is not the negotiated protocol. Instead, it is
|
||||
# the SSL/TLS protocol that first defined <cipher>. Since the ciphers that were
|
||||
# first defined for TLSv1.3 may only be used with TLSv1.3, this line may be used
|
||||
# to determine whether TLSv1.3 was negotiated, but if another protocol is specified
|
||||
# on this line, then this line does not indicate the actual protocol negotiated. Also,
|
||||
# only TLSv1.3 cipher suites have names that begin with TLS_, which provides additional
|
||||
# assurance that the above match will only succeed if TLSv1.3 was negotiated.
|
||||
protocol="TLSv1.3"
|
||||
fi
|
||||
tm_out "$protocol"
|
||||
}
|
||||
|
||||
# now some function for the integrated BIGIP F5 Cookie detector (see https://github.com/drwetter/F5-BIGIP-Decoder)
|
||||
|
||||
f5_hex2ip() {
|
||||
debugme echo "$1"
|
||||
echo $((16#${1:0:2})).$((16#${1:2:2})).$((16#${1:4:2})).$((16#${1:6:2}))
|
||||
}
|
||||
f5_hex2ip6() {
|
||||
debugme echo "$1"
|
||||
echo "[${1:0:4}:${1:4:4}:${1:8:4}:${1:12:4}.${1:16:4}:${1:20:4}:${1:24:4}:${1:28:4}]"
|
||||
}
|
||||
|
||||
f5_determine_routeddomain() {
|
||||
local tmp
|
||||
tmp="${1%%o*}"
|
||||
echo "${tmp/rd/}"
|
||||
}
|
||||
|
||||
f5_ip_oldstyle() {
|
||||
local tmp
|
||||
local a b c d
|
||||
|
||||
tmp="${1/%.*}" # until first dot
|
||||
tmp="$(printf "%08x" "$tmp")" # convert the whole thing to hex, now back to ip (reversed notation:
|
||||
tmp="$(f5_hex2ip $tmp)" # transform to ip with reversed notation
|
||||
IFS="." read -r a b c d <<< "$tmp" # reverse it
|
||||
echo $d.$c.$b.$a
|
||||
}
|
||||
|
||||
f5_port_decode() {
|
||||
local tmp
|
||||
|
||||
tmp="$(strip_lf "$1")" # remove lf if there is one
|
||||
tmp="${tmp/.0000/}" # to be sure remove trailing zeros with a dot
|
||||
tmp="${tmp#*.}" # get the port
|
||||
tmp="$(printf "%04x" "${tmp}")" # to hex
|
||||
if [[ ${#tmp} -eq 4 ]]; then
|
||||
:
|
||||
elif [[ ${#tmp} -eq 3 ]]; then # fill it up with leading zeros if needed
|
||||
tmp=0${tmp}
|
||||
elif [[ ${#tmp} -eq 2 ]]; then
|
||||
tmp=00${tmp}
|
||||
fi
|
||||
echo $((16#${tmp:2:2}${tmp:0:2})) # reverse order and convert it from hex to dec
|
||||
}
|
||||
|
||||
###### START ServerHello/OpenSSL/F5 function definitions ######
|
||||
###### END helper function definitions ######
|
||||
|
||||
|
||||
##################### START output file formatting functions #########################
|
||||
#################### START JSON file functions ####################
|
||||
|
||||
fileout_json_footer() {
|
||||
if "$do_json"; then
|
||||
@ -853,8 +1107,6 @@ fileout_json_finding() {
|
||||
fi
|
||||
}
|
||||
|
||||
##################### FILE FORMATTING #########################
|
||||
|
||||
fileout_pretty_json_banner() {
|
||||
local target
|
||||
|
||||
@ -1040,7 +1292,7 @@ csv_header() {
|
||||
}
|
||||
|
||||
|
||||
################# JSON FILE FORMATTING END. HTML START ####################
|
||||
################# END JSON file functions. START HTML functions ####################
|
||||
|
||||
html_header() {
|
||||
local fname_prefix
|
||||
@ -1111,7 +1363,7 @@ html_footer() {
|
||||
return 0
|
||||
}
|
||||
|
||||
################# HTML FILE FORMATTING END ####################
|
||||
################# END HTML file functions ####################
|
||||
|
||||
prepare_logging() {
|
||||
# arg1: for testing mx records name we put a name of logfile in here, otherwise we get strange file names
|
||||
@ -1146,258 +1398,7 @@ prepare_logging() {
|
||||
exec > >(tee -a -i "$LOGFILE")
|
||||
}
|
||||
|
||||
################### FILE FORMATTING END #########################
|
||||
|
||||
###### START helper function definitions ######
|
||||
|
||||
if [[ "${BASH_VERSINFO[0]}" == 3 ]]; then
|
||||
# older bash can do this only (MacOS X), even SLES 11, see #697
|
||||
toupper() { tr 'a-z' 'A-Z' <<< "$1"; }
|
||||
tolower() { tr 'A-Z' 'a-z' <<< "$1"; }
|
||||
else
|
||||
toupper() { echo -n "${1^^}"; }
|
||||
tolower() { echo -n "${1,,}"; }
|
||||
fi
|
||||
|
||||
get_last_char() {
|
||||
echo "${1:~0}" # "${string: -1}" would work too (both also in bash 3.2)
|
||||
}
|
||||
# Checking for last char. If already a separator supplied, we don't need an additional one
|
||||
debugme() {
|
||||
[[ "$DEBUG" -ge 2 ]] && "$@"
|
||||
return 0
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
# convert decimal number between 256 and < 256*256 to hex
|
||||
dec04hex() {
|
||||
local a=$(printf "%04x" "$1")
|
||||
printf "x%02s, x%02s" "${a:0:2}" "${a:2:2}"
|
||||
}
|
||||
|
||||
|
||||
# trim spaces for BSD and old sed
|
||||
count_lines() {
|
||||
#echo "${$(wc -l <<< "$1")// /}"
|
||||
# ^^ bad substitution under bash, zsh ok. For some reason this does the trick:
|
||||
echo $(wc -l <<< "$1")
|
||||
}
|
||||
|
||||
count_words() {
|
||||
#echo "${$(wc -w <<< "$1")// /}"
|
||||
# ^^ bad substitution under bash, zsh ok. For some reason this does the trick:
|
||||
echo $(wc -w <<< "$1")
|
||||
}
|
||||
|
||||
count_ciphers() {
|
||||
echo $(wc -w <<< "${1//:/ }")
|
||||
}
|
||||
|
||||
#arg1: TLS 1.2 and below ciphers
|
||||
#arg2: TLS 1.3 ciphers
|
||||
#arg3: options (e.g., -V)
|
||||
actually_supported_osslciphers() {
|
||||
local tls13_ciphers="$TLS13_OSSL_CIPHERS"
|
||||
|
||||
[[ "$2" != ALL ]] && tls13_ciphers="$2"
|
||||
if "$HAS_CIPHERSUITES"; then
|
||||
$OPENSSL ciphers $3 $OSSL_CIPHERS_S -ciphersuites "$tls13_ciphers" "$1" 2>/dev/null || echo ""
|
||||
elif [[ -n "$tls13_ciphers" ]]; then
|
||||
$OPENSSL ciphers $3 $OSSL_CIPHERS_S "$tls13_ciphers:$1" 2>/dev/null || echo ""
|
||||
else
|
||||
$OPENSSL ciphers $OSSL_CIPHERS_S $3 "$1" 2>/dev/null || echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# Given a protocol (arg1) and a list of ciphers (arg2) that is formatted as
|
||||
# ", xx,xx, xx,xx, xx,xx, xx,xx" remove any TLSv1.3 ciphers if the protocol
|
||||
# is less than 04 and remove any TLSv1.2-only ciphers if the protocol is less
|
||||
# than 03.
|
||||
strip_inconsistent_ciphers() {
|
||||
local -i proto=0x$1
|
||||
local cipherlist="$2"
|
||||
|
||||
[[ $proto -lt 4 ]] && cipherlist="${cipherlist//, 13,0[0-9a-fA-F]/}"
|
||||
if [[ $proto -lt 3 ]]; then
|
||||
cipherlist="${cipherlist//, 00,3[b-fB-F]/}"
|
||||
cipherlist="${cipherlist//, 00,40/}"
|
||||
cipherlist="${cipherlist//, 00,6[7-9a-dA-D]/}"
|
||||
cipherlist="${cipherlist//, 00,9[c-fC-F]/}"
|
||||
cipherlist="${cipherlist//, 00,[abAB][0-9a-fA-F]/}"
|
||||
cipherlist="${cipherlist//, 00,[cC][0-5]/}"
|
||||
cipherlist="${cipherlist//, 16,[bB][7-9aA]/}"
|
||||
cipherlist="${cipherlist//, [cC]0,2[3-9a-fA-F]/}"
|
||||
cipherlist="${cipherlist//, [cC]0,3[01278a-fA-F]/}"
|
||||
cipherlist="${cipherlist//, [cC]0,[4-9aA][0-9a-fA-F]/}"
|
||||
cipherlist="${cipherlist//, [cC][cC],1[345]/}"
|
||||
cipherlist="${cipherlist//, [cC][cC],[aA][89a-eA-E]/}"
|
||||
fi
|
||||
echo "$cipherlist"
|
||||
return 0
|
||||
}
|
||||
|
||||
newline_to_spaces() {
|
||||
tr '\n' ' ' <<< "$1" | sed 's/ $//'
|
||||
}
|
||||
|
||||
colon_to_spaces() {
|
||||
echo "${1//:/ }"
|
||||
}
|
||||
|
||||
strip_lf() {
|
||||
tr -d '\n' <<< "$1" | tr -d '\r'
|
||||
}
|
||||
|
||||
strip_spaces() {
|
||||
echo "${1// /}"
|
||||
}
|
||||
|
||||
# https://web.archive.org/web/20121022051228/http://codesnippets.joyent.com/posts/show/1816
|
||||
strip_leading_space() {
|
||||
printf "%s" "${1#"${1%%[![:space:]]*}"}"
|
||||
}
|
||||
strip_trailing_space() {
|
||||
printf "%s" "${1%"${1##*[![:space:]]}"}"
|
||||
}
|
||||
|
||||
|
||||
# retrieve cipher from ServerHello (via openssl)
|
||||
get_cipher() {
|
||||
local cipher=""
|
||||
local server_hello="$(cat -v "$1")"
|
||||
# This and two other following instances are not best practice and normally a useless use of "cat", see
|
||||
# https://web.archive.org/web/20160711205930/http://porkmail.org/era/unix/award.html#uucaletter
|
||||
# However there seem to be cases where the preferred $(< "$1") logic has a problem.
|
||||
# Esepcially with bash 3.2 (Mac OS X) and when on the server side binary chars
|
||||
# are returned, see https://stackoverflow.com/questions/7427262/how-to-read-a-file-into-a-variable-in-shell#22607352
|
||||
# and https://github.com/drwetter/testssl.sh/issues/1292
|
||||
# Performance measurements showed no to barely measureable penalty (1s displayed in 9 tries).
|
||||
|
||||
if [[ "$server_hello" =~ Cipher\ *:\ ([A-Z0-9]+-[A-Za-z0-9\-]+|TLS_[A-Za-z0-9_]+) ]]; then
|
||||
cipher="${BASH_REMATCH##* }"
|
||||
elif [[ "$server_hello" =~ (New|Reused)", "(SSLv[23]|TLSv1(\.[0-3])?(\/SSLv3)?)", Cipher is "([A-Z0-9]+-[A-Za-z0-9\-]+|TLS_[A-Za-z0-9_]+) ]]; then
|
||||
cipher="${BASH_REMATCH##* }"
|
||||
fi
|
||||
tm_out "$cipher"
|
||||
}
|
||||
|
||||
# retrieve protocol from ServerHello (via openssl)
|
||||
get_protocol() {
|
||||
local protocol=""
|
||||
local server_hello="$(cat -v "$1")"
|
||||
|
||||
if [[ "$server_hello" =~ Protocol\ *:\ (SSLv[23]|TLSv1(\.[0-3])?) ]]; then
|
||||
protocol="${BASH_REMATCH##* }"
|
||||
elif [[ "$server_hello" =~ (New|Reused)", TLSv1.3, Cipher is "TLS_[A-Z0-9_]+ ]]; then
|
||||
# Note: When OpenSSL prints "New, <protocol>, Cipher is <cipher>", <cipher> is the
|
||||
# negotiated cipher, but <protocol> is not the negotiated protocol. Instead, it is
|
||||
# the SSL/TLS protocol that first defined <cipher>. Since the ciphers that were
|
||||
# first defined for TLSv1.3 may only be used with TLSv1.3, this line may be used
|
||||
# to determine whether TLSv1.3 was negotiated, but if another protocol is specified
|
||||
# on this line, then this line does not indicate the actual protocol negotiated. Also,
|
||||
# only TLSv1.3 cipher suites have names that begin with TLS_, which provides additional
|
||||
# assurance that the above match will only succeed if TLSv1.3 was negotiated.
|
||||
protocol="TLSv1.3"
|
||||
fi
|
||||
tm_out "$protocol"
|
||||
}
|
||||
|
||||
is_number() {
|
||||
[[ "$1" =~ ^[1-9][0-9]*$ ]] && \
|
||||
return 0 || \
|
||||
return 1
|
||||
}
|
||||
|
||||
is_ipv4addr() {
|
||||
local octet="(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])"
|
||||
local ipv4address="$octet\\.$octet\\.$octet\\.$octet"
|
||||
|
||||
[[ -z "$1" ]] && return 1
|
||||
# more than numbers, important for hosts like AAA.BBB.CCC.DDD.in-addr.arpa.DOMAIN.TLS
|
||||
[[ -n $(tr -d '0-9\.' <<< "$1") ]] && return 1
|
||||
|
||||
grep -Eq "$ipv4address" <<< "$1" && \
|
||||
return 0 || \
|
||||
return 1
|
||||
}
|
||||
|
||||
# a bit easier
|
||||
is_ipv6addr() {
|
||||
[[ -z "$1" ]] && return 1
|
||||
# less than 2x ":"
|
||||
[[ $(count_lines "$(tr ':' '\n' <<< "$1")") -le 1 ]] && \
|
||||
return 1
|
||||
#check on chars allowed:
|
||||
[[ -n "$(tr -d '0-9:a-fA-F ' <<< "$1" | sed -e '/^$/d')" ]] && \
|
||||
return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
# now some function for the integrated BIGIP F5 Cookie detector (see https://github.com/drwetter/F5-BIGIP-Decoder)
|
||||
|
||||
f5_hex2ip() {
|
||||
debugme echo "$1"
|
||||
echo $((16#${1:0:2})).$((16#${1:2:2})).$((16#${1:4:2})).$((16#${1:6:2}))
|
||||
}
|
||||
f5_hex2ip6() {
|
||||
debugme echo "$1"
|
||||
echo "[${1:0:4}:${1:4:4}:${1:8:4}:${1:12:4}.${1:16:4}:${1:20:4}:${1:24:4}:${1:28:4}]"
|
||||
}
|
||||
|
||||
f5_determine_routeddomain() {
|
||||
local tmp
|
||||
tmp="${1%%o*}"
|
||||
echo "${tmp/rd/}"
|
||||
}
|
||||
|
||||
f5_ip_oldstyle() {
|
||||
local tmp
|
||||
local a b c d
|
||||
|
||||
tmp="${1/%.*}" # until first dot
|
||||
tmp="$(printf "%08x" "$tmp")" # convert the whole thing to hex, now back to ip (reversed notation:
|
||||
tmp="$(f5_hex2ip $tmp)" # transform to ip with reversed notation
|
||||
IFS="." read -r a b c d <<< "$tmp" # reverse it
|
||||
echo $d.$c.$b.$a
|
||||
}
|
||||
|
||||
f5_port_decode() {
|
||||
local tmp
|
||||
|
||||
tmp="$(strip_lf "$1")" # remove lf if there is one
|
||||
tmp="${tmp/.0000/}" # to be sure remove trailing zeros with a dot
|
||||
tmp="${tmp#*.}" # get the port
|
||||
tmp="$(printf "%04x" "${tmp}")" # to hex
|
||||
if [[ ${#tmp} -eq 4 ]]; then
|
||||
:
|
||||
elif [[ ${#tmp} -eq 3 ]]; then # fill it up with leading zeros if needed
|
||||
tmp=0${tmp}
|
||||
elif [[ ${#tmp} -eq 2 ]]; then
|
||||
tmp=00${tmp}
|
||||
fi
|
||||
echo $((16#${tmp:2:2}${tmp:0:2})) # reverse order and convert it from hex to dec
|
||||
}
|
||||
|
||||
|
||||
|
||||
###### END helper function definitions ######
|
||||
################### END all file output functions #########################
|
||||
|
||||
# prints out multiple lines in $1, left aligned by spaces in $2
|
||||
out_row_aligned() {
|
||||
@ -1744,9 +1745,9 @@ check_revocation_crl() {
|
||||
return 1
|
||||
fi
|
||||
if grep -q "\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-" $TEMPDIR/intermediatecerts.pem; then
|
||||
$OPENSSL verify -crl_check -CAfile <(cat $ADDITIONAL_CA_FILES "$GOOD_CA_BUNDLE" "${tmpfile%%.crl}.pem") -untrusted $TEMPDIR/intermediatecerts.pem $HOSTCERT &> "${tmpfile%%.crl}.err"
|
||||
$OPENSSL verify -crl_check -CAfile <(cat $ADDTL_CA_FILES "$GOOD_CA_BUNDLE" "${tmpfile%%.crl}.pem") -untrusted $TEMPDIR/intermediatecerts.pem $HOSTCERT &> "${tmpfile%%.crl}.err"
|
||||
else
|
||||
$OPENSSL verify -crl_check -CAfile <(cat $ADDITIONAL_CA_FILES "$GOOD_CA_BUNDLE" "${tmpfile%%.crl}.pem") $HOSTCERT &> "${tmpfile%%.crl}.err"
|
||||
$OPENSSL verify -crl_check -CAfile <(cat $ADDTL_CA_FILES "$GOOD_CA_BUNDLE" "${tmpfile%%.crl}.pem") $HOSTCERT &> "${tmpfile%%.crl}.err"
|
||||
fi
|
||||
if [[ $? -eq 0 ]]; then
|
||||
out ", "
|
||||
@ -1797,7 +1798,7 @@ check_revocation_ocsp() {
|
||||
asciihex_to_binary "$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 $ADDITIONAL_CA_FILES "$GOOD_CA_BUNDLE") -cert $HOSTCERT -text &> "$tmpfile"
|
||||
-CAfile <(cat $ADDTL_CA_FILES "$GOOD_CA_BUNDLE") -cert $HOSTCERT -text &> "$tmpfile"
|
||||
else
|
||||
host_header=${uri##http://}
|
||||
host_header=${host_header%%/*}
|
||||
@ -1811,7 +1812,7 @@ check_revocation_ocsp() {
|
||||
fi
|
||||
$OPENSSL ocsp -no_nonce ${host_header} -url "$uri" \
|
||||
-issuer $TEMPDIR/hostcert_issuer.pem -verify_other $TEMPDIR/intermediatecerts.pem \
|
||||
-CAfile <(cat $ADDITIONAL_CA_FILES "$GOOD_CA_BUNDLE") -cert $HOSTCERT -text &> "$tmpfile"
|
||||
-CAfile <(cat $ADDTL_CA_FILES "$GOOD_CA_BUNDLE") -cert $HOSTCERT -text &> "$tmpfile"
|
||||
fi
|
||||
if [[ $? -eq 0 ]] && grep -Fq "Response verify OK" "$tmpfile"; then
|
||||
response="$(grep -F "$HOSTCERT: " "$tmpfile")"
|
||||
@ -6971,9 +6972,9 @@ determine_trust() {
|
||||
# in a subshell because that should be valid here only
|
||||
(export SSL_CERT_DIR="/dev/null"; export SSL_CERT_FILE="/dev/null"
|
||||
if [[ $certificates_provided -ge 2 ]]; then
|
||||
$OPENSSL verify -purpose sslserver -CAfile <(cat $ADDITIONAL_CA_FILES "$bundle_fname") -untrusted $TEMPDIR/intermediatecerts.pem $HOSTCERT >$TEMPDIR/${certificate_file[i]}.1 2>$TEMPDIR/${certificate_file[i]}.2
|
||||
$OPENSSL verify -purpose sslserver -CAfile <(cat $ADDTL_CA_FILES "$bundle_fname") -untrusted $TEMPDIR/intermediatecerts.pem $HOSTCERT >$TEMPDIR/${certificate_file[i]}.1 2>$TEMPDIR/${certificate_file[i]}.2
|
||||
else
|
||||
$OPENSSL verify -purpose sslserver -CAfile <(cat $ADDITIONAL_CA_FILES "$bundle_fname") $HOSTCERT >$TEMPDIR/${certificate_file[i]}.1 2>$TEMPDIR/${certificate_file[i]}.2
|
||||
$OPENSSL verify -purpose sslserver -CAfile <(cat $ADDTL_CA_FILES "$bundle_fname") $HOSTCERT >$TEMPDIR/${certificate_file[i]}.1 2>$TEMPDIR/${certificate_file[i]}.2
|
||||
fi)
|
||||
verify_retcode[i]=$(awk '/error [1-9][0-9]? at [0-9]+ depth lookup:/ { if (!found) {print $2; found=1} }' $TEMPDIR/${certificate_file[i]}.1 $TEMPDIR/${certificate_file[i]}.2)
|
||||
[[ -z "${verify_retcode[i]}" ]] && verify_retcode[i]=0
|
||||
@ -19570,7 +19571,7 @@ parse_cmd_line() {
|
||||
do_grease=true
|
||||
;;
|
||||
--add-ca|--add-CA|--add-ca=*|--add-CA=*)
|
||||
ADDITIONAL_CA_FILES="$(parse_opt_equal_sign "$1" "$2")"
|
||||
ADDTL_CA_FILES="$(parse_opt_equal_sign "$1" "$2")"
|
||||
[[ $? -eq 0 ]] && shift
|
||||
;;
|
||||
--devel) ### this development feature will soon disappear
|
||||
@ -19876,8 +19877,8 @@ parse_cmd_line() {
|
||||
"$do_mx_all_ips" && [[ "$NODNS" == none ]] && fatal "\"--mx\" and \"--nodns=none\" don't work together" $ERR_CMDLINE
|
||||
[[ -n "$CONNECT_TIMEOUT" ]] && [[ "$MASS_TESTING_MODE" == parallel ]] && fatal "Parallel mass scanning and specifying connect timeouts currently don't work together" $ERR_CMDLINE
|
||||
|
||||
ADDITIONAL_CA_FILES="${ADDITIONAL_CA_FILES//,/ }"
|
||||
for fname in $ADDITIONAL_CA_FILES; do
|
||||
ADDTL_CA_FILES="${ADDTL_CA_FILES//,/ }"
|
||||
for fname in $ADDTL_CA_FILES; do
|
||||
[[ -s "$fname" ]] || fatal "CA file \"$fname\" does not exist" $ERR_RESOURCE
|
||||
grep -q "BEGIN CERTIFICATE" "$fname" || fatal "\"$fname\" is not CA file in PEM format" $ERR_RESOURCE
|
||||
done
|
||||
|
Loading…
Reference in New Issue
Block a user