@@ -197,6 +197,7 @@ IGN_OCSP_PROXY=${IGN_OCSP_PROXY:-false} # Also when --proxy is supplied it is ig
197197HEADER_MAXSLEEP=${HEADER_MAXSLEEP:-5} # we wait this long before killing the process to retrieve a service banner / http header
198198MAX_SOCKET_FAIL=${MAX_SOCKET_FAIL:-2} # If this many failures for TCP socket connects are reached we terminate
199199MAX_OSSL_FAIL=${MAX_OSSL_FAIL:-2} # If this many failures for s_client connects are reached we terminate
200+ MAX_STARTTLS_FAIL=${MAX_STARTTLS_FAIL:-2} # max number of STARTTLS handshake failures in plaintext phase
200201MAX_HEADER_FAIL=${MAX_HEADER_FAIL:-2} # If this many failures for HTTP GET are encountered we don't try again to get the header
201202MAX_WAITSOCK=${MAX_WAITSOCK:-10} # waiting at max 10 seconds for socket reply. There shouldn't be any reason to change this.
202203CCS_MAX_WAITSOCK=${CCS_MAX_WAITSOCK:-5} # for the two CCS payload (each). There shouldn't be any reason to change this.
@@ -252,6 +253,7 @@ TIMEOUT_CMD=""
252253HAD_SLEPT=0
253254NR_SOCKET_FAIL=0 # Counter for socket failures
254255NR_OSSL_FAIL=0 # .. for OpenSSL connects
256+ NR_STARTTLS_FAIL=0 # .. for STARTTLS failures
255257NR_HEADER_FAIL=0 # .. for HTTP_GET
256258PROTOS_OFFERED="" # This keeps which protocol is being offered. See has_server_protocol().
257259TLS12_CIPHER_OFFERED="" # This contains the hexcode of a cipher known to be supported by the server with TLS 1.2
@@ -10415,16 +10417,19 @@ starttls_io() {
1041510417
1041610418
1041710419# Line-based send with newline characters appended (arg2 empty)
10418- # Stream-based send (not in use currently): arg2: <any>.
10420+ # arg2: debug_string -- what we had in the caller previously
1041910421starttls_just_send(){
10420- if [[ -z "$2" ]] ; then
10421- debugme echo "C: $1\r\n"
10422- echo -ne "$1\r\n" >&5
10422+ local -i ret=0
10423+
10424+ debugme echo "C: $1\r\n"
10425+ echo -ne "$1\r\n" >&5
10426+ ret=$?
10427+ if [[ $ret -eq 0 ]]; then
10428+ debugme echo " > succeeded: $2"
1042310429 else
10424- debugme echo -e "C: $1"
10425- echo -ne "$1" >&5
10430+ debugme echo " > failed: $2 ($ret)"
1042610431 fi
10427- return $?
10432+ return $ret
1042810433}
1042910434
1043010435# arg1: (optional): wait time
@@ -10444,66 +10449,74 @@ starttls_just_read(){
1044410449starttls_full_read(){
1044510450 local cont_pattern="$1"
1044610451 local end_pattern="$2"
10447- local regex="$3"
10452+ local starttls_regex="$3" # optional: pattern we search for in the server's response
10453+ local debug_str="$4" # optional
1044810454 local starttls_read_data=()
1044910455 local one_line=""
1045010456 local ret=0
1045110457 local ret_found=0
1045210458 local debugpad=" > found: "
10459+ local oldIFS="$IFS"
1045310460
1045410461 debugme echo "=== reading banner ... ==="
10455- if [[ $# -ge 3 ]]; then
10456- debugme echo "=== we'll have to search for \"$regex\" pattern ==="
10462+ if [[ -n "$starttls_regex" ]]; then
10463+ debugme echo "=== we'll have to search for \"$starttls_regex\" pattern ==="
10464+ # pre-set an error if we won't find the ~regex
1045710465 ret_found=3
1045810466 fi
1045910467
10460- local oldIFS="$IFS"
1046110468 IFS=''
10469+ # Now read handshake line by line and act on the args supplied.
10470+ # Exit the subshell if timeout has been hit (-t $STARTTLS_SLEEP)
1046210471 while read -r -t $STARTTLS_SLEEP one_line; ret=$?; (exit $ret); do
1046310472 debugme tmln_out "S: ${one_line}"
1046410473 if [[ $DEBUG -ge 5 ]]; then
1046510474 echo "end_pattern/cont_pattern: ${end_pattern} / ${cont_pattern}"
1046610475 fi
10467- if [[ $# -ge 3 ]]; then
10468- if [[ ${one_line} =~ $regex ]]; then
10469- ret_found=0
10476+ if [[ -n "$starttls_regex" ]]; then
10477+ if [[ ${one_line} =~ $starttls_regex ]]; then
1047010478 debugme tmln_out "${debugpad} ${one_line} "
10479+ # We don't exit here as the buffer is not empty. So we continue reading but save the status:
10480+ ret_found=0
1047110481 fi
1047210482 fi
1047310483 starttls_read_data+=("${one_line}")
1047410484 if [[ ${one_line} =~ ${end_pattern} ]]; then
1047510485 debugme tmln_out "${debugpad} ${one_line} "
1047610486 IFS="${oldIFS}"
10477- return ${ret_found}
10487+ break
1047810488 fi
1047910489 if [[ ! ${one_line} =~ ${cont_pattern} ]]; then
1048010490 debugme echo "=== full read syntax error, expected regex pattern ${cont_pattern} (cont) or ${end_pattern} (end) ==="
1048110491 IFS="${oldIFS}"
10482- return 2
10492+ ret_found=2
10493+ break
1048310494 fi
1048410495 done <&5
10485- if [[ $DEBUG -ge 2 ]]; then
10496+ if [[ $ret_found -eq 0 ]]; then
10497+ # Print the debug statement we previously had in the caller function
10498+ [[ -n "$debug_str" ]] && debugme echo " >> $debug_str"
10499+ else
1048610500 if [[ $ret -ge 128 ]]; then
10487- echo "=== timeout reading ==="
10488- else
10489- echo "=== full read error (no timeout) ==="
10501+ debugme echo "=== timeout reading ==="
10502+ $ret_found=$ret
1049010503 fi
1049110504 fi
1049210505 IFS="${oldIFS}"
10493- return $ret
10506+ return $ret_found
1049410507}
1049510508
1049610509starttls_ftp_dialog() {
10497- local debugpad=" > "
10498- local reAUTHTLS ='^ AUTH TLS'
10510+ local -i ret=0
10511+ local reSTARTTLS ='^ AUTH TLS'
1049910512
1050010513 debugme echo "=== starting ftp STARTTLS dialog ==="
10501- starttls_full_read '^220-' '^220 ' && debugme echo "${debugpad} received server greeting" &&
10502- starttls_just_send 'FEAT' && debugme echo "${debugpad} sent FEAT" &&
10503- starttls_full_read '^(211-| )' '^211 ' "${reAUTHTLS }" && debugme echo "${debugpad} received server features and checked STARTTLS availability" &&
10504- starttls_just_send 'AUTH TLS' && debugme echo "${debugpad} initiated STARTTLS" &&
10505- starttls_full_read '^234-' '^234 ' && debugme echo "${debugpad} received ack for STARTTLS"
10506- local ret=$?
10514+ starttls_full_read '^220-' '^220 ' '' " received server greeting" &&
10515+ starttls_just_send 'FEAT' " sent FEAT" &&
10516+ starttls_full_read '^(211-| )' '^211 ' "${reSTARTTLS }" " received server features and checked STARTTLS availability" &&
10517+ starttls_just_send 'AUTH TLS' " initiated STARTTLS" &&
10518+ starttls_full_read '^234-' '^234 ' '' " received ack for STARTTLS"
10519+ ret=$?
1050710520 debugme echo "=== finished ftp STARTTLS dialog with ${ret} ==="
1050810521 return $ret
1050910522}
@@ -10513,59 +10526,61 @@ starttls_ftp_dialog() {
1051310526starttls_smtp_dialog() {
1051410527 local greet_str="EHLO testssl.sh"
1051510528 local proto="smtp"
10516- local re250STARTTLS ='^250[ -]STARTTLS'
10517- local debugpad=" > "
10529+ local reSTARTTLS ='^250[ -]STARTTLS'
10530+ local -i ret=0
1051810531
1051910532 if [[ "$1" == lmtp ]]; then
1052010533 proto="lmtp"
1052110534 greet_str="LHLO"
1052210535 fi
1052310536 if [[ -n "$2" ]]; then
10524- # Here we can "add" commands in the future. Please note \r\n will automatically appended
10537+ # Here we can "add" commands in the future. Please note \r\n will automatically be appended
1052510538 greet_str="$2"
1052610539 elif "$SNEAKY"; then
1052710540 greet_str="EHLO google.com"
1052810541 fi
1052910542 debugme echo "=== starting $proto STARTTLS dialog ==="
1053010543
10531- starttls_full_read '^220-' '^220 ' && debugme echo "${debugpad} received server greeting" &&
10532- starttls_just_send "$greet_str" && debugme echo "${debugpad} sent $greet_str" &&
10533- starttls_full_read '^250-' '^250 ' "${re250STARTTLS }" && debugme echo "${debugpad} received server capabilities and checked STARTTLS availability" &&
10534- starttls_just_send 'STARTTLS' && debugme echo "${debugpad} initiated STARTTLS" &&
10535- starttls_full_read '^220-' '^220 ' && debugme echo "${debugpad} received ack for STARTTLS"
10536- local ret=$?
10544+ starttls_full_read '^220-' '^220 ' '' " received server greeting" &&
10545+ starttls_just_send "$greet_str" " sent $greet_str" &&
10546+ starttls_full_read '^250-' '^250 ' "${reSTARTTLS }" " received server capabilities and checked STARTTLS availability" &&
10547+ starttls_just_send 'STARTTLS' " initiated STARTTLS" &&
10548+ starttls_full_read '^220-' '^220 ' '' " received ack for STARTTLS"
10549+ ret=$?
1053710550 debugme echo "=== finished $proto STARTTLS dialog with ${ret} ==="
1053810551 return $ret
1053910552}
1054010553
1054110554starttls_pop3_dialog() {
10542- local debugpad=" > "
10555+ local -i ret=0
1054310556
1054410557 debugme echo "=== starting pop3 STARTTLS dialog ==="
10545- starttls_full_read '^\+OK' '^\+OK' && debugme echo "${debugpad} received server greeting" &&
10546- starttls_just_send 'STLS' && debugme echo "${debugpad} initiated STARTTLS" &&
10547- starttls_full_read '^\+OK' '^\+OK' && debugme echo "${debugpad} received ack for STARTTLS"
10548- local ret=$?
10558+ starttls_full_read '^\+OK' '^\+OK' '' " received server greeting" &&
10559+ starttls_just_send 'STLS' " initiated STARTTLS" &&
10560+ starttls_full_read '^\+OK' '^\+OK' '' " received ack for STARTTLS"
10561+ ret=$?
1054910562 debugme echo "=== finished pop3 STARTTLS dialog with ${ret} ==="
1055010563 return $ret
1055110564}
1055210565
1055310566starttls_imap_dialog() {
10567+ local -i ret=0
1055410568 local reSTARTTLS='^\* CAPABILITY(( .*)? IMAP4rev1( .*)? STARTTLS(.*)?|( .*)? STARTTLS( .*)? IMAP4rev1(.*)?)$'
10555- local debugpad=" > "
1055610569
1055710570 debugme echo "=== starting imap STARTTLS dialog ==="
10558- starttls_full_read '^\* ' '^\* OK ' && debugme echo "${debugpad} received server greeting" &&
10559- starttls_just_send 'a001 CAPABILITY' && debugme echo "${debugpad} sent CAPABILITY" &&
10560- starttls_full_read '^\* ' '^a001 OK ' "${reSTARTTLS}" && debugme echo "${debugpad} received server capabilities and checked STARTTLS availability" &&
10561- starttls_just_send 'a002 STARTTLS' && debugme echo "${debugpad} initiated STARTTLS" &&
10562- starttls_full_read '^\* ' '^a002 OK ' && debugme echo "${debugpad} received ack for STARTTLS"
10563- local ret=$?
10571+ starttls_full_read '^\* ' '^\* OK ' '' " received server greeting" &&
10572+ starttls_just_send 'a001 CAPABILITY' " sent CAPABILITY" &&
10573+ starttls_full_read '^\* ' '^a001 OK ' "${reSTARTTLS}" " received server capabilities and checked STARTTLS availability" &&
10574+ starttls_just_send 'a002 STARTTLS' " initiated STARTTLS" &&
10575+ starttls_full_read '^\* ' '^a002 OK ' '' " received ack for STARTTLS"
10576+ ret=$?
1056410577 debugme echo "=== finished imap STARTTLS dialog with ${ret} ==="
1056510578 return $ret
1056610579}
1056710580
1056810581starttls_xmpp_dialog() {
10582+ local -i ret=0
10583+
1056910584 debugme echo "=== starting xmpp STARTTLS dialog ==="
1057010585 [[ -z $XMPP_HOST ]] && XMPP_HOST="$NODE"
1057110586
@@ -10575,36 +10590,40 @@ starttls_xmpp_dialog() {
1057510590 starttls_io "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"$namespace"' to='"$XMPP_HOST"' version='1.0'>" 'starttls(.*)features' 1 &&
1057610591 starttls_io "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>" '<proceed' 1
1057710592 # starttls_io "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"$namespace"' to='"$XMPP_HOST"' version='1.0'>" 'JUSTSEND' 2
10578- local ret=$?
10593+ ret=$?
1057910594 debugme echo "=== finished xmpp STARTTLS dialog with ${ret} ==="
1058010595 return $ret
1058110596}
1058210597
1058310598starttls_nntp_dialog() {
10584- local debugpad=" > "
10599+ local -i ret=0
1058510600
1058610601 debugme echo "=== starting nntp STARTTLS dialog ==="
10587- starttls_full_read '$^' '^20[01] ' && debugme echo "${debugpad} received server greeting" &&
10588- starttls_just_send 'STARTTLS' && debugme echo "${debugpad} initiated STARTTLS" &&
10589- starttls_full_read '$^' '^382 ' && debugme echo "${debugpad} received ack for STARTTLS"
10590- local ret=$?
10602+ starttls_full_read '$^' '^20[01] ' '' " received server greeting" &&
10603+ starttls_just_send 'STARTTLS' " initiated STARTTLS" &&
10604+ starttls_full_read '$^' '^382 ' '' " received ack for STARTTLS"
10605+ ret=$?
1059110606 debugme echo "=== finished nntp STARTTLS dialog with ${ret} ==="
1059210607 return $ret
1059310608}
1059410609
1059510610starttls_postgres_dialog() {
10611+ local -i ret=0
10612+ local debugpad=" > "
10613+ local starttls_init=", x00, x00 ,x00 ,x08 ,x04 ,xD2 ,x16 ,x2F"
10614+
1059610615 debugme echo "=== starting postgres STARTTLS dialog ==="
10597- local init_tls=", x00, x00 ,x00 ,x08 ,x04 ,xD2 ,x16 ,x2F"
10598- socksend "${init_tls}" 0 && debugme echo "${debugpad}initiated STARTTLS" &&
10616+ socksend "${starttls_init}" 0 && debugme echo "${debugpad}initiated STARTTLS" &&
1059910617 starttls_io "" S 1 && debugme echo "${debugpad}received ack (="S") for STARTTLS"
10600- local ret=$?
10618+ ret=$?
1060110619 debugme echo "=== finished postgres STARTTLS dialog with ${ret} ==="
1060210620 return $ret
1060310621}
1060410622
1060510623starttls_mysql_dialog() {
1060610624 local debugpad=" > "
10607- local login_request="
10625+ local -i ret=0
10626+ local starttls_init="
1060810627 , x20, x00, x00, x01, # payload_length, sequence_id
1060910628 x85, xae, xff, x00, # capability flags, CLIENT_SSL always set
1061010629 x00, x00, x00, x01, # max-packet size
@@ -10614,16 +10633,16 @@ starttls_mysql_dialog() {
1061410633 x00, x00, x00, x00, x00, x00, x00"
1061510634
1061610635 debugme echo "=== starting mysql STARTTLS dialog ==="
10617- socksend "${login_request }" 0
10618- starttls_just_read 1 && debugme echo "${debugpad} read succeeded"
10636+ socksend "${starttls_init }" 0 && debugme echo "${debugpad}initiated STARTTLS" &&
10637+ starttls_just_read 1 " read succeeded"
1061910638 # 1 is the timeout value which only MySQL needs. Note, there seems no response whether STARTTLS
1062010639 # succeeded. We could try harder, see https://github.com/openssl/openssl/blob/master/apps/s_client.c
1062110640 # but atm this seems sufficient as later we will fail if there's no STARTTLS.
1062210641 # BUT: there seeem to be cases when the handshake fails (8S01Bad handshake --> 30 38 53 30 31 42 61 64 20 68 61 6e 64 73 68 61 6b 65).
1062310642 # also there's a banner in the reply "<version><somebytes>mysql_native_password"
1062410643 # TODO: We could detect if the server supports STARTTLS via the "Server Capabilities"
1062510644 # bit field, but we'd need to parse the binary stream, with greater precision than regex.
10626- local ret=$?
10645+ ret=$?
1062710646 debugme echo "=== finished mysql STARTTLS dialog with ${ret} ==="
1062810647 return $ret
1062910648}
@@ -10731,9 +10750,19 @@ fd_socket() {
1073110750 *) # we need to throw an error here -- otherwise testssl.sh treats the STARTTLS protocol as plain SSL/TLS which leads to FP
1073210751 fatal "FIXME: STARTTLS protocol $STARTTLS_PROTOCOL is not yet supported" $ERR_NOSUPPORT
1073310752 esac
10753+ ret=$?
10754+ case $ret in
10755+ 0) return 0 ;;
10756+ 3) fatal "No STARTTLS found in handshake" $ERR_CONNECT ;;
10757+ *) ((NR_STARTTLS_FAIL++))
10758+ # This are mostly timeouts here (code >=128). We give the client a chance to try again later. For cases
10759+ # where we have no STARTTLS in the server banner however - ret code=3 - we don't neet to try again
10760+ connectivity_problem $NR_STARTTLS_FAIL $MAX_STARTTLS_FAIL "STARTTLS handshake failed (code: $ret)" "repeated STARTTLS problems, giving up ($ret)"
10761+ return 6 ;;
10762+ esac
1073410763 fi
10764+ # Plain socket ok, yes or no?
1073510765 [[ $? -eq 0 ]] && return 0
10736- prln_warning " STARTTLS handshake failed"
1073710766 return 1
1073810767}
1073910768
0 commit comments