Commit 77b11965 authored by Julius Goryavsky's avatar Julius Goryavsky

MDEV-26360: Using hostnames breaks certificate validation

Fixed flaws with overly strict or, conversely,
overly soft verification of certificates in some
scenarios:

1. Removed the check that the 'commonname' (CN) in the
   certificate matches the 'localhost' value on the side
   of the joiner node, which was performed earlier, even
   if the address was received by the script only as an
   argument (out of the exchange via the Galera protocol) -
   since for the joining node this argument always contains
   its own local address, not the address of the remote host,
   so it is always treated as 'localhost', which is not
   necessarily true (outside of mtr testing);
2. Removed checking the domain name or IP-address of the
   peer node in the encrypt=2 mode;
3. Fixed checking of compliance of certificates when
   rsync SST is used;
4. Added the ability to specify CA not only as a file,
   but also as a path to the directory where the certificates
   are stored. To do this, the user just needs to specify the
   path to this directory as the value ssl-ca or tca parameter,
   ending with the '/' character.
parent 467011bc
...@@ -1065,8 +1065,9 @@ check_for_dhparams() ...@@ -1065,8 +1065,9 @@ check_for_dhparams()
# #
verify_ca_matches_cert() verify_ca_matches_cert()
{ {
local ca_path="$1" local ca="$1"
local cert_path="$2" local cert="$2"
local path=${3:-0}
# If the openssl utility is not installed, then # If the openssl utility is not installed, then
# we will not do this certificate check: # we will not do this certificate check:
...@@ -1075,8 +1076,15 @@ verify_ca_matches_cert() ...@@ -1075,8 +1076,15 @@ verify_ca_matches_cert()
return return
fi fi
if ! "$OPENSSL_BINARY" verify -verbose -CAfile "$ca_path" "$cert_path" >/dev/null 2>&1 local not_match=0
then
if [ $path -eq 0 ]; then
"$OPENSSL_BINARY" verify -verbose -CAfile "$ca" "$cert" >/dev/null 2>&1 || not_match=1
else
"$OPENSSL_BINARY" verify -verbose -CApath "$ca" "$cert" >/dev/null 2>&1 || not_match=1
fi
if [ $not_match -eq 1 ]; then
wsrep_log_error "******** FATAL ERROR ********************************************" wsrep_log_error "******** FATAL ERROR ********************************************"
wsrep_log_error "* The certifcate and CA (certificate authority) do not match. *" wsrep_log_error "* The certifcate and CA (certificate authority) do not match. *"
wsrep_log_error "* It does not appear that the certificate was issued by the CA. *" wsrep_log_error "* It does not appear that the certificate was issued by the CA. *"
......
...@@ -34,6 +34,7 @@ ssyslog="" ...@@ -34,6 +34,7 @@ ssyslog=""
ssystag="" ssystag=""
BACKUP_PID="" BACKUP_PID=""
tcert="" tcert=""
tpath=0
tpem="" tpem=""
tkey="" tkey=""
tmode="DISABLED" tmode="DISABLED"
...@@ -85,7 +86,6 @@ readonly SECRET_TAG="secret" ...@@ -85,7 +86,6 @@ readonly SECRET_TAG="secret"
# Required for backup locks # Required for backup locks
# For backup locks it is 1 sent by joiner # For backup locks it is 1 sent by joiner
# 5.6.21 PXC and later can't donate to an older joiner
sst_ver=1 sst_ver=1
if [ -n "$(command -v pv)" ] && pv --help | grep -qw -- '-F'; then if [ -n "$(command -v pv)" ] && pv --help | grep -qw -- '-F'; then
...@@ -339,64 +339,83 @@ get_transfer() ...@@ -339,64 +339,83 @@ get_transfer()
fi fi
fi fi
CN_option=",commonname=''"
if [ $encrypt -eq 2 ]; then if [ $encrypt -eq 2 ]; then
wsrep_log_info "Using openssl based encryption with socat: with crt and pem" wsrep_log_info "Using openssl based encryption with socat: with crt and pem"
if [ -z "$tpem" -o -z "$tcert" ]; then if [ -z "$tpem" -o -z "$tcert" ]; then
wsrep_log_error "Both PEM and CRT files required" wsrep_log_error \
"Both PEM file and CRT file (or path) are required"
exit 22 exit 22
fi fi
if [ ! -r "$tpem" -o ! -r "$tcert" ]; then if [ ! -r "$tpem" -o ! -r "$tcert" ]; then
wsrep_log_error "Both PEM and CRT files must be readable" wsrep_log_error \
"Both PEM file and CRT file (or path) must be readable"
exit 22 exit 22
fi fi
verify_ca_matches_cert "$tcert" "$tpem" verify_ca_matches_cert "$tcert" "$tpem" $tpath
tcmd="$tcmd,cert='$tpem',cafile='$tcert'$sockopt" if [ $tpath -eq 0 ]; then
tcmd="$tcmd,cert='$tpem',cafile='$tcert'"
else
tcmd="$tcmd,cert='$tpem',capath='$tcert'"
fi
stagemsg="$stagemsg-OpenSSL-Encrypted-2" stagemsg="$stagemsg-OpenSSL-Encrypted-2"
wsrep_log_info "$action with cert=$tpem, cafile=$tcert" wsrep_log_info "$action with cert=$tpem, ca=$tcert"
elif [ $encrypt -eq 3 -o $encrypt -eq 4 ]; then elif [ $encrypt -eq 3 -o $encrypt -eq 4 ]; then
wsrep_log_info "Using openssl based encryption with socat: with key and crt" wsrep_log_info "Using openssl based encryption with socat: with key and crt"
if [ -z "$tpem" -o -z "$tkey" ]; then if [ -z "$tpem" -o -z "$tkey" ]; then
wsrep_log_error "Both certificate and key files required" wsrep_log_error "Both certificate file (or path) " \
"and key file are required"
exit 22 exit 22
fi fi
if [ ! -r "$tpem" -o ! -r "$tkey" ]; then if [ ! -r "$tpem" -o ! -r "$tkey" ]; then
wsrep_log_error "Both certificate and key files must be readable" wsrep_log_error "Both certificate file (or path) " \
"and key file must be readable"
exit 22 exit 22
fi fi
verify_cert_matches_key "$tpem" "$tkey" verify_cert_matches_key "$tpem" "$tkey"
stagemsg="$stagemsg-OpenSSL-Encrypted-3" stagemsg="$stagemsg-OpenSSL-Encrypted-3"
if [ -z "$tcert" ]; then if [ -z "$tcert" ]; then
if [ $encrypt -eq 4 ]; then if [ $encrypt -eq 4 ]; then
wsrep_log_error "Peer certificate required if encrypt=4" wsrep_log_error \
"Peer certificate file (or path) required if encrypt=4"
exit 22 exit 22
fi fi
# no verification # no verification
tcmd="$tcmd,cert='$tpem',key='$tkey',verify=0$sockopt" CN_option=""
tcmd="$tcmd,cert='$tpem',key='$tkey',verify=0"
wsrep_log_info "$action with cert=$tpem, key=$tkey, verify=0" wsrep_log_info "$action with cert=$tpem, key=$tkey, verify=0"
else else
# CA verification # CA verification
if [ ! -r "$tcert" ]; then if [ ! -r "$tcert" ]; then
wsrep_log_error "Certificate file must be readable" wsrep_log_error "Certificate file or path must be readable"
exit 22 exit 22
fi fi
verify_ca_matches_cert "$tcert" "$tpem" verify_ca_matches_cert "$tcert" "$tpem" $tpath
if [ -n "$WSREP_SST_OPT_REMOTE_USER" ]; then if [ -n "$WSREP_SST_OPT_REMOTE_USER" ]; then
CN_option=",commonname='$WSREP_SST_OPT_REMOTE_USER'" CN_option=",commonname='$WSREP_SST_OPT_REMOTE_USER'"
elif [ $encrypt -eq 4 ]; then elif [ "$WSREP_SST_OPT_ROLE" = 'joiner' -o $encrypt -eq 4 ]
then
CN_option=",commonname=''" CN_option=",commonname=''"
elif is_local_ip "$WSREP_SST_OPT_HOST_UNESCAPED"; then elif is_local_ip "$WSREP_SST_OPT_HOST_UNESCAPED"; then
CN_option=',commonname=localhost' CN_option=',commonname=localhost'
else else
CN_option=",commonname='$WSREP_SST_OPT_HOST_UNESCAPED'" CN_option=",commonname='$WSREP_SST_OPT_HOST_UNESCAPED'"
fi fi
tcmd="$tcmd,cert='$tpem',key='$tkey',cafile='$tcert'$CN_option$sockopt" if [ $tpath -eq 0 ]; then
wsrep_log_info "$action with cert=$tpem, key=$tkey, cafile=$tcert" tcmd="$tcmd,cert='$tpem',key='$tkey',cafile='$tcert'"
else
tcmd="$tcmd,cert='$tpem',key='$tkey',capath='$tcert'"
fi
wsrep_log_info "$action with cert=$tpem, key=$tkey, ca=$tcert"
fi fi
else else
wsrep_log_info "Unknown encryption mode: encrypt=$encrypt" wsrep_log_info "Unknown encryption mode: encrypt=$encrypt"
exit 22 exit 22
fi fi
tcmd="$tcmd$CN_option$sockopt"
if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
tcmd="$tcmd stdio" tcmd="$tcmd stdio"
fi fi
...@@ -473,6 +492,12 @@ check_server_ssl_config() ...@@ -473,6 +492,12 @@ check_server_ssl_config()
"of the tca, tcert and/or tkey in the [sst] section" "of the tca, tcert and/or tkey in the [sst] section"
fi fi
fi fi
if [ -n "$tcert" ]; then
tcert=$(trim_string "$tcert")
if [ "${tcert%/}" != "$tcert" ]; then
tpath=1
fi
fi
} }
read_cnf() read_cnf()
......
...@@ -236,11 +236,18 @@ check_server_ssl_config() ...@@ -236,11 +236,18 @@ check_server_ssl_config()
SSLMODE=$(parse_cnf "$SST_SECTIONS" 'ssl-mode' | tr [:lower:] [:upper:]) SSLMODE=$(parse_cnf "$SST_SECTIONS" 'ssl-mode' | tr [:lower:] [:upper:])
# no old-style SSL config in [sst], check for new one: # no old-style SSL config in [sst], check for new one:
if [ -z "$SSTKEY" -a -z "$SSTCERT" -a -z "$SSTCA" ] if [ -z "$SSTKEY" -a -z "$SSTCERT" -a -z "$SSTCA" ]; then
then
check_server_ssl_config check_server_ssl_config
fi fi
SSTPATH=0
if [ -n "$SSTCA" ]; then
SSTCA=$(trim_string "$SSTCA")
if [ "${SSTCA%/}" != "$SSTCA" ]; then
SSTPATH=1
fi
fi
if [ -z "$SSLMODE" ]; then if [ -z "$SSLMODE" ]; then
# Implicit verification if CA is set and the SSL mode # Implicit verification if CA is set and the SSL mode
# is not specified by user: # is not specified by user:
...@@ -254,9 +261,19 @@ if [ -z "$SSLMODE" ]; then ...@@ -254,9 +261,19 @@ if [ -z "$SSLMODE" ]; then
fi fi
fi fi
if [ -n "$SSTCA" ] if [ -n "$SSTCERT" -a -n "$SSTKEY" ]; then
then verify_cert_matches_key "$SSTCERT" "$SSTKEY"
CAFILE_OPT="CAfile = $SSTCA" fi
if [ -n "$SSTCA" ]; then
if [ $SSTPATH -eq 0 ]; then
CAFILE_OPT="CAfile = $SSTCA"
else
CAFILE_OPT="CApath = $SSTCA"
fi
if [ -n "$SSTCERT" ]; then
verify_ca_matches_cert "$SSTCA" "$SSTCERT" $SSTPATH
fi
else else
CAFILE_OPT="" CAFILE_OPT=""
fi fi
...@@ -272,38 +289,38 @@ then ...@@ -272,38 +289,38 @@ then
;; ;;
'VERIFY_CA') 'VERIFY_CA')
VERIFY_OPT='verifyChain = yes' VERIFY_OPT='verifyChain = yes'
if [ -n "$WSREP_SST_OPT_REMOTE_USER" ]; then
CHECK_OPT="checkHost = $WSREP_SST_OPT_REMOTE_USER"
else
# check if the address is an ip-address (v4 or v6):
if echo "$WSREP_SST_OPT_HOST_UNESCAPED" | \
grep -q -E '^([0-9]+(\.[0-9]+){3}|[0-9a-fA-F]*(\:[0-9a-fA-F]*)+)$'
then
CHECK_OPT="checkIP = $WSREP_SST_OPT_HOST_UNESCAPED"
else
CHECK_OPT="checkHost = $WSREP_SST_OPT_HOST"
fi
if is_local_ip "$WSREP_SST_OPT_HOST_UNESCAPED"; then
CHECK_OPT_LOCAL="checkHost = localhost"
fi
fi
;; ;;
*) *)
wsrep_log_error "Unrecognized ssl-mode option: '$SSLMODE'" wsrep_log_error "Unrecognized ssl-mode option: '$SSLMODE'"
exit 22 # EINVAL exit 22 # EINVAL
;; ;;
esac esac
if [ -z "$CAFILE_OPT" ]; then if [ -z "$SSTCA" ]; then
wsrep_log_error "Can't have ssl-mode='$SSLMODE' without CA file" wsrep_log_error "Can't have ssl-mode='$SSLMODE' without CA file or path"
exit 22 # EINVAL exit 22 # EINVAL
fi fi
if [ -n "$WSREP_SST_OPT_REMOTE_USER" ]; then
CHECK_OPT="checkHost = $WSREP_SST_OPT_REMOTE_USER"
elif [ "$WSREP_SST_OPT_ROLE" = 'donor' ]; then
# check if the address is an ip-address (v4 or v6):
if echo "$WSREP_SST_OPT_HOST_UNESCAPED" | \
grep -q -E '^([0-9]+(\.[0-9]+){3}|[0-9a-fA-F]*(\:[0-9a-fA-F]*)+)$'
then
CHECK_OPT="checkIP = $WSREP_SST_OPT_HOST_UNESCAPED"
else
CHECK_OPT="checkHost = $WSREP_SST_OPT_HOST"
fi
if is_local_ip "$WSREP_SST_OPT_HOST_UNESCAPED"; then
CHECK_OPT_LOCAL="checkHost = localhost"
fi
fi
fi fi
STUNNEL="" STUNNEL=""
if [ -n "$SSLMODE" -a "$SSLMODE" != 'DISABLED' ]; then if [ -n "$SSLMODE" -a "$SSLMODE" != 'DISABLED' ]; then
STUNNEL_BIN="$(command -v stunnel)" STUNNEL_BIN="$(command -v stunnel)"
if [ -n "$STUNNEL_BIN" ]; then if [ -n "$STUNNEL_BIN" ]; then
wsrep_log_info "Using stunnel for SSL encryption: CAfile: '$SSTCA', ssl-mode='$SSLMODE'" wsrep_log_info "Using stunnel for SSL encryption: CA: '$SSTCA', ssl-mode='$SSLMODE'"
STUNNEL="$STUNNEL_BIN $STUNNEL_CONF" STUNNEL="$STUNNEL_BIN $STUNNEL_CONF"
fi fi
fi fi
......
...@@ -34,6 +34,7 @@ ssyslog="" ...@@ -34,6 +34,7 @@ ssyslog=""
ssystag="" ssystag=""
BACKUP_PID="" BACKUP_PID=""
tcert="" tcert=""
tpath=0
tpem="" tpem=""
tkey="" tkey=""
tmode="DISABLED" tmode="DISABLED"
...@@ -201,9 +202,10 @@ get_keys() ...@@ -201,9 +202,10 @@ get_keys()
if [ -z "$ekey" ]; then if [ -z "$ekey" ]; then
ecmd="xbcrypt --encrypt-algo='$ealgo' --encrypt-key-file='$ekeyfile'" ecmd="xbcrypt --encrypt-algo='$ealgo' --encrypt-key-file='$ekeyfile'"
else else
wsrep_log_warning "Using the 'encrypt-key' option causes the encryption key" wsrep_log_warning \
wsrep_log_warning "to be set via the command-line and is considered insecure." "Using the 'encrypt-key' option causes the encryption key " \
wsrep_log_warning "It is recommended to use the 'encrypt-key-file' option instead." "to be set via the command-line and is considered insecure. " \
"It is recommended to use the 'encrypt-key-file' option instead."
ecmd="xbcrypt --encrypt-algo='$ealgo' --encrypt-key='$ekey'" ecmd="xbcrypt --encrypt-algo='$ealgo' --encrypt-key='$ekey'"
fi fi
if [ -n "$encrypt_threads" ]; then if [ -n "$encrypt_threads" ]; then
...@@ -341,64 +343,83 @@ get_transfer() ...@@ -341,64 +343,83 @@ get_transfer()
fi fi
fi fi
CN_option=",commonname=''"
if [ $encrypt -eq 2 ]; then if [ $encrypt -eq 2 ]; then
wsrep_log_info "Using openssl based encryption with socat: with crt and pem" wsrep_log_info "Using openssl based encryption with socat: with crt and pem"
if [ -z "$tpem" -o -z "$tcert" ]; then if [ -z "$tpem" -o -z "$tcert" ]; then
wsrep_log_error "Both PEM and CRT files required" wsrep_log_error \
"Both PEM file and CRT file (or path) are required"
exit 22 exit 22
fi fi
if [ ! -r "$tpem" -o ! -r "$tcert" ]; then if [ ! -r "$tpem" -o ! -r "$tcert" ]; then
wsrep_log_error "Both PEM and CRT files must be readable" wsrep_log_error \
"Both PEM file and CRT file (or path) must be readable"
exit 22 exit 22
fi fi
verify_ca_matches_cert "$tcert" "$tpem" verify_ca_matches_cert "$tcert" "$tpem" $tpath
tcmd="$tcmd,cert='$tpem',cafile='$tcert'$sockopt" if [ $tpath -eq 0 ]; then
tcmd="$tcmd,cert='$tpem',cafile='$tcert'"
else
tcmd="$tcmd,cert='$tpem',capath='$tcert'"
fi
stagemsg="$stagemsg-OpenSSL-Encrypted-2" stagemsg="$stagemsg-OpenSSL-Encrypted-2"
wsrep_log_info "$action with cert=$tpem, cafile=$tcert" wsrep_log_info "$action with cert=$tpem, ca=$tcert"
elif [ $encrypt -eq 3 -o $encrypt -eq 4 ]; then elif [ $encrypt -eq 3 -o $encrypt -eq 4 ]; then
wsrep_log_info "Using openssl based encryption with socat: with key and crt" wsrep_log_info "Using openssl based encryption with socat: with key and crt"
if [ -z "$tpem" -o -z "$tkey" ]; then if [ -z "$tpem" -o -z "$tkey" ]; then
wsrep_log_error "Both certificate and key files required" wsrep_log_error "Both certificate file (or path) " \
"and key file are required"
exit 22 exit 22
fi fi
if [ ! -r "$tpem" -o ! -r "$tkey" ]; then if [ ! -r "$tpem" -o ! -r "$tkey" ]; then
wsrep_log_error "Both certificate and key files must be readable" wsrep_log_error "Both certificate file (or path) " \
"and key file must be readable"
exit 22 exit 22
fi fi
verify_cert_matches_key "$tpem" "$tkey" verify_cert_matches_key "$tpem" "$tkey"
stagemsg="$stagemsg-OpenSSL-Encrypted-3" stagemsg="$stagemsg-OpenSSL-Encrypted-3"
if [ -z "$tcert" ]; then if [ -z "$tcert" ]; then
if [ $encrypt -eq 4 ]; then if [ $encrypt -eq 4 ]; then
wsrep_log_error "Peer certificate required if encrypt=4" wsrep_log_error \
"Peer certificate file (or path) required if encrypt=4"
exit 22 exit 22
fi fi
# no verification # no verification
tcmd="$tcmd,cert='$tpem',key='$tkey',verify=0$sockopt" CN_option=""
tcmd="$tcmd,cert='$tpem',key='$tkey',verify=0"
wsrep_log_info "$action with cert=$tpem, key=$tkey, verify=0" wsrep_log_info "$action with cert=$tpem, key=$tkey, verify=0"
else else
# CA verification # CA verification
if [ ! -r "$tcert" ]; then if [ ! -r "$tcert" ]; then
wsrep_log_error "Certificate file must be readable" wsrep_log_error "Certificate file or path must be readable"
exit 22 exit 22
fi fi
verify_ca_matches_cert "$tcert" "$tpem" verify_ca_matches_cert "$tcert" "$tpem" $tpath
if [ -n "$WSREP_SST_OPT_REMOTE_USER" ]; then if [ -n "$WSREP_SST_OPT_REMOTE_USER" ]; then
CN_option=",commonname='$WSREP_SST_OPT_REMOTE_USER'" CN_option=",commonname='$WSREP_SST_OPT_REMOTE_USER'"
elif [ $encrypt -eq 4 ]; then elif [ "$WSREP_SST_OPT_ROLE" = 'joiner' -o $encrypt -eq 4 ]
then
CN_option=",commonname=''" CN_option=",commonname=''"
elif is_local_ip "$WSREP_SST_OPT_HOST_UNESCAPED"; then elif is_local_ip "$WSREP_SST_OPT_HOST_UNESCAPED"; then
CN_option=',commonname=localhost' CN_option=',commonname=localhost'
else else
CN_option=",commonname='$WSREP_SST_OPT_HOST_UNESCAPED'" CN_option=",commonname='$WSREP_SST_OPT_HOST_UNESCAPED'"
fi fi
tcmd="$tcmd,cert='$tpem',key='$tkey',cafile='$tcert'$CN_option$sockopt" if [ $tpath -eq 0 ]; then
wsrep_log_info "$action with cert=$tpem, key=$tkey, cafile=$tcert" tcmd="$tcmd,cert='$tpem',key='$tkey',cafile='$tcert'"
else
tcmd="$tcmd,cert='$tpem',key='$tkey',capath='$tcert'"
fi
wsrep_log_info "$action with cert=$tpem, key=$tkey, ca=$tcert"
fi fi
else else
wsrep_log_info "Unknown encryption mode: encrypt=$encrypt" wsrep_log_info "Unknown encryption mode: encrypt=$encrypt"
exit 22 exit 22
fi fi
tcmd="$tcmd$CN_option$sockopt"
if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
tcmd="$tcmd stdio" tcmd="$tcmd stdio"
fi fi
...@@ -475,6 +496,12 @@ check_server_ssl_config() ...@@ -475,6 +496,12 @@ check_server_ssl_config()
"of the tca, tcert and/or tkey in the [sst] section" "of the tca, tcert and/or tkey in the [sst] section"
fi fi
fi fi
if [ -n "$tcert" ]; then
tcert=$(trim_string "$tcert")
if [ "${tcert%/}" != "$tcert" ]; then
tpath=1
fi
fi
} }
read_cnf() read_cnf()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment