Created
October 7, 2020 04:52
-
-
Save zachwalton/883b08a2acb6a36177b189977b009593 to your computer and use it in GitHub Desktop.
wtftls - monitor network devices for TLS alerts and display helpful error messages from RFC 5246
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
interpreter=$(ps h -p $$ -o args='' | cut -f1 -d' ') | |
if [[ "$interpreter" != *zsh* ]] && [[ ${BASH_VERSINFO:-0} -lt 4 ]]; then | |
echo "bash version 4 or greater required" >&2 | |
exit 1 | |
fi | |
declare -A messages | |
declare -A codes | |
# Below are messages copied from RFC 5246. Later alert codes that aren't supported by | |
# ssldump aren't supported here (may be an issue with TLS 1.3+) | |
# https://tools.ietf.org/html/rfc5246#section-7.2 | |
messages[close_notify]="This message notifies the recipient that the sender will not send | |
any more messages on this connection. Note that as of TLS 1.1, | |
failure to properly close a connection no longer requires that a | |
session not be resumed. This is a change from TLS 1.0 to conform | |
with widespread implementation practice." | |
messages[unexpected_message]="An inappropriate message was received. This alert is always fatal | |
and should never be observed in communication between proper | |
implementations." | |
messages[bad_record_mac]="This alert is returned if a record is received with an incorrect | |
MAC. This alert also MUST be returned if an alert is sent because | |
a TLSCiphertext decrypted in an invalid way: either it wasn't an | |
even multiple of the block length, or its padding values, when | |
checked, weren't correct. This message is always fatal and should | |
never be observed in communication between proper implementations | |
(except when messages were corrupted in the network)." | |
messages[decryption_failed]="This alert was used in some earlier versions of TLS, and may have | |
permitted certain attacks against the CBC mode [CBCATT]. It MUST | |
NOT be sent by compliant implementations." | |
messages[record_overflow]="A TLSCiphertext record was received that had a length more than | |
2^14+2048 bytes, or a record decrypted to a TLSCompressed record | |
with more than 2^14+1024 bytes. This message is always fatal and | |
should never be observed in communication between proper | |
implementations (except when messages were corrupted in the | |
network)." | |
messages[decompression_failure]="The decompression function received improper input (e.g., data | |
that would expand to excessive length). This message is always | |
fatal and should never be observed in communication between proper | |
implementations." | |
messages[handshake_failure]="Reception of a handshake_failure alert message indicates that the | |
sender was unable to negotiate an acceptable set of security | |
parameters given the options available. This is a fatal error." | |
messages[bad_certificate]="A certificate was corrupt, contained signatures that did not | |
verify correctly, etc." | |
messages[unsupported_certificate]="A certificate was of an unsupported type." | |
messages[certificate_revoked]="A certificate was revoked by its signer." | |
messages[certificate_expired]="A certificate has expired or is not currently valid." | |
messages[certificate_unknown]="Some other (unspecified) issue arose in processing the | |
certificate, rendering it unacceptable." | |
messages[illegal_parameter]="A field in the handshake was out of range or inconsistent with | |
other fields. This message is always fatal." | |
messages[unknown_ca]="A valid certificate chain or partial chain was received, but the | |
certificate was not accepted because the CA certificate could not | |
be located or couldn't be matched with a known, trusted CA. This | |
message is always fatal." | |
messages[access_denied]="A valid certificate was received, but when access control was | |
applied, the sender decided not to proceed with negotiation. This | |
message is always fatal." | |
messages[decode_error]="A message could not be decoded because some field was out of the | |
specified range or the length of the message was incorrect. This | |
message is always fatal and should never be observed in | |
communication between proper implementations (except when messages | |
were corrupted in the network)." | |
messages[decrypt_error]="A handshake cryptographic operation failed, including being unable | |
to correctly verify a signature or validate a Finished message. | |
This message is always fatal." | |
messages[export_restriction]="This alert was used in some earlier versions of TLS. It MUST NOT | |
be sent by compliant implementations." | |
messages[protocol_version]="The protocol version the client has attempted to negotiate is | |
recognized but not supported. (For example, old protocol versions | |
might be avoided for security reasons.) This message is always | |
fatal." | |
messages[insufficient_security]="Returned instead of handshake_failure when a negotiation has | |
failed specifically because the server requires ciphers more | |
secure than those supported by the client. This message is always | |
fatal." | |
messages[internal_error]="An internal error unrelated to the peer or the correctness of the | |
protocol (such as a memory allocation failure) makes it impossible | |
to continue. This message is always fatal." | |
messages[user_canceled]="This handshake is being canceled for some reason unrelated to a | |
protocol failure. If the user cancels an operation after the | |
handshake is complete, just closing the connection by sending a | |
close_notify is more appropriate. This alert should be followed | |
by a close_notify. This message is generally a warning." | |
messages[no_renegotiation]="Sent by the client in response to a hello request or by the server | |
in response to a client hello after initial handshaking. Either | |
of these would normally lead to renegotiation; when that is not | |
appropriate, the recipient should respond with this alert. At | |
that point, the original requester can decide whether to proceed | |
with the connection. One case where this would be appropriate is | |
where a server has spawned a process to satisfy a request; the | |
process might receive security parameters (key length, | |
authentication, etc.) at startup, and it might be difficult to | |
communicate changes to these parameters after that point. This | |
message is always a warning." | |
messages[close_notify]="This message notifies the recipient that the sender will not send | |
any more messages on this connection. Note that as of TLS 1.1, | |
failure to properly close a connection no longer requires that a | |
session not be resumed. This is a change from TLS 1.0 to conform | |
with widespread implementation practice." | |
messages[unexpected_message]="An inappropriate message was received. This alert is always fatal | |
and should never be observed in communication between proper | |
implementations." | |
messages[bad_record_mac]="This alert is returned if a record is received with an incorrect | |
MAC. This alert also MUST be returned if an alert is sent because | |
a TLSCiphertext decrypted in an invalid way: either it wasn't an | |
even multiple of the block length, or its padding values, when | |
checked, weren't correct. This message is always fatal and should | |
never be observed in communication between proper implementations | |
(except when messages were corrupted in the network)." | |
messages[decryption_failed]="This alert was used in some earlier versions of TLS, and may have | |
permitted certain attacks against the CBC mode [CBCATT]. It MUST | |
NOT be sent by compliant implementations." | |
messages[record_overflow]="A TLSCiphertext record was received that had a length more than | |
2^14+2048 bytes, or a record decrypted to a TLSCompressed record | |
with more than 2^14+1024 bytes. This message is always fatal and | |
should never be observed in communication between proper | |
implementations (except when messages were corrupted in the | |
network)." | |
messages[decompression_failure]="The decompression function received improper input (e.g., data | |
that would expand to excessive length). This message is always | |
fatal and should never be observed in communication between proper | |
implementations." | |
messages[handshake_failure]="Reception of a handshake_failure alert message indicates that the | |
sender was unable to negotiate an acceptable set of security | |
parameters given the options available. This is a fatal error." | |
messages[bad_certificate]="A certificate was corrupt, contained signatures that did not | |
verify correctly, etc." | |
messages[unsupported_certificate]="A certificate was of an unsupported type." | |
messages[certificate_revoked]="A certificate was revoked by its signer." | |
messages[certificate_expired]="A certificate has expired or is not currently valid." | |
messages[certificate_unknown]="Some other (unspecified) issue arose in processing the | |
certificate, rendering it unacceptable." | |
messages[illegal_parameter]="A field in the handshake was out of range or inconsistent with | |
other fields. This message is always fatal." | |
messages[unknown_ca]="A valid certificate chain or partial chain was received, but the | |
certificate was not accepted because the CA certificate could not | |
be located or couldn't be matched with a known, trusted CA. This | |
message is always fatal." | |
messages[access_denied]="A valid certificate was received, but when access control was | |
applied, the sender decided not to proceed with negotiation. This | |
message is always fatal." | |
messages[decode_error]="A message could not be decoded because some field was out of the | |
specified range or the length of the message was incorrect. This | |
message is always fatal and should never be observed in | |
communication between proper implementations (except when messages | |
were corrupted in the network)." | |
messages[decrypt_error]="A handshake cryptographic operation failed, including being unable | |
to correctly verify a signature or validate a Finished message. | |
This message is always fatal." | |
messages[export_restriction]="This alert was used in some earlier versions of TLS. It MUST NOT | |
be sent by compliant implementations." | |
messages[protocol_version]="The protocol version the client has attempted to negotiate is | |
recognized but not supported. (For example, old protocol versions | |
might be avoided for security reasons.) This message is always | |
fatal." | |
messages[insufficient_security]="Returned instead of handshake_failure when a negotiation has | |
failed specifically because the server requires ciphers more | |
secure than those supported by the client. This message is always | |
fatal." | |
messages[internal_error]="An internal error unrelated to the peer or the correctness of the | |
protocol (such as a memory allocation failure) makes it impossible | |
to continue. This message is always fatal." | |
messages[user_canceled]="This handshake is being canceled for some reason unrelated to a | |
protocol failure. If the user cancels an operation after the | |
handshake is complete, just closing the connection by sending a | |
close_notify is more appropriate. This alert should be followed | |
by a close_notify. This message is generally a warning." | |
messages[no_renegotiation]="Sent by the client in response to a hello request or by the server | |
in response to a client hello after initial handshaking. Either | |
of these would normally lead to renegotiation; when that is not | |
appropriate, the recipient should respond with this alert. At | |
that point, the original requester can decide whether to proceed | |
with the connection. One case where this would be appropriate is | |
where a server has spawned a process to satisfy a request; the | |
process might receive security parameters (key length, | |
authentication, etc.) at startup, and it might be difficult to | |
communicate changes to these parameters after that point. This | |
message is always a warning." | |
# alert codes copied from RFC 5246 | |
codes[close_notify]="0" | |
codes[unexpected_message]="10" | |
codes[bad_record_mac]="20" | |
codes[decryption_failed]="21" | |
codes[record_overflow]="22" | |
codes[decompression_failure]="30" | |
codes[handshake_failure]="40" | |
codes[bad_certificate]="42" | |
codes[unsupported_certificate]="43" | |
codes[certificate_revoked]="44" | |
codes[certificate_expired]="45" | |
codes[certificate_unknown]="46" | |
codes[illegal_parameter]="47" | |
codes[unknown_ca]="48" | |
codes[access_denied]="49" | |
codes[decode_error]="50" | |
codes[decrypt_error]="51" | |
codes[export_restriction]="60" | |
codes[protocol_version]="70" | |
codes[insufficient_security]="71" | |
codes[internal_error]="80" | |
codes[user_canceled]="90" | |
codes[no_renegotiation]="100" | |
if [[ -z "$1" ]] || [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then | |
cat <<"EOF" | |
usage: wtftls -i <interface> ... | |
wtftls wraps ssldump to monitor a network device for TLS traffic. | |
when an alert is encountered (aka the connection failed), it prints | |
the alert code and a meaningful description from RFC 5246. | |
this is to aid in debugging TLS connections without resorting to | |
wireshark or referencing the RFC. | |
see `man 1 ssldump` for all supported flags. | |
EOF | |
exit 0 | |
fi | |
if ! command -v ssldump &> /dev/null; then | |
echo "ssldump command not found" | |
fi | |
stdbuf -o0 ssldump -q "$@" | while IFS= read -r line; do | |
if [[ "$line" == *Alert* ]]; then | |
alert=$(echo "$line" | awk '{print $NF}') | |
if [[ ! -z "$alert" ]]; then | |
echo -e "Error in TLS connection: ${alert} [alert code ${codes[$alert]}]\\n" | |
echo "${messages[$alert]}" | |
echo | |
fi | |
fi | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
brew install bash ssldump
to use on macos (or invoke with zsh:zsh ./wtftls
)