From 6868593defef8187c07a320608ebd7f1140a15a1 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 8 Apr 2021 14:43:23 -0400 Subject: [PATCH] Quit testssl.sh on all command line errors As suggested in #1844, this commit changes testssl.sh so that the parent process quits immediately if there is an error in the command line for one of the child processes. Currently, a signal is sent to the parent process to quit if the child process encounters an error and calls help(), but sometimes parse_cmd_line() just prints an error message and calls fatal() rather than help(), in which case the parent process does not stop. This commit addresses the issue by creating a new function, fatal_cmd_line(), which is almost the same as fatal(), but additionally sends a signal to the parent indicating that the parent should stop. This commit also changes calls to fatal() to calls to fatal_cmd_line() if json_header(), csv_header(), html_header(), or prepare_logging() encounter a problem. The same is done if prettyprint_local() with the command-line option provided for it. There may be other places in which it would be appropriate to call fatal_cmd_line() rather than fatal() (e.g., in parse_hn_port() or check_proxy()), but those changes are not made in this commit. --- testssl.sh | 102 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 40 deletions(-) diff --git a/testssl.sh b/testssl.sh index 67f6efb69..c05845372 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1415,7 +1415,7 @@ json_header() { local filename_provided=false if [[ -n "$PARENT_JSONFILE" ]]; then - [[ -n "$JSONFILE" ]] && fatal "Can't write to both $PARENT_JSONFILE and $JSONFILE" $ERR_CMDLINE + [[ -n "$JSONFILE" ]] && fatal_cmd_line "Can't write to both $PARENT_JSONFILE and $JSONFILE" $ERR_CMDLINE JSONFILE="$PARENT_JSONFILE" fi [[ -n "$JSONFILE" ]] && [[ ! -d "$JSONFILE" ]] && filename_provided=true @@ -1451,7 +1451,7 @@ json_header() { JSONHEADER=false else if [[ -s "$JSONFILE" ]]; then - "$OVERWRITE" || fatal "non-empty \"$JSONFILE\" exists. Either use \"--append\" or (re)move it" $ERR_FCREATE + "$OVERWRITE" || fatal_cmd_line "non-empty \"$JSONFILE\" exists. Either use \"--append\" or (re)move it" $ERR_FCREATE cp /dev/null "$JSONFILE" fi "$do_json" && echo "[" > "$JSONFILE" @@ -1466,7 +1466,7 @@ csv_header() { local filename_provided=false if [[ -n "$PARENT_CSVFILE" ]]; then - [[ -n "$CSVFILE" ]] && fatal "Can't write to both $PARENT_CSVFILE and $CSVFILE" $ERR_CMDLINE + [[ -n "$CSVFILE" ]] && fatal_cmd_line "Can't write to both $PARENT_CSVFILE and $CSVFILE" $ERR_CMDLINE CSVFILE="$PARENT_CSVFILE" fi [[ -n "$CSVFILE" ]] && [[ ! -d "$CSVFILE" ]] && filename_provided=true @@ -1499,7 +1499,7 @@ csv_header() { CSVHEADER=false else if [[ -s "$CSVFILE" ]]; then - "$OVERWRITE" || fatal "non-empty \"$CSVFILE\" exists. Either use \"--append\" or (re)move it" $ERR_FCREATE + "$OVERWRITE" || fatal_cmd_line "non-empty \"$CSVFILE\" exists. Either use \"--append\" or (re)move it" $ERR_FCREATE cp /dev/null "$CSVFILE" fi touch "$CSVFILE" @@ -1520,7 +1520,7 @@ html_header() { local filename_provided=false if [[ -n "$PARENT_HTMLFILE" ]]; then - [[ -n "$HTMLFILE" ]] && fatal "Can't write to both $PARENT_HTMLFILE and $HTMLFILE" $ERR_CMDLINE + [[ -n "$HTMLFILE" ]] && fatal_cmd_line "Can't write to both $PARENT_HTMLFILE and $HTMLFILE" $ERR_CMDLINE HTMLFILE="$PARENT_HTMLFILE" fi [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] && filename_provided=true @@ -1556,7 +1556,7 @@ html_header() { HTMLHEADER=false else if [[ -s "$HTMLFILE" ]]; then - "$OVERWRITE" || fatal "non-empty \"$HTMLFILE\" exists. Either use \"--append\" or (re)move it" $ERR_FCREATE + "$OVERWRITE" || fatal_cmd_line "non-empty \"$HTMLFILE\" exists. Either use \"--append\" or (re)move it" $ERR_FCREATE cp /dev/null "$HTMLFILE" fi html_out "\n" @@ -1599,7 +1599,7 @@ prepare_logging() { local filename_provided=false if [[ -n "$PARENT_LOGFILE" ]]; then - [[ -n "$LOGFILE" ]] && fatal "Can't write to both $PARENT_LOGFILE and $LOGFILE" $ERR_CMDLINE + [[ -n "$LOGFILE" ]] && fatal_cmd_line "Can't write to both $PARENT_LOGFILE and $LOGFILE" $ERR_CMDLINE LOGFILE="$PARENT_LOGFILE" fi [[ -n "$LOGFILE" ]] && [[ ! -d "$LOGFILE" ]] && filename_provided=true @@ -1622,7 +1622,7 @@ prepare_logging() { if ! "$APPEND"; then if [[ -s "$LOGFILE" ]]; then - "$OVERWRITE" || fatal "non-empty \"$LOGFILE\" exists. Either use \"--append\" or (re)move it" $ERR_FCREATE + "$OVERWRITE" || fatal_cmd_line "non-empty \"$LOGFILE\" exists. Either use \"--append\" or (re)move it" $ERR_FCREATE cp /dev/null "$LOGFILE" fi fi @@ -3426,7 +3426,7 @@ prettyprint_local() { local re='^[0-9A-Fa-f]+$' if [[ "$1" == 0x* ]] || [[ "$1" == 0X* ]]; then - fatal "pls supply x instead" $ERR_CMDLINE + fatal_cmd_line "pls supply x instead" $ERR_CMDLINE fi if [[ -z "$1" ]]; then @@ -20704,6 +20704,28 @@ fatal() { exit $2 } +# Program terminates as a result of an error in the command line. +# arg1: string to print / to write to file +# arg2: global error code, see ERR_* above +# arg3: an optional hint (string) +# +fatal_cmd_line() { + outln + prln_magenta "Fatal error: $1" >&2 + [[ -n "$LOGFILE" ]] && prln_magenta "Fatal error: $1" >>$LOGFILE + if [[ -n "$3" ]]; then + outln "$3" >&2 + [[ -n "$LOGFILE" ]] && outln "$3" >>$LOGFILE + fi + # Make sure we don't try to write into files when not created yet. + # No shorthand expression to avoid errors when $CMDLINE_PARSED haven't been filled yet. + HTMLHEADER=false + JSONHEADER=false + [[ $CMDLINE_PARSED == true ]] && fileout "scanProblem" "FATAL" "$1" + "$CHILD_MASS_TESTING" && kill -s USR1 $PPID + exit $2 +} + # This OTOH doesn't exit but puts a fatal error to the screen but continues with the next # IP/hostname. It should only be used if a single IP/Hostname in a scan is not reachable. # arg1: string to print / to write to file @@ -23133,7 +23155,7 @@ parse_cmd_line() { while [[ $# -gt 0 ]]; do case $1 in --help|-b|--banner|-v|--version) - fatal "$1 is a standalone command line option" $ERR_CMDLINE + fatal_cmd_line "$1 is a standalone command line option" $ERR_CMDLINE ;; --mx) do_mx_all_ips=true @@ -23162,7 +23184,7 @@ parse_cmd_line() { NODNS="$(parse_opt_equal_sign "$1" "$2")" [[ $? -eq 0 ]] && shift if [[ "$NODNS" != none ]] && [[ "$NODNS" != min ]]; then - fatal "Value for nodns switch can be either \"min\" or \"none\"" $ERR_CMDLINE + fatal_cmd_line "Value for nodns switch can be either \"min\" or \"none\"" $ERR_CMDLINE fi ;; -V|-V=*|--local|--local=*) # attention, this could have a value or not! @@ -23448,12 +23470,12 @@ parse_cmd_line() { COLORBLIND=true ;; --log|--logging) - "$do_logging" && fatal "two --log* arguments" $ERR_CMDLINE + "$do_logging" && fatal_cmd_line "two --log* arguments" $ERR_CMDLINE do_logging=true ;; # DEFINITION of LOGFILE if no arg specified: automagically in parse_hn_port() # following does the same but additionally we can specify a log location --logfile|--logfile=*|-oL|-oL=*) - "$do_logging" && fatal "two --log* arguments" $ERR_CMDLINE + "$do_logging" && fatal_cmd_line "two --log* arguments" $ERR_CMDLINE LOGFILE="$(parse_opt_equal_sign "$1" "$2")" [[ $? -eq 0 ]] && shift do_logging=true @@ -23468,17 +23490,17 @@ parse_cmd_line() { 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 + "$do_pretty_json" && fatal_cmd_line "flat and pretty JSON output are mutually exclusive" $ERR_CMDLINE + "$do_json" && fatal_cmd_line "--json and --jsonfile are mutually exclusive" $ERR_CMDLINE if [[ "$2" =~ \.(json|JSON)$ ]]; then - fatal "No file name allowed after \"--json\" (use \"--jsonfile\" instead)" $ERR_CMDLINE + fatal_cmd_line "No file name allowed after \"--json\" (use \"--jsonfile\" instead)" $ERR_CMDLINE fi do_json=true ;; # DEFINITION of JSONFILE is not arg specified: automagically in parse_hn_port() # following does the same but additionally we can specify a log location --jsonfile|--jsonfile=*|-oj|-oj=*) - "$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 + "$do_pretty_json" && fatal_cmd_line "flat and pretty JSON output are mutually exclusive" $ERR_CMDLINE + "$do_json" && fatal_cmd_line "--json and --jsonfile are mutually exclusive" $ERR_CMDLINE JSONFILE="$(parse_opt_equal_sign "$1" "$2")" [[ $? -eq 0 ]] && shift do_json=true @@ -23493,16 +23515,16 @@ parse_cmd_line() { 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 + "$do_json" && fatal_cmd_line "flat and pretty JSON output are mutually exclusive" $ERR_CMDLINE + "$do_pretty_json" && fatal_cmd_line "--json-pretty and --jsonfile-pretty are mutually exclusive" $ERR_CMDLINE if [[ "$2" =~ \.(json|JSON)$ ]]; then - fatal "No file name allowed after \"--json\" (use \"--jsonfile-pretty\" instead)" $ERR_CMDLINE + fatal_cmd_line "No file name allowed after \"--json\" (use \"--jsonfile-pretty\" instead)" $ERR_CMDLINE fi do_pretty_json=true ;; --jsonfile-pretty|--jsonfile-pretty=*|-oJ|-oJ=*) - "$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 + "$do_json" && fatal_cmd_line "flat and pretty JSON output are mutually exclusive" $ERR_CMDLINE + "$do_pretty_json" && fatal_cmd_line "--json-pretty and --jsonfile-pretty are mutually exclusive" $ERR_CMDLINE JSONFILE="$(parse_opt_equal_sign "$1" "$2")" [[ $? -eq 0 ]] && shift do_pretty_json=true @@ -23524,15 +23546,15 @@ parse_cmd_line() { GIVE_HINTS=true ;; --csv) - "$do_csv" && fatal "two --csv* arguments" $ERR_CMDLINE + "$do_csv" && fatal_cmd_line "two --csv* arguments" $ERR_CMDLINE if [[ "$2" =~ \.(csv|CSV)$ ]]; then - fatal "No file name allowed after \"--csv\" (use \"--csvfile\" instead)" $ERR_CMDLINE + fatal_cmd_line "No file name allowed after \"--csv\" (use \"--csvfile\" instead)" $ERR_CMDLINE fi do_csv=true ;; # DEFINITION of CSVFILE is not arg specified: automagically in parse_hn_port() # following does the same but additionally we can specify a log location --csvfile|--csvfile=*|-oC|-oC=*) - "$do_csv" && fatal "two --csv* arguments" $ERR_CMDLINE + "$do_csv" && fatal_cmd_line "two --csv* arguments" $ERR_CMDLINE CSVFILE="$(parse_opt_equal_sign "$1" "$2")" [[ $? -eq 0 ]] && shift do_csv=true @@ -23547,15 +23569,15 @@ parse_cmd_line() { do_csv=true ;; --html) - "$do_html" && fatal "two --html* arguments" $ERR_CMDLINE + "$do_html" && fatal_cmd_line "two --html* arguments" $ERR_CMDLINE if [[ "$2" =~ \.(htm|html|HTM|HTML)$ ]]; then - fatal "No file name allowed after \"--html\" (use \"--htmlfile\" instead)" $ERR_CMDLINE + fatal_cmd_line "No file name allowed after \"--html\" (use \"--htmlfile\" instead)" $ERR_CMDLINE fi do_html=true ;; # DEFINITION of HTMLFILE is not arg specified: automagically in parse_hn_port() # following does the same but additionally we can specify a file location --htmlfile|--htmlfile=*|-oH|-oH=*) - "$do_html" && fatal "two --html* arguments" $ERR_CMDLINE + "$do_html" && fatal_cmd_line "two --html* arguments" $ERR_CMDLINE HTMLFILE="$(parse_opt_equal_sign "$1" "$2")" [[ $? -eq 0 ]] && shift do_html=true @@ -23570,7 +23592,7 @@ parse_cmd_line() { 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 + { "$do_html" || "$do_json" || "$do_pretty_json" || "$do_csv" || "$do_logging"; } && fatal_cmd_line "check your arguments four multiple file output options" $ERR_CMDLINE outfile_arg="$(parse_opt_equal_sign "$1" "$2")" if [[ "$outfile_arg" != "auto" ]]; then if [[ -d "$outfile_arg" ]]; then @@ -23592,7 +23614,7 @@ parse_cmd_line() { do_logging=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 + { "$do_html" || "$do_json" || "$do_pretty_json" || "$do_csv" || "$do_logging"; } && fatal_cmd_line "check your arguments four multiple file output options" $ERR_CMDLINE outfile_arg="$(parse_opt_equal_sign "$1" "$2")" if [[ "$outfile_arg" != "auto" ]]; then if [[ -d "$outfile_arg" ]]; then @@ -23614,11 +23636,11 @@ parse_cmd_line() { do_logging=true ;; --overwrite) - "$APPEND" && fatal "using --overwrite and --append is contradicting" $ERR_CMDLINE + "$APPEND" && fatal_cmd_line "using --overwrite and --append is contradicting" $ERR_CMDLINE OVERWRITE=true ;; --append) - "$OVERWRITE" && fatal "using --append and --overwrite is contradicting" $ERR_CMDLINE + "$OVERWRITE" && fatal_cmd_line "using --append and --overwrite is contradicting" $ERR_CMDLINE APPEND=true ;; --outprefix) @@ -23697,17 +23719,17 @@ parse_cmd_line() { # Show usage if no further options were specified if [[ -z "$1" ]] && [[ -z "$FNAME" ]] && ! "$do_display_only"; then - fatal "URI missing" $ERR_CMDLINE + fatal_cmd_line "URI missing" $ERR_CMDLINE else # What is left here should be the URI. URI="$1" - [[ -n "$2" ]] && fatal "URI comes last" $ERR_CMDLINE + [[ -n "$2" ]] && fatal_cmd_line "URI comes last" $ERR_CMDLINE fi # Now spot some incompatibilities in cmdlines - [[ $CMDLINE_IP == one ]] && [[ "$NODNS" == none ]] && fatal "\"--ip=one\" and \"--nodns=none\" don't work together" $ERR_CMDLINE - [[ $CMDLINE_IP == one ]] && ( is_ipv4addr "$URI" || is_ipv6addr "$URI" ) && fatal "\"--ip=one\" plus supplying an IP address doesn't work" $ERR_CMDLINE - "$do_mx_all_ips" && [[ "$NODNS" == none ]] && fatal "\"--mx\" and \"--nodns=none\" don't work together" $ERR_CMDLINE + [[ $CMDLINE_IP == one ]] && [[ "$NODNS" == none ]] && fatal_cmd_line "\"--ip=one\" and \"--nodns=none\" don't work together" $ERR_CMDLINE + [[ $CMDLINE_IP == one ]] && ( is_ipv4addr "$URI" || is_ipv6addr "$URI" ) && fatal_cmd_line "\"--ip=one\" plus supplying an IP address doesn't work" $ERR_CMDLINE + "$do_mx_all_ips" && [[ "$NODNS" == none ]] && fatal_cmd_line "\"--mx\" and \"--nodns=none\" don't work together" $ERR_CMDLINE if [[ -d $ADDTL_CA_FILES ]]; then ADDTL_CA_FILES="$ADDTL_CA_FILES/*.pem" @@ -23715,8 +23737,8 @@ parse_cmd_line() { ADDTL_CA_FILES="${ADDTL_CA_FILES//,/ }" fi 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 + [[ -s "$fname" ]] || fatal_cmd_line "CA file \"$fname\" does not exist" $ERR_RESOURCE + grep -q 'BEGIN CERTIFICATE' "$fname" || fatal_cmd_line "\"$fname\" is not CA file in PEM format" $ERR_RESOURCE done if "$do_starttls_injection" && [[ "$STARTTLS_PROTOCOL" =~ smtp ]]; then