Skip to content

Commit

Permalink
Force strong TLS cipher suites in rustup-init.sh
Browse files Browse the repository at this point in the history
Force curl and wget to use strong TLS cipher suites if supported by
local tools. If RUSTUP_TLS_CIPHERSUITES variable is set by user then
use it. Closes #2284.

curl and wget TLS backends supported for strong TLS cipher suites:
GnuTLS, OpenSSL, LibreSSL and BoringSSL. Other backends
(NSS, WolfSSL, etc.) fall back to prior behavior but can also
handle user-specified cipher suites in any syntax required
(syntax is not checked by script).

curl and wget (if support is detected) will use the same strong
TLS 1.2-1.3 cipher suite as Firefox 68 ESR with all weak cipher
suites disabled using about:config. Sequence of all 9 cipher suites
for OpenSSL is identical to Firefox (slightly different for GnuTLS).

DHE is excluded from TLS 1.2 because servers often use bad
DH params (see RFC 7919).

GnuTLS priority string produces:

TLS_AES_128_GCM_SHA256                 0x13, 0x01    TLS1.3
TLS_CHACHA20_POLY1305_SHA256           0x13, 0x03    TLS1.3
TLS_AES_256_GCM_SHA384                 0x13, 0x02    TLS1.3
TLS_ECDHE_ECDSA_AES_128_GCM_SHA256     0xc0, 0x2b    TLS1.2
TLS_ECDHE_ECDSA_CHACHA20_POLY1305      0xcc, 0xa9    TLS1.2
TLS_ECDHE_ECDSA_AES_256_GCM_SHA384     0xc0, 0x2c    TLS1.2
TLS_ECDHE_RSA_AES_128_GCM_SHA256       0xc0, 0x2f    TLS1.2
TLS_ECDHE_RSA_CHACHA20_POLY1305        0xcc, 0xa8    TLS1.2
TLS_ECDHE_RSA_AES_256_GCM_SHA384       0xc0, 0x30    TLS1.2

GnuTLS priority string could be hardened more but it isn't forgiving
of unknown/unsupported values, so the bare minimum was specified.
  • Loading branch information
x448 committed Apr 21, 2020
1 parent 2138b7a commit 59114b8
Showing 1 changed file with 110 additions and 8 deletions.
118 changes: 110 additions & 8 deletions rustup-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ ignore() {
# use wget instead.
downloader() {
local _dld
local _ciphersuites
if check_cmd curl; then
_dld=curl
elif check_cmd wget; then
Expand All @@ -387,18 +388,32 @@ downloader() {
if [ "$1" = --check ]; then
need_cmd "$_dld"
elif [ "$_dld" = curl ]; then
if ! check_help_for "$3" curl --proto --tlsv1.2; then
echo "Warning: Not forcing TLS v1.2, this is potentially less secure"
curl --silent --show-error --fail --location "$1" --output "$2"
get_ciphersuites_for_curl
_ciphersuites="$RETVAL"
if [ -n "$_ciphersuites" ]; then
curl --proto '=https' --tlsv1.2 --ciphers "$_ciphersuites" --silent --show-error --fail --location "$1" --output "$2"
else
curl --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2"
echo "Warning: Not forcing strong cipher suites for TLS, this is potentially less secure"
if ! check_help_for "$3" curl --proto --tlsv1.2; then
echo "Warning: Not forcing TLS v1.2, this is potentially less secure"
curl --silent --show-error --fail --location "$1" --output "$2"
else
curl --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2"
fi
fi
elif [ "$_dld" = wget ]; then
if ! check_help_for "$3" wget --https-only --secure-protocol; then
echo "Warning: Not forcing TLS v1.2, this is potentially less secure"
wget "$1" -O "$2"
get_ciphersuites_for_wget
_ciphersuites="$RETVAL"
if [ -n "$_ciphersuites" ]; then
wget --https-only --secure-protocol=TLSv1_2 --ciphers "$_ciphersuites" "$1" -O "$2"
else
wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2"
echo "Warning: Not forcing strong cipher suites for TLS, this is potentially less secure"
if ! check_help_for "$3" wget --https-only --secure-protocol; then
echo "Warning: Not forcing TLS v1.2, this is potentially less secure"
wget "$1" -O "$2"
else
wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2"
fi
fi
else
err "Unknown downloader" # should not reach here
Expand Down Expand Up @@ -441,4 +456,91 @@ check_help_for() {
test "$_ok" = "y"
}

# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
# if support by local tools is detected. Detection currently supports these curl backends:
# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
get_ciphersuites_for_curl() {
if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then
# user specified custom cipher suites, assume they know what they're doing
RETVAL="$RUSTUP_TLS_CIPHERSUITES"
return
fi

local _openssl_syntax="no"
local _gnutls_syntax="no"
local _backend_supported="yes"
if curl -V | grep -q ' OpenSSL/'; then
_openssl_syntax="yes"
elif curl -V | grep -iq ' LibreSSL/'; then
_openssl_syntax="yes"
elif curl -V | grep -iq ' BoringSSL/'; then
_openssl_syntax="yes"
elif curl -V | grep -iq ' GnuTLS/'; then
_gnutls_syntax="yes"
else
_backend_supported="no"
fi

local _args_supported="no"
if [ "$_backend_supported" = "yes" ]; then
# "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
if check_help_for "notspecified" "curl" "--tlsv1.2" "--ciphers" "--proto"; then
_args_supported="yes"
fi
fi

local _cs=""
if [ "$_args_supported" = "yes" ]; then
if [ "$_openssl_syntax" = "yes" ]; then
_cs=$(get_strong_ciphersuites_for "openssl")
elif [ "$_gnutls_syntax" = "yes" ]; then
_cs=$(get_strong_ciphersuites_for "gnutls")
fi
fi

RETVAL="$_cs"
}

# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
# if support by local tools is detected. Detection currently supports these wget backends:
# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
get_ciphersuites_for_wget() {
if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then
# user specified custom cipher suites, assume they know what they're doing
RETVAL="$RUSTUP_TLS_CIPHERSUITES"
return
fi

local _cs=""
if wget -V | grep -q '\-DHAVE_LIBSSL'; then
# "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
_cs=$(get_strong_ciphersuites_for "openssl")
fi
elif wget -V | grep -q '\-DHAVE_LIBGNUTLS'; then
# "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
_cs=$(get_strong_ciphersuites_for "gnutls")
fi
fi

RETVAL="$_cs"
}

# Return strong TLS 1.2-1.3 cipher suites in OpenSSL or GnuTLS syntax. TLS 1.2
# excludes non-ECDHE and non-AEAD cipher suites. DHE is excluded due to bad
# DH params often found on servers (see RFC 7919). Sequence matches or is
# similar to Firefox 68 ESR with weak cipher suites disabled via about:config.
# $1 must be openssl or gnutls.
get_strong_ciphersuites_for() {
if [ "$1" = "openssl" ]; then
# OpenSSL is forgiving of unknown values, no problems with TLS 1.3 values on versions that don't support it yet.
echo "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
elif [ "$1" = "gnutls" ]; then
# GnuTLS isn't forgiving of unknown values, so this may require a GnuTLS version that supports TLS 1.3 even if wget doesn't.
# Begin with SECURE128 (and higher) then remove/add to build cipher suites. Produces same 9 cipher suites as OpenSSL but in slightly different order.
echo "SECURE128:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS-ALL:-CIPHER-ALL:-MAC-ALL:-KX-ALL:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+AES-128-GCM:+CHACHA20-POLY1305:+AES-256-GCM"
fi
}

main "$@" || exit 1

1 comment on commit 59114b8

@verdinjoshua99
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My data

Please sign in to comment.