From acda4eae5eeb24a7b0ab9ec7b1783d74eb43687c Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Thu, 2 Apr 2026 01:05:54 +0200 Subject: [PATCH] runtests: allow configuring SSH host/client key algorithm via env `CURL_TEST_SSH_KEYALGO`, `rsa` (default), `ecdsa`, `ed25519`. To ease debugging and testing and to make these code paths more universal. Closes #21223 --- .github/workflows/windows.yml | 1 + docs/tests/FILEFORMAT.md | 1 + tests/data/test1459 | 2 +- tests/runtests.pl | 4 ++++ tests/servers.pm | 16 ++++++++++++++++ tests/sshhelp.pm | 8 ++++---- tests/sshserver.pl | 25 +++++++++++++++++-------- 7 files changed, 44 insertions(+), 13 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index ebc3193252..ff65607850 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -34,6 +34,7 @@ permissions: {} env: CURL_CI: github CURL_TEST_MIN: 1700 + CURL_TEST_SSH_KEYALGO: ed25519 OPENSSH_WINDOWS_VERSION: 10.0.0.0p2-Preview OPENSSH_WINDOWS_SHA256_ARM64: 698c6aec31c1dd0fb996206e8741f4531a97355686b5431ef347d531b07fcd42 OPENSSH_WINDOWS_SHA256_WIN64: 23f50f3458c4c5d0b12217c6a5ddfde0137210a30fa870e98b29827f7b43aba5 diff --git a/docs/tests/FILEFORMAT.md b/docs/tests/FILEFORMAT.md index 20718a9618..bce3e440f2 100644 --- a/docs/tests/FILEFORMAT.md +++ b/docs/tests/FILEFORMAT.md @@ -202,6 +202,7 @@ Available substitute variables include: - `%SRCDIR` - Full path to the source dir - `%SCP_PWD` - Current directory friendly for the SSH server for the scp:// protocol - `%SFTP_PWD` - Current directory friendly for the SSH server for the sftp:// protocol +- `%SSHKEYALGO` - SSH host and client key algorithm, e.g. `ssh-rsa` or `ssh-ed25519` - `%SSHPORT` - Port number of the SCP/SFTP server - `%SSHSRVMD5` - MD5 of SSH server's public key - `%SSHSRVSHA256` - SHA256 of SSH server's public key diff --git a/tests/data/test1459 b/tests/data/test1459 index 9285177c9a..ee7e3e5ffe 100644 --- a/tests/data/test1459 +++ b/tests/data/test1459 @@ -23,7 +23,7 @@ SFTP with corrupted known_hosts -u : sftp://%HOSTIP:%SSHPORT/ -l --knownhosts %LOGDIR/known%TESTNUMBER -|1|qy29Y1x/+/F39AzdG5515YSSw+c=|iB2WX5jrU3ZTWc+ZfGau7HHEvBc= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAynDN8cDJ3xNzRjTNNGciSHSxpubxhZ6YnkLdp1TkrGW8n\ +|1|qy29Y1x/+/F39AzdG5515YSSw+c=|iB2WX5jrU3ZTWc+ZfGau7HHEvBc= %SSHKEYALGO AAAAB3NzaC1yc2EAAAABIwAAAIEAynDN8cDJ3xNzRjTNNGciSHSxpubxhZ6YnkLdp1TkrGW8n\ R93Ey5VtBeBblYTRlFXBWJgKFcTKBRJ/O4qBZwbUgt10AHj31i6h8NehfT19tR8wG/YCmj3KtYLHmwdzmW1edEL9G2NdX2KiKYv7/zuly3QvmP0QA0NhWkAz0KdWNM= diff --git a/tests/runtests.pl b/tests/runtests.pl index 8a3a035d92..e9184fd47a 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -627,9 +627,13 @@ sub checksystemfeatures { } if($libcurl =~ /libssh2/i) { $feature{"libssh2"} = 1; + $feature{"sshkeyalgo"} = ($ENV{'CURL_TEST_SSH_KEYALGO'} and + $ENV{'CURL_TEST_SSH_KEYALGO'} =~ /^(?:rsa|ecdsa|ed25519)$/) ? $ENV{'CURL_TEST_SSH_KEYALGO'} : 'rsa'; } if($libcurl =~ /libssh\/([0-9.]*)\//i) { $feature{"libssh"} = 1; + $feature{"sshkeyalgo"} = ($ENV{'CURL_TEST_SSH_KEYALGO'} and + $ENV{'CURL_TEST_SSH_KEYALGO'} =~ /^(?:rsa|ecdsa|ed25519)$/) ? $ENV{'CURL_TEST_SSH_KEYALGO'} : 'rsa'; # Detect simple cases of default libssh configuration files ending up # setting `StrictHostKeyChecking no`. include files, quoted values, # '=value' format not implemented. diff --git a/tests/servers.pm b/tests/servers.pm index 8c183a5366..fca0597ec6 100644 --- a/tests/servers.pm +++ b/tests/servers.pm @@ -1784,6 +1784,17 @@ sub runrtspserver { return (0, $rtsppid, $pid2, $port); } +#*************************************************************************** +# Return key algorithm string +# +sub sshkeyalgostr { + my ($algo) = @_; + my %algomap = ( + ecdsa => 'ecdsa-sha2-nistp256', + ); + return exists $algomap{$algo} ? $algomap{$algo} : 'ssh-' . $algo; +} + ####################################################################### # Start the ssh (scp/sftp) server # @@ -1831,6 +1842,9 @@ sub runsshserver { $flags .= "--id $idnum " if($idnum > 1); $flags .= "--ipv$ipvnum --addr \"$ip\" "; $flags .= "--user \"$USER\""; + if(defined $feature{"sshkeyalgo"}) { + $flags .= ' --keyalgo ' . $feature{"sshkeyalgo"}; + } my @tports; my $port = getfreeport($ipvnum); @@ -3199,6 +3213,8 @@ sub subvariables { $$thing =~ s/${prefix}SSHSRVMD5/$SSHSRVMD5/g; $$thing =~ s/${prefix}SSHSRVSHA256/$SSHSRVSHA256/g; + my $keyalgostr = sshkeyalgostr(defined $feature{"sshkeyalgo"} ? $feature{"sshkeyalgo"} : ""); + $$thing =~ s/${prefix}SSHKEYALGO/$keyalgostr/g; # The purpose of FTPTIME2 is to provide times that can be # used for time-out tests and that would work on most hosts as these diff --git a/tests/sshhelp.pm b/tests/sshhelp.pm index a939670cde..41a2166261 100644 --- a/tests/sshhelp.pm +++ b/tests/sshhelp.pm @@ -91,10 +91,10 @@ our $sshlog = undef; # ssh client log file our $sftplog = undef; # sftp client log file our $sftpcmds = 'curl_sftp_cmds'; # sftp client commands batch file our $knownhosts = 'curl_client_knownhosts'; # ssh knownhosts file -our $hstprvkeyf = 'curl_host_rsa_key'; # host private key file -our $hstpubkeyf = 'curl_host_rsa_key.pub'; # host public key file -our $hstpubmd5f = 'curl_host_rsa_key.pub_md5'; # md5 hash of host public key -our $hstpubsha256f = 'curl_host_rsa_key.pub_sha256'; # sha256 hash of host public key +our $hstprvkeyf = 'curl_host_key'; # host private key file +our $hstpubkeyf = 'curl_host_key.pub'; # host public key file +our $hstpubmd5f = 'curl_host_key.pub_md5'; # md5 hash of host public key +our $hstpubsha256f = 'curl_host_key.pub_sha256'; # sha256 hash of host public key our $cliprvkeyf = 'curl_client_key'; # client private key file our $clipubkeyf = 'curl_client_key.pub'; # client public key file diff --git a/tests/sshserver.pl b/tests/sshserver.pl index 282c9fbf29..e425f92ff0 100755 --- a/tests/sshserver.pl +++ b/tests/sshserver.pl @@ -96,6 +96,7 @@ my $listenaddr = '127.0.0.1'; # default address on which to listen my $ipvnum = 4; # default IP version of listener address my $idnum = 1; # default ssh daemon instance number my $proto = 'ssh'; # protocol the ssh daemon speaks +my $keyalgo = 'rsa'; # key algorithm my $path = getcwd(); # current working directory my $logdir = $path .'/log'; # directory for log files my $piddir; # directory for server config files @@ -190,6 +191,12 @@ while(@ARGV) { } } } + elsif($ARGV[0] eq '--keyalgo') { + if($ARGV[1]) { + $keyalgo = $ARGV[1]; + shift @ARGV; + } + } else { print STDERR "\nWarning: sshserver.pl unknown parameter: '$ARGV[0]'\n"; } @@ -373,6 +380,7 @@ if((($sshid =~ /OpenSSH/) && ($sshvernum < 299)) || # -N: new passphrase : OpenSSH 1.2.1 and later # -q: quiet keygen : OpenSSH 1.2.1 and later # -t: key type : OpenSSH 2.5.0 and later +# -m: key format : OpenSSH 5.6.0 and later # # -C: identity comment : SunSSH 1.0.0 and later # -f: key filename : SunSSH 1.0.0 and later @@ -404,17 +412,17 @@ if((! -e pp($hstprvkeyf)) || (! -s pp($hstprvkeyf)) || # format, e.g. WinCNG. # Accepted values: RFC4716, PKCS8, PEM (see also 'man ssh-keygen') push @sshkeygenopt, '-m'; - # Default to the most compatible RSA format for tests. + # Default to the most compatible format for tests. push @sshkeygenopt, $ENV{'CURL_TEST_SSH_KEY_FORMAT'} ? $ENV{'CURL_TEST_SSH_KEY_FORMAT'} : 'PEM'; } logmsg "generating host keys...\n" if($verbose); - if(system($sshkeygen, ('-q', '-t', 'rsa', '-f', pp($hstprvkeyf), '-C', 'curl test server', '-N', '', @sshkeygenopt))) { + if(system($sshkeygen, ('-q', '-t', $keyalgo, '-f', pp($hstprvkeyf), '-C', 'curl test server', '-N', '', @sshkeygenopt))) { logmsg "Could not generate host key\n"; exit 1; } display_file_top(pp($hstprvkeyf)) if($verbose); logmsg "generating client keys...\n" if($verbose); - if(system($sshkeygen, ('-q', '-t', 'rsa', '-f', pp($cliprvkeyf), '-C', 'curl test client', '-N', '', @sshkeygenopt))) { + if(system($sshkeygen, ('-q', '-t', $keyalgo, '-f', pp($cliprvkeyf), '-C', 'curl test client', '-N', '', @sshkeygenopt))) { logmsg "Could not generate client key\n"; exit 1; } @@ -604,7 +612,7 @@ if($sshdid !~ /OpenSSH-Windows/) { push @cfgarr, "PidFile $pidfile_config"; push @cfgarr, '#'; } -if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 880)) { +if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 880) && ($keyalgo eq 'rsa')) { push @cfgarr, 'HostKeyAlgorithms +ssh-rsa'; push @cfgarr, 'PubkeyAcceptedKeyTypes +ssh-rsa'; } @@ -828,11 +836,12 @@ if(system("\"$sshd\" -t -f $sshdconfig_abs > $sshdlog 2>&1")) { if((! -e pp($knownhosts)) || (! -s pp($knownhosts))) { logmsg "generating ssh client known hosts file...\n" if($verbose); unlink(pp($knownhosts)); - if(open(my $rsakeyfile, "<", pp($hstpubkeyf))) { - my @rsahostkey = do { local $/ = ' '; <$rsakeyfile> }; - if(close($rsakeyfile)) { + if(open(my $keyfile, "<", pp($hstpubkeyf))) { + chomp(my $line = <$keyfile>); + if(close($keyfile)) { if(open(my $knownhostsh, ">", pp($knownhosts))) { - print $knownhostsh "$listenaddr ssh-rsa $rsahostkey[1]\n"; + my @hostkey = split /\s+/, $line; + print $knownhostsh "$listenaddr $hostkey[0] $hostkey[1]\n"; if(!close($knownhostsh)) { $error = "Error: cannot close file $knownhosts"; }