#!/usr/bin/env perl1#***************************************************************************2# _ _ ____ _3# Project ___| | | | _ \| |4# / __| | | | |_) | |5# | (__| |_| | _ <| |___6# \___|\___/|_| \_\_____|7#8# Copyright (C) Daniel Stenberg, <[email protected]>, et al.9#10# This software is licensed as described in the file COPYING, which11# you should have received as part of this distribution. The terms12# are also available at https://curl.se/docs/copyright.html.13#14# You may opt to use, copy, modify, merge, publish, distribute and/or sell15# copies of the Software, and permit persons to whom the Software is16# furnished to do so, under the terms of the COPYING file.17#18# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY19# KIND, either express or implied.20#21# SPDX-License-Identifier: curl22#23#***************************************************************************2425# Starts sshd for use in the SCP and SFTP curl test harness tests.26# Also creates the ssh configuration files needed for these tests.2728use strict;29use warnings;30use Cwd;31use Cwd 'abs_path';32use Digest::MD5;33use Digest::MD5 'md5_hex';34use Digest::SHA;35use Digest::SHA 'sha256_base64';36use MIME::Base64;37use File::Basename;3839#***************************************************************************40# Variables and subs imported from sshhelp module41#42use sshhelp qw(43$sshdexe44$sshexe45$sftpsrvexe46$sftpexe47$sshkeygenexe48$sshdconfig49$sshconfig50$sftpconfig51$knownhosts52$sshdlog53$sshlog54$sftplog55$sftpcmds56$hstprvkeyf57$hstpubkeyf58$hstpubmd5f59$hstpubsha256f60$cliprvkeyf61$clipubkeyf62display_file_top63display_sshdconfig64display_sshconfig65display_sftpconfig66display_sshdlog67display_sshlog68display_sftplog69dump_array70find_sshd71find_ssh72find_sftpsrv73find_sftp74find_sshkeygen75sshversioninfo76);7778#***************************************************************************79# Subs imported from serverhelp module80#81use serverhelp qw(82$logfile83server_pidfilename84server_logfilename85);8687use pathhelp;8889#***************************************************************************9091my $verbose = 0; # set to 1 for debugging92my $debugprotocol = 0; # set to 1 for protocol debugging93my $port = 8999; # our default SCP/SFTP server port94my $listenaddr = '127.0.0.1'; # default address on which to listen95my $ipvnum = 4; # default IP version of listener address96my $idnum = 1; # default ssh daemon instance number97my $proto = 'ssh'; # protocol the ssh daemon speaks98my $path = getcwd(); # current working directory99my $logdir = $path .'/log'; # directory for log files100my $piddir; # directory for server config files101my $username = $ENV{USER}; # default user102my $pidfile; # ssh daemon pid file103my $identity = 'curl_client_key'; # default identity file104105my $error;106my @cfgarr;107108#***************************************************************************109# Returns a path of the given file name in the log directory (PiddirPath)110#111sub pp {112my $file = $_[0];113return "$piddir/$file";114# TODO: do Windows path conversion here115}116117#***************************************************************************118# Save the message to the log and print it119sub logmsg {120my $msg = $_[0];121serverhelp::logmsg $msg;122print $msg;123}124125#***************************************************************************126# Parse command line options127#128while(@ARGV) {129if($ARGV[0] eq '--verbose') {130$verbose = 1;131}132elsif($ARGV[0] eq '--debugprotocol') {133$verbose = 1;134$debugprotocol = 1;135}136elsif($ARGV[0] eq '--user') {137if($ARGV[1]) {138$username = $ARGV[1];139shift @ARGV;140}141}142elsif($ARGV[0] eq '--id') {143if($ARGV[1]) {144if($ARGV[1] =~ /^(\d+)$/) {145$idnum = $1 if($1 > 0);146shift @ARGV;147}148}149}150elsif($ARGV[0] eq '--ipv4') {151$ipvnum = 4;152$listenaddr = '127.0.0.1' if($listenaddr eq '::1');153}154elsif($ARGV[0] eq '--ipv6') {155$ipvnum = 6;156$listenaddr = '::1' if($listenaddr eq '127.0.0.1');157}158elsif($ARGV[0] eq '--addr') {159if($ARGV[1]) {160my $tmpstr = $ARGV[1];161if($tmpstr =~ /^(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)$/) {162$listenaddr = "$1.$2.$3.$4" if($ipvnum == 4);163shift @ARGV;164}165elsif($ipvnum == 6) {166$listenaddr = $tmpstr;167$listenaddr =~ s/^\[(.*)\]$/$1/;168shift @ARGV;169}170}171}172elsif($ARGV[0] eq '--pidfile') {173if($ARGV[1]) {174$pidfile = "$path/". $ARGV[1];175shift @ARGV;176}177}178elsif($ARGV[0] eq '--logdir') {179if($ARGV[1]) {180$logdir = "$path/". $ARGV[1];181shift @ARGV;182}183}184elsif($ARGV[0] eq '--sshport') {185if($ARGV[1]) {186if($ARGV[1] =~ /^(\d+)$/) {187$port = $1;188shift @ARGV;189}190}191}192else {193print STDERR "\nWarning: sshserver.pl unknown parameter: $ARGV[0]\n";194}195shift @ARGV;196}197198#***************************************************************************199# Initialize command line option dependent variables200#201202#***************************************************************************203# Default ssh daemon pid file name & directory204#205if($pidfile) {206# Use our pidfile directory to store server config files207$piddir = dirname($pidfile);208}209else {210# Use the current directory to store server config files211$piddir = $path;212$pidfile = server_pidfilename($piddir, $proto, $ipvnum, $idnum);213}214215#***************************************************************************216# ssh and sftp server log file names217#218$sshdlog = server_logfilename($logdir, 'ssh', $ipvnum, $idnum);219$sftplog = server_logfilename($logdir, 'sftp', $ipvnum, $idnum);220$logfile = "$logdir/sshserver.log"; # used by logmsg221222#***************************************************************************223# Logging level for ssh server and client224#225my $loglevel = $debugprotocol?'DEBUG3':'DEBUG2';226227228#***************************************************************************229# Validate username230#231if(!$username) {232$error = 'Will not run ssh server without a user name';233}234elsif($username eq 'root') {235$error = 'Will not run ssh server as root to mitigate security risks';236}237if($error) {238logmsg "$error\n";239exit 1;240}241242243#***************************************************************************244# Find out ssh daemon canonical file name245#246my $sshd = find_sshd();247if(!$sshd) {248logmsg "cannot find $sshdexe\n";249exit 1;250}251252253#***************************************************************************254# Find out ssh daemon version info255#256my ($sshdid, $sshdvernum, $sshdverstr, $sshderror) = sshversioninfo($sshd);257if(!$sshdid) {258# Not an OpenSSH or SunSSH ssh daemon259logmsg "$sshderror\n" if($verbose);260logmsg "SCP and SFTP tests require OpenSSH 2.9.9 or later\n";261exit 1;262}263logmsg "ssh server found $sshd is $sshdverstr\n" if($verbose);264265266#***************************************************************************267# ssh daemon command line options we might use and version support268#269# -e: log stderr : OpenSSH 2.9.0 and later270# -f: sshd config file : OpenSSH 1.2.1 and later271# -D: no daemon forking : OpenSSH 2.5.0 and later272# -o: command-line option : OpenSSH 3.1.0 and later273# -t: test config file : OpenSSH 2.9.9 and later274# -?: sshd version info : OpenSSH 1.2.1 and later275#276# -e: log stderr : SunSSH 1.0.0 and later277# -f: sshd config file : SunSSH 1.0.0 and later278# -D: no daemon forking : SunSSH 1.0.0 and later279# -o: command-line option : SunSSH 1.0.0 and later280# -t: test config file : SunSSH 1.0.0 and later281# -?: sshd version info : SunSSH 1.0.0 and later282283284#***************************************************************************285# Verify minimum ssh daemon version286#287if((($sshdid =~ /OpenSSH/) && ($sshdvernum < 299)) ||288(($sshdid =~ /SunSSH/) && ($sshdvernum < 100))) {289logmsg "SCP and SFTP tests require OpenSSH 2.9.9 or later\n";290exit 1;291}292293294#***************************************************************************295# Find out sftp server plugin canonical file name296#297my $sftpsrv = find_sftpsrv();298if(!$sftpsrv) {299logmsg "cannot find $sftpsrvexe\n";300exit 1;301}302logmsg "sftp server plugin found $sftpsrv\n" if($verbose);303304305#***************************************************************************306# Find out sftp client canonical file name307#308my $sftp = find_sftp();309if(!$sftp) {310logmsg "cannot find $sftpexe\n";311exit 1;312}313logmsg "sftp client found $sftp\n" if($verbose);314315316#***************************************************************************317# Find out ssh keygen canonical file name318#319my $sshkeygen = find_sshkeygen();320if(!$sshkeygen) {321logmsg "cannot find $sshkeygenexe\n";322exit 1;323}324logmsg "ssh keygen found $sshkeygen\n" if($verbose);325326327#***************************************************************************328# Find out ssh client canonical file name329#330my $ssh = find_ssh();331if(!$ssh) {332logmsg "cannot find $sshexe\n";333exit 1;334}335336337#***************************************************************************338# Find out ssh client version info339#340my ($sshid, $sshvernum, $sshverstr, $ssherror) = sshversioninfo($ssh);341if(!$sshid) {342# Not an OpenSSH or SunSSH ssh client343logmsg "$ssherror\n" if($verbose);344logmsg "SCP and SFTP tests require OpenSSH 2.9.9 or later\n";345exit 1;346}347logmsg "ssh client found $ssh is $sshverstr\n" if($verbose);348349350#***************************************************************************351# ssh client command line options we might use and version support352#353# -D: dynamic app port forwarding : OpenSSH 2.9.9 and later354# -F: ssh config file : OpenSSH 2.9.9 and later355# -N: no shell/command : OpenSSH 2.1.0 and later356# -p: connection port : OpenSSH 1.2.1 and later357# -v: verbose messages : OpenSSH 1.2.1 and later358# -vv: increase verbosity : OpenSSH 2.3.0 and later359# -V: ssh version info : OpenSSH 1.2.1 and later360#361# -D: dynamic app port forwarding : SunSSH 1.0.0 and later362# -F: ssh config file : SunSSH 1.0.0 and later363# -N: no shell/command : SunSSH 1.0.0 and later364# -p: connection port : SunSSH 1.0.0 and later365# -v: verbose messages : SunSSH 1.0.0 and later366# -vv: increase verbosity : SunSSH 1.0.0 and later367# -V: ssh version info : SunSSH 1.0.0 and later368369370#***************************************************************************371# Verify minimum ssh client version372#373if((($sshid =~ /OpenSSH/) && ($sshvernum < 299)) ||374(($sshid =~ /SunSSH/) && ($sshvernum < 100))) {375logmsg "SCP and SFTP tests require OpenSSH 2.9.9 or later\n";376exit 1;377}378379380#***************************************************************************381# ssh keygen command line options we actually use and version support382#383# -C: identity comment : OpenSSH 1.2.1 and later384# -f: key filename : OpenSSH 1.2.1 and later385# -N: new passphrase : OpenSSH 1.2.1 and later386# -q: quiet keygen : OpenSSH 1.2.1 and later387# -t: key type : OpenSSH 2.5.0 and later388#389# -C: identity comment : SunSSH 1.0.0 and later390# -f: key filename : SunSSH 1.0.0 and later391# -N: new passphrase : SunSSH 1.0.0 and later392# -q: quiet keygen : SunSSH 1.0.0 and later393# -t: key type : SunSSH 1.0.0 and later394395$sshdconfig = pp($sshdconfig);396$sshconfig = pp($sshconfig);397$sftpconfig = pp($sftpconfig);398399#***************************************************************************400# Generate host and client key files for curl's tests401#402if((! -e pp($hstprvkeyf)) || (! -s pp($hstprvkeyf)) ||403(! -e pp($hstpubkeyf)) || (! -s pp($hstpubkeyf)) ||404(! -e pp($hstpubmd5f)) || (! -s pp($hstpubmd5f)) ||405(! -e pp($hstpubsha256f)) || (! -s pp($hstpubsha256f)) ||406(! -e pp($cliprvkeyf)) || (! -s pp($cliprvkeyf)) ||407(! -e pp($clipubkeyf)) || (! -s pp($clipubkeyf))) {408# Make sure all files are gone so ssh-keygen doesn't complain409unlink(pp($hstprvkeyf), pp($hstpubkeyf), pp($hstpubmd5f),410pp($hstpubsha256f), pp($cliprvkeyf), pp($clipubkeyf));411412my $sshkeygenopt = '';413if(($sshid =~ /OpenSSH/) && ($sshvernum >= 560)) {414# Override the default key format. Necessary to force legacy PEM format415# for libssh2 crypto backends that do not understand the OpenSSH (RFC4716)416# format, e.g. WinCNG.417# Accepted values: RFC4716, PKCS8, PEM (see also 'man ssh-keygen')418if($ENV{'CURL_TEST_SSH_KEY_FORMAT'}) {419$sshkeygenopt .= ' -m ' . $ENV{'CURL_TEST_SSH_KEY_FORMAT'};420}421else {422$sshkeygenopt .= ' -m PEM'; # Use the most compatible RSA format for tests.423}424}425logmsg "generating host keys...\n" if($verbose);426if(system "\"$sshkeygen\" -q -t rsa -f " . pp($hstprvkeyf) . " -C 'curl test server' -N ''" . $sshkeygenopt) {427logmsg "Could not generate host key\n";428exit 1;429}430display_file_top(pp($hstprvkeyf));431logmsg "generating client keys...\n" if($verbose);432if(system "\"$sshkeygen\" -q -t rsa -f " . pp($cliprvkeyf) . " -C 'curl test client' -N ''" . $sshkeygenopt) {433logmsg "Could not generate client key\n";434exit 1;435}436display_file_top(pp($cliprvkeyf));437# Make sure that permissions are restricted so openssh doesn't complain438chmod 0600, pp($hstprvkeyf);439chmod 0600, pp($cliprvkeyf);440if(($^O eq 'cygwin' || $^O eq 'msys') && -e "/bin/setfacl") {441# https://cygwin.com/cygwin-ug-net/setfacl.html442system "/bin/setfacl --remove-all " . pp($hstprvkeyf);443}444elsif(pathhelp::os_is_win()) {445# https://ss64.com/nt/icacls.html446$ENV{'MSYS2_ARG_CONV_EXCL'} = '/reset';447system "icacls \"" . pathhelp::sys_native_abs_path(pp($hstprvkeyf)) . "\" /reset";448system "icacls \"" . pathhelp::sys_native_abs_path(pp($hstprvkeyf)) . "\" /grant:r \"$username:(R)\"";449system "icacls \"" . pathhelp::sys_native_abs_path(pp($hstprvkeyf)) . "\" /inheritance:r";450}451# Save md5 and sha256 hashes of public host key452open(my $rsakeyfile, "<", pp($hstpubkeyf));453my @rsahostkey = do { local $/ = ' '; <$rsakeyfile> };454close($rsakeyfile);455if(!$rsahostkey[1]) {456logmsg "Failed parsing base64 encoded RSA host key\n";457exit 1;458}459open(my $pubmd5file, ">", pp($hstpubmd5f));460print $pubmd5file md5_hex(decode_base64($rsahostkey[1]));461close($pubmd5file);462if((! -e pp($hstpubmd5f)) || (! -s pp($hstpubmd5f))) {463logmsg "Failed writing md5 hash of RSA host key\n";464exit 1;465}466open(my $pubsha256file, ">", pp($hstpubsha256f));467print $pubsha256file sha256_base64(decode_base64($rsahostkey[1]));468close($pubsha256file);469if((! -e pp($hstpubsha256f)) || (! -s pp($hstpubsha256f))) {470logmsg "Failed writing sha256 hash of RSA host key\n";471exit 1;472}473}474475476#***************************************************************************477# Convert paths for curl's tests running on Windows with Cygwin/MSYS OpenSSH478#479my $clipubkeyf_config;480my $hstprvkeyf_config;481my $pidfile_config;482my $sftpsrv_config;483my $sshdconfig_abs;484if ($sshdid =~ /OpenSSH-Windows/) {485# Ensure to use native Windows paths with OpenSSH for Windows486$clipubkeyf_config = pathhelp::sys_native_abs_path(pp($clipubkeyf));487$hstprvkeyf_config = pathhelp::sys_native_abs_path(pp($hstprvkeyf));488$pidfile_config = pathhelp::sys_native_abs_path($pidfile);489$sftpsrv_config = pathhelp::sys_native_abs_path($sftpsrv);490$sshdconfig_abs = pathhelp::sys_native_abs_path($sshdconfig);491}492elsif (pathhelp::os_is_win()) {493# Ensure to use MinGW/Cygwin paths494$clipubkeyf_config = pathhelp::build_sys_abs_path(pp($clipubkeyf));495$hstprvkeyf_config = pathhelp::build_sys_abs_path(pp($hstprvkeyf));496$pidfile_config = pathhelp::build_sys_abs_path($pidfile);497$sftpsrv_config = "internal-sftp";498$sshdconfig_abs = pathhelp::build_sys_abs_path($sshdconfig);499}500else {501$clipubkeyf_config = abs_path(pp($clipubkeyf));502$hstprvkeyf_config = abs_path(pp($hstprvkeyf));503$pidfile_config = $pidfile;504$sftpsrv_config = $sftpsrv;505$sshdconfig_abs = abs_path($sshdconfig);506}507508#***************************************************************************509# ssh daemon configuration file options we might use and version support510#511# AFSTokenPassing : OpenSSH 1.2.1 and later [1]512# AddressFamily : OpenSSH 4.0.0 and later513# AllowTcpForwarding : OpenSSH 2.3.0 and later514# AllowUsers : OpenSSH 1.2.1 and later515# AuthorizedKeysFile : OpenSSH 2.9.9 and later516# AuthorizedKeysFile2 : OpenSSH 2.9.9 till 5.9517# Banner : OpenSSH 2.5.0 and later518# ChallengeResponseAuthentication : OpenSSH 2.5.0 and later519# Ciphers : OpenSSH 2.1.0 and later [3]520# ClientAliveCountMax : OpenSSH 2.9.0 and later521# ClientAliveInterval : OpenSSH 2.9.0 and later522# Compression : OpenSSH 3.3.0 and later523# DenyUsers : OpenSSH 1.2.1 and later524# ForceCommand : OpenSSH 4.4.0 and later [3]525# GatewayPorts : OpenSSH 2.1.0 and later526# GSSAPIAuthentication : OpenSSH 3.7.0 and later [1]527# GSSAPICleanupCredentials : OpenSSH 3.8.0 and later [1]528# GSSAPIKeyExchange : SunSSH 1.0.0 and later [1]529# GSSAPIStoreDelegatedCredentials : SunSSH 1.0.0 and later [1]530# GSSCleanupCreds : SunSSH 1.0.0 and later [1]531# GSSUseSessionCredCache : SunSSH 1.0.0 and later [1]532# HostbasedAuthentication : OpenSSH 2.9.0 and later533# HostbasedUsesNameFromPacketOnly : OpenSSH 2.9.0 and later534# HostKey : OpenSSH 1.2.1 and later535# IgnoreRhosts : OpenSSH 1.2.1 and later536# IgnoreUserKnownHosts : OpenSSH 1.2.1 and later537# KbdInteractiveAuthentication : OpenSSH 2.3.0 and later538# KeepAlive : OpenSSH 1.2.1 and later539# KerberosAuthentication : OpenSSH 1.2.1 and later [1]540# KerberosGetAFSToken : OpenSSH 3.8.0 and later [1]541# KerberosOrLocalPasswd : OpenSSH 1.2.1 and later [1]542# KerberosTgtPassing : OpenSSH 1.2.1 and later [1]543# KerberosTicketCleanup : OpenSSH 1.2.1 and later [1]544# KeyRegenerationInterval : OpenSSH 1.2.1 till 7.3545# ListenAddress : OpenSSH 1.2.1 and later546# LoginGraceTime : OpenSSH 1.2.1 and later547# LogLevel : OpenSSH 1.2.1 and later548# LookupClientHostnames : SunSSH 1.0.0 and later549# MACs : OpenSSH 2.5.0 and later [3]550# Match : OpenSSH 4.4.0 and later [3]551# MaxAuthTries : OpenSSH 3.9.0 and later552# MaxStartups : OpenSSH 2.2.0 and later553# PAMAuthenticationViaKbdInt : OpenSSH 2.9.0 and later [2]554# PasswordAuthentication : OpenSSH 1.2.1 and later555# PermitEmptyPasswords : OpenSSH 1.2.1 and later556# PermitOpen : OpenSSH 4.4.0 and later [3]557# PermitRootLogin : OpenSSH 1.2.1 and later558# PermitTunnel : OpenSSH 4.3.0 and later559# PermitUserEnvironment : OpenSSH 3.5.0 and later560# PidFile : OpenSSH 2.1.0 and later561# Port : OpenSSH 1.2.1 and later562# PrintLastLog : OpenSSH 2.9.0 and later563# PrintMotd : OpenSSH 1.2.1 and later564# Protocol : OpenSSH 2.1.0 and later565# PubkeyAuthentication : OpenSSH 2.5.0 and later566# RhostsAuthentication : OpenSSH 1.2.1 and later567# RhostsRSAAuthentication : OpenSSH 1.2.1 till 7.3568# RSAAuthentication : OpenSSH 1.2.1 till 7.3569# ServerKeyBits : OpenSSH 1.2.1 till 7.3570# SkeyAuthentication : OpenSSH 1.2.1 and later [1]571# StrictModes : OpenSSH 1.2.1 and later572# Subsystem : OpenSSH 2.2.0 and later573# SyslogFacility : OpenSSH 1.2.1 and later574# TCPKeepAlive : OpenSSH 3.8.0 and later575# UseDNS : OpenSSH 3.7.0 and later576# UseLogin : OpenSSH 1.2.1 till 7.3577# UsePAM : OpenSSH 3.7.0 and later [1][2]578# UsePrivilegeSeparation : OpenSSH 3.2.2 and later579# VerifyReverseMapping : OpenSSH 3.1.0 and later580# X11DisplayOffset : OpenSSH 1.2.1 and later [3]581# X11Forwarding : OpenSSH 1.2.1 and later582# X11UseLocalhost : OpenSSH 3.1.0 and later583# XAuthLocation : OpenSSH 2.1.1 and later [3]584#585# [1] Option only available if activated at compile time586# [2] Option specific for portable versions587# [3] Option not used in our ssh server config file588589590#***************************************************************************591# Initialize sshd config with options actually supported in OpenSSH 2.9.9592#593logmsg "generating ssh server config file...\n" if($verbose);594@cfgarr = ();595push @cfgarr, '# This is a generated file. Do not edit.';596push @cfgarr, "# $sshdverstr sshd configuration file for curl testing";597push @cfgarr, '#';598599# AllowUsers and DenyUsers options should use lowercase on Windows600# and do not support quotes around values for some unknown reason.601if ($sshdid =~ /OpenSSH-Windows/) {602my $username_lc = lc $username;603push @cfgarr, "AllowUsers " . $username_lc =~ s/ /\?/gr;604if (exists $ENV{USERDOMAIN}) {605my $userdomain_lc = lc $ENV{USERDOMAIN};606$username_lc = "$userdomain_lc\\$username_lc";607$username_lc =~ s/ /\?/g; # replace space with ?608push @cfgarr, "AllowUsers " . $username_lc =~ s/ /\?/gr;609}610} else {611push @cfgarr, "AllowUsers $username";612}613614push @cfgarr, "AuthorizedKeysFile $clipubkeyf_config";615if(!($sshdid =~ /OpenSSH/) || ($sshdvernum <= 590)) {616push @cfgarr, "AuthorizedKeysFile2 $clipubkeyf_config";617}618push @cfgarr, "HostKey $hstprvkeyf_config";619if ($sshdid !~ /OpenSSH-Windows/) {620push @cfgarr, "PidFile $pidfile_config";621push @cfgarr, '#';622}623if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 880)) {624push @cfgarr, 'HostKeyAlgorithms +ssh-rsa';625push @cfgarr, 'PubkeyAcceptedKeyTypes +ssh-rsa';626}627push @cfgarr, '#';628push @cfgarr, "Port $port";629push @cfgarr, "ListenAddress $listenaddr";630push @cfgarr, 'Protocol 2';631push @cfgarr, '#';632push @cfgarr, 'AllowTcpForwarding yes';633push @cfgarr, 'Banner none';634push @cfgarr, 'ChallengeResponseAuthentication no';635push @cfgarr, 'ClientAliveCountMax 3';636push @cfgarr, 'ClientAliveInterval 0';637push @cfgarr, 'GatewayPorts no';638push @cfgarr, 'HostbasedAuthentication no';639push @cfgarr, 'HostbasedUsesNameFromPacketOnly no';640push @cfgarr, 'IgnoreRhosts yes';641push @cfgarr, 'IgnoreUserKnownHosts yes';642push @cfgarr, 'LoginGraceTime 30';643push @cfgarr, "LogLevel $loglevel";644push @cfgarr, 'MaxStartups 5';645push @cfgarr, 'PasswordAuthentication no';646push @cfgarr, 'PermitEmptyPasswords no';647push @cfgarr, 'PermitRootLogin no';648push @cfgarr, 'PrintLastLog no';649push @cfgarr, 'PrintMotd no';650push @cfgarr, 'PubkeyAuthentication yes';651push @cfgarr, 'StrictModes no';652push @cfgarr, "Subsystem sftp \"$sftpsrv_config\"";653push @cfgarr, 'SyslogFacility AUTH';654if(!($sshdid =~ /OpenSSH/) || ($sshdvernum <= 730)) {655push @cfgarr, 'KeyRegenerationInterval 0';656push @cfgarr, 'RhostsRSAAuthentication no';657push @cfgarr, 'RSAAuthentication no';658push @cfgarr, 'ServerKeyBits 768';659push @cfgarr, 'UseLogin no';660}661push @cfgarr, 'X11Forwarding no';662push @cfgarr, '#';663664665#***************************************************************************666# Write out initial sshd configuration file for curl's tests667#668$error = dump_array($sshdconfig, @cfgarr);669if($error) {670logmsg "$error\n";671exit 1;672}673674675#***************************************************************************676# Verifies at run time if sshd supports a given configuration file option677#678sub sshd_supports_opt {679my ($option, $value) = @_;680my $err;681#682if((($sshdid =~ /OpenSSH/) && ($sshdvernum >= 310)) ||683($sshdid =~ /SunSSH/)) {684# ssh daemon supports command line options -t -f and -o685$err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/,686`\"$sshd\" -t -f $sshdconfig_abs -o \"$option=$value\" 2>&1`;687return !$err;688}689if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 299)) {690# ssh daemon supports command line options -t and -f691$err = dump_array($sshdconfig, (@cfgarr, "$option $value"));692if($err) {693logmsg "$err\n";694return 0;695}696$err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/,697`\"$sshd\" -t -f $sshdconfig_abs 2>&1`;698unlink $sshdconfig;699return !$err;700}701return 0;702}703704705#***************************************************************************706# Kerberos Authentication support may have not been built into sshd707#708if(sshd_supports_opt('KerberosAuthentication','no')) {709push @cfgarr, 'KerberosAuthentication no';710}711if(sshd_supports_opt('KerberosGetAFSToken','no')) {712push @cfgarr, 'KerberosGetAFSToken no';713}714if(sshd_supports_opt('KerberosOrLocalPasswd','no')) {715push @cfgarr, 'KerberosOrLocalPasswd no';716}717if(sshd_supports_opt('KerberosTgtPassing','no')) {718push @cfgarr, 'KerberosTgtPassing no';719}720if(sshd_supports_opt('KerberosTicketCleanup','yes')) {721push @cfgarr, 'KerberosTicketCleanup yes';722}723724725#***************************************************************************726# Andrew File System support may have not been built into sshd727#728if(sshd_supports_opt('AFSTokenPassing','no')) {729push @cfgarr, 'AFSTokenPassing no';730}731732733#***************************************************************************734# S/Key authentication support may have not been built into sshd735#736if(sshd_supports_opt('SkeyAuthentication','no')) {737push @cfgarr, 'SkeyAuthentication no';738}739740741#***************************************************************************742# GSSAPI Authentication support may have not been built into sshd743#744my $sshd_builtwith_GSSAPI;745if(sshd_supports_opt('GSSAPIAuthentication','no')) {746push @cfgarr, 'GSSAPIAuthentication no';747$sshd_builtwith_GSSAPI = 1;748}749if(sshd_supports_opt('GSSAPICleanupCredentials','yes')) {750push @cfgarr, 'GSSAPICleanupCredentials yes';751}752if(sshd_supports_opt('GSSAPIKeyExchange','no')) {753push @cfgarr, 'GSSAPIKeyExchange no';754}755if(sshd_supports_opt('GSSAPIStoreDelegatedCredentials','no')) {756push @cfgarr, 'GSSAPIStoreDelegatedCredentials no';757}758if(sshd_supports_opt('GSSCleanupCreds','yes')) {759push @cfgarr, 'GSSCleanupCreds yes';760}761if(sshd_supports_opt('GSSUseSessionCredCache','no')) {762push @cfgarr, 'GSSUseSessionCredCache no';763}764push @cfgarr, '#';765766767#***************************************************************************768# Options that might be supported or not in sshd OpenSSH 2.9.9 and later769#770if(sshd_supports_opt('AddressFamily','any')) {771# Address family must be specified before ListenAddress772splice @cfgarr, 11, 0, 'AddressFamily any';773}774if(sshd_supports_opt('Compression','no')) {775push @cfgarr, 'Compression no';776}777if(sshd_supports_opt('KbdInteractiveAuthentication','no')) {778push @cfgarr, 'KbdInteractiveAuthentication no';779}780if(sshd_supports_opt('KeepAlive','no')) {781push @cfgarr, 'KeepAlive no';782}783if(sshd_supports_opt('LookupClientHostnames','no')) {784push @cfgarr, 'LookupClientHostnames no';785}786if(sshd_supports_opt('MaxAuthTries','10')) {787push @cfgarr, 'MaxAuthTries 10';788}789if(sshd_supports_opt('PAMAuthenticationViaKbdInt','no')) {790push @cfgarr, 'PAMAuthenticationViaKbdInt no';791}792if(sshd_supports_opt('PermitTunnel','no')) {793push @cfgarr, 'PermitTunnel no';794}795if(sshd_supports_opt('PermitUserEnvironment','no')) {796push @cfgarr, 'PermitUserEnvironment no';797}798if(sshd_supports_opt('RhostsAuthentication','no')) {799push @cfgarr, 'RhostsAuthentication no';800}801if(sshd_supports_opt('TCPKeepAlive','no')) {802push @cfgarr, 'TCPKeepAlive no';803}804if(sshd_supports_opt('UseDNS','no')) {805push @cfgarr, 'UseDNS no';806}807if(sshd_supports_opt('UsePAM','no')) {808push @cfgarr, 'UsePAM no';809}810811if($sshdid =~ /OpenSSH/) {812# http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6492415813if(sshd_supports_opt('UsePrivilegeSeparation','no')) {814push @cfgarr, 'UsePrivilegeSeparation no';815}816}817818if(sshd_supports_opt('VerifyReverseMapping','no')) {819push @cfgarr, 'VerifyReverseMapping no';820}821if(sshd_supports_opt('X11UseLocalhost','yes')) {822push @cfgarr, 'X11UseLocalhost yes';823}824push @cfgarr, '#';825826827#***************************************************************************828# Write out resulting sshd configuration file for curl's tests829#830$error = dump_array($sshdconfig, @cfgarr);831if($error) {832logmsg "$error\n";833exit 1;834}835836837#***************************************************************************838# Verify that sshd actually supports our generated configuration file839#840if(system "\"$sshd\" -t -f $sshdconfig_abs > $sshdlog 2>&1") {841logmsg "sshd configuration file $sshdconfig failed verification\n";842display_sshdlog();843display_sshdconfig();844exit 1;845}846847848#***************************************************************************849# Generate ssh client host key database file for curl's tests850#851if((! -e pp($knownhosts)) || (! -s pp($knownhosts))) {852logmsg "generating ssh client known hosts file...\n" if($verbose);853unlink(pp($knownhosts));854if(open(my $rsakeyfile, "<", pp($hstpubkeyf))) {855my @rsahostkey = do { local $/ = ' '; <$rsakeyfile> };856if(close($rsakeyfile)) {857if(open(my $knownhostsh, ">", pp($knownhosts))) {858print $knownhostsh "$listenaddr ssh-rsa $rsahostkey[1]\n";859if(!close($knownhostsh)) {860$error = "Error: cannot close file $knownhosts";861}862}863else {864$error = "Error: cannot write file $knownhosts";865}866}867else {868$error = "Error: cannot close file $hstpubkeyf";869}870}871else {872$error = "Error: cannot read file $hstpubkeyf";873}874if($error) {875logmsg "$error\n";876exit 1;877}878}879880881#***************************************************************************882# Convert paths for curl's tests running on Windows using Cygwin OpenSSH883#884my $identity_config;885my $knownhosts_config;886if ($sshdid =~ /OpenSSH-Windows/) {887# Ensure to use native Windows paths with OpenSSH for Windows888$identity_config = pathhelp::sys_native_abs_path(pp($identity));889$knownhosts_config = pathhelp::sys_native_abs_path(pp($knownhosts));890}891elsif (pathhelp::os_is_win()) {892# Ensure to use MinGW/Cygwin paths893$identity_config = pathhelp::build_sys_abs_path(pp($identity));894$knownhosts_config = pathhelp::build_sys_abs_path(pp($knownhosts));895}896else {897$identity_config = abs_path(pp($identity));898$knownhosts_config = abs_path(pp($knownhosts));899}900901902#***************************************************************************903# ssh client configuration file options we might use and version support904#905# AddressFamily : OpenSSH 3.7.0 and later906# BatchMode : OpenSSH 1.2.1 and later907# BindAddress : OpenSSH 2.9.9 and later908# ChallengeResponseAuthentication : OpenSSH 2.5.0 and later909# CheckHostIP : OpenSSH 1.2.1 and later910# Cipher : OpenSSH 1.2.1 and later [3]911# Ciphers : OpenSSH 2.1.0 and later [3]912# ClearAllForwardings : OpenSSH 2.9.9 and later913# Compression : OpenSSH 1.2.1 and later914# CompressionLevel : OpenSSH 1.2.1 and later [3]915# ConnectionAttempts : OpenSSH 1.2.1 and later916# ConnectTimeout : OpenSSH 3.7.0 and later917# ControlMaster : OpenSSH 3.9.0 and later918# ControlPath : OpenSSH 3.9.0 and later919# DisableBanner : SunSSH 1.2.0 and later920# DynamicForward : OpenSSH 2.9.0 and later921# EnableSSHKeysign : OpenSSH 3.6.0 and later922# EscapeChar : OpenSSH 1.2.1 and later [3]923# ExitOnForwardFailure : OpenSSH 4.4.0 and later924# ForwardAgent : OpenSSH 1.2.1 and later925# ForwardX11 : OpenSSH 1.2.1 and later926# ForwardX11Trusted : OpenSSH 3.8.0 and later927# GatewayPorts : OpenSSH 1.2.1 and later928# GlobalKnownHostsFile : OpenSSH 1.2.1 and later929# GSSAPIAuthentication : OpenSSH 3.7.0 and later [1]930# GSSAPIDelegateCredentials : OpenSSH 3.7.0 and later [1]931# HashKnownHosts : OpenSSH 4.0.0 and later932# Host : OpenSSH 1.2.1 and later933# HostbasedAuthentication : OpenSSH 2.9.0 and later934# HostKeyAlgorithms : OpenSSH 2.9.0 and later [3]935# HostKeyAlias : OpenSSH 2.5.0 and later [3]936# HostName : OpenSSH 1.2.1 and later937# IdentitiesOnly : OpenSSH 3.9.0 and later938# IdentityFile : OpenSSH 1.2.1 and later939# IgnoreIfUnknown : SunSSH 1.2.0 and later940# KeepAlive : OpenSSH 1.2.1 and later941# KbdInteractiveAuthentication : OpenSSH 2.3.0 and later942# KbdInteractiveDevices : OpenSSH 2.3.0 and later [3]943# LocalCommand : OpenSSH 4.3.0 and later [3]944# LocalForward : OpenSSH 1.2.1 and later [3]945# LogLevel : OpenSSH 1.2.1 and later946# MACs : OpenSSH 2.5.0 and later [3]947# NoHostAuthenticationForLocalhost : OpenSSH 3.0.0 and later948# NumberOfPasswordPrompts : OpenSSH 1.2.1 and later949# PasswordAuthentication : OpenSSH 1.2.1 and later950# PermitLocalCommand : OpenSSH 4.3.0 and later951# Port : OpenSSH 1.2.1 and later952# PreferredAuthentications : OpenSSH 2.5.2 and later953# Protocol : OpenSSH 2.1.0 and later954# ProxyCommand : OpenSSH 1.2.1 and later [3]955# PubkeyAuthentication : OpenSSH 2.5.0 and later956# RekeyLimit : OpenSSH 3.7.0 and later957# RemoteForward : OpenSSH 1.2.1 and later [3]958# RhostsRSAAuthentication : OpenSSH 1.2.1 and later959# RSAAuthentication : OpenSSH 1.2.1 and later960# ServerAliveCountMax : OpenSSH 3.8.0 and later961# ServerAliveInterval : OpenSSH 3.8.0 and later962# SmartcardDevice : OpenSSH 2.9.9 and later [1][3]963# StrictHostKeyChecking : OpenSSH 1.2.1 and later964# TCPKeepAlive : OpenSSH 3.8.0 and later965# Tunnel : OpenSSH 4.3.0 and later966# TunnelDevice : OpenSSH 4.3.0 and later [3]967# UsePAM : OpenSSH 3.7.0 and later [1][2][3]968# UsePrivilegedPort : OpenSSH 1.2.1 and later969# User : OpenSSH 1.2.1 and later970# UserKnownHostsFile : OpenSSH 1.2.1 and later971# VerifyHostKeyDNS : OpenSSH 3.8.0 and later972# XAuthLocation : OpenSSH 2.1.1 and later [3]973#974# [1] Option only available if activated at compile time975# [2] Option specific for portable versions976# [3] Option not used in our ssh client config file977978979#***************************************************************************980# Initialize ssh config with options actually supported in OpenSSH 2.9.9981#982logmsg "generating ssh client config file...\n" if($verbose);983@cfgarr = ();984push @cfgarr, '# This is a generated file. Do not edit.';985push @cfgarr, "# $sshverstr ssh client configuration file for curl testing";986push @cfgarr, '#';987push @cfgarr, 'Host *';988push @cfgarr, '#';989push @cfgarr, "Port $port";990push @cfgarr, "HostName $listenaddr";991push @cfgarr, "User $username";992push @cfgarr, 'Protocol 2';993push @cfgarr, '#';994995# BindAddress option is not supported by OpenSSH for Windows996if (!($sshdid =~ /OpenSSH-Windows/)) {997push @cfgarr, "BindAddress $listenaddr";998}9991000push @cfgarr, '#';1001push @cfgarr, "IdentityFile $identity_config";1002push @cfgarr, "UserKnownHostsFile $knownhosts_config";1003push @cfgarr, '#';1004push @cfgarr, 'BatchMode yes';1005push @cfgarr, 'ChallengeResponseAuthentication no';1006push @cfgarr, 'CheckHostIP no';1007push @cfgarr, 'ClearAllForwardings no';1008push @cfgarr, 'Compression no';1009push @cfgarr, 'ConnectionAttempts 3';1010push @cfgarr, 'ForwardAgent no';1011push @cfgarr, 'ForwardX11 no';1012push @cfgarr, 'GatewayPorts no';1013push @cfgarr, 'GlobalKnownHostsFile /dev/null';1014push @cfgarr, 'HostbasedAuthentication no';1015push @cfgarr, 'KbdInteractiveAuthentication no';1016push @cfgarr, "LogLevel $loglevel";1017push @cfgarr, 'NumberOfPasswordPrompts 0';1018push @cfgarr, 'PasswordAuthentication no';1019push @cfgarr, 'PreferredAuthentications publickey';1020push @cfgarr, 'PubkeyAuthentication yes';10211022# RSA authentication options are deprecated by newer OpenSSH1023if (!($sshid =~ /OpenSSH/) || ($sshvernum <= 730)) {1024push @cfgarr, 'RhostsRSAAuthentication no';1025push @cfgarr, 'RSAAuthentication no';1026}10271028# Disabled StrictHostKeyChecking since it makes the tests fail on my1029# OpenSSH_6.0p1 on Debian Linux / Daniel1030push @cfgarr, 'StrictHostKeyChecking no';1031push @cfgarr, 'UsePrivilegedPort no';1032push @cfgarr, '#';103310341035#***************************************************************************1036# Options supported in ssh client newer than OpenSSH 2.9.91037#10381039if(($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) {1040push @cfgarr, 'AddressFamily any';1041}10421043if((($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) ||1044(($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {1045push @cfgarr, 'ConnectTimeout 30';1046}10471048if(($sshid =~ /OpenSSH/) && ($sshvernum >= 390)) {1049push @cfgarr, 'ControlMaster no';1050}10511052if(($sshid =~ /OpenSSH/) && ($sshvernum >= 420)) {1053push @cfgarr, 'ControlPath none';1054}10551056if(($sshid =~ /SunSSH/) && ($sshvernum >= 120)) {1057push @cfgarr, 'DisableBanner yes';1058}10591060if(($sshid =~ /OpenSSH/) && ($sshvernum >= 360)) {1061push @cfgarr, 'EnableSSHKeysign no';1062}10631064if(($sshid =~ /OpenSSH/) && ($sshvernum >= 440)) {1065push @cfgarr, 'ExitOnForwardFailure yes';1066}10671068if((($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) ||1069(($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {1070push @cfgarr, 'ForwardX11Trusted no';1071}10721073if(($sshd_builtwith_GSSAPI) && ($sshdid eq $sshid) &&1074($sshdvernum == $sshvernum)) {1075push @cfgarr, 'GSSAPIAuthentication no';1076push @cfgarr, 'GSSAPIDelegateCredentials no';1077if($sshid =~ /SunSSH/) {1078push @cfgarr, 'GSSAPIKeyExchange no';1079}1080}10811082if((($sshid =~ /OpenSSH/) && ($sshvernum >= 400)) ||1083(($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {1084push @cfgarr, 'HashKnownHosts no';1085}10861087if(($sshid =~ /OpenSSH/) && ($sshvernum >= 390)) {1088push @cfgarr, 'IdentitiesOnly yes';1089}10901091if(($sshid =~ /SunSSH/) && ($sshvernum >= 120)) {1092push @cfgarr, 'IgnoreIfUnknown no';1093}10941095if((($sshid =~ /OpenSSH/) && ($sshvernum < 380)) ||1096($sshid =~ /SunSSH/)) {1097push @cfgarr, 'KeepAlive no';1098}10991100if((($sshid =~ /OpenSSH/) && ($sshvernum >= 300)) ||1101($sshid =~ /SunSSH/)) {1102push @cfgarr, 'NoHostAuthenticationForLocalhost no';1103}11041105if(($sshid =~ /OpenSSH/) && ($sshvernum >= 430)) {1106push @cfgarr, 'PermitLocalCommand no';1107}11081109if((($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) ||1110(($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {1111push @cfgarr, 'RekeyLimit 1G';1112}11131114if((($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) ||1115(($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {1116push @cfgarr, 'ServerAliveCountMax 3';1117push @cfgarr, 'ServerAliveInterval 0';1118}11191120if(($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) {1121push @cfgarr, 'TCPKeepAlive no';1122}11231124if(($sshid =~ /OpenSSH/) && ($sshvernum >= 430)) {1125push @cfgarr, 'Tunnel no';1126}11271128if(($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) {1129push @cfgarr, 'VerifyHostKeyDNS no';1130}11311132push @cfgarr, '#';113311341135#***************************************************************************1136# Write out resulting ssh client configuration file for curl's tests1137#1138$error = dump_array($sshconfig, @cfgarr);1139if($error) {1140logmsg "$error\n";1141exit 1;1142}114311441145#***************************************************************************1146# Initialize client sftp config with options actually supported.1147#1148logmsg "generating sftp client config file...\n" if($verbose);1149splice @cfgarr, 1, 1, "# $sshverstr sftp client configuration file for curl testing";1150#1151for(my $i = scalar(@cfgarr) - 1; $i > 0; $i--) {1152if($cfgarr[$i] =~ /^DynamicForward/) {1153splice @cfgarr, $i, 1;1154next;1155}1156if($cfgarr[$i] =~ /^ClearAllForwardings/) {1157splice @cfgarr, $i, 1, "ClearAllForwardings yes";1158next;1159}1160}116111621163#***************************************************************************1164# Write out resulting sftp client configuration file for curl's tests1165#1166$error = dump_array($sftpconfig, @cfgarr);1167if($error) {1168logmsg "$error\n";1169exit 1;1170}1171@cfgarr = ();117211731174#***************************************************************************1175# Generate client sftp commands batch file for sftp server verification1176#1177logmsg "generating sftp client commands file...\n" if($verbose);1178push @cfgarr, 'pwd';1179push @cfgarr, 'quit';1180$error = dump_array(pp($sftpcmds), @cfgarr);1181if($error) {1182logmsg "$error\n";1183exit 1;1184}1185@cfgarr = ();11861187#***************************************************************************1188# Prepare command line of ssh server daemon1189#1190my $cmd = "\"$sshd\" -e -D -f $sshdconfig_abs > $sshdlog 2>&1";1191logmsg "SCP/SFTP server listening on port $port\n" if($verbose);1192logmsg "RUN: $cmd\n" if($verbose);11931194#***************************************************************************1195# Start the ssh server daemon on Windows without forking it1196#1197if ($sshdid =~ /OpenSSH-Windows/) {1198# Fake pidfile for ssh server on Windows.1199if(open(my $out, ">", "$pidfile")) {1200print $out $$ . "\n";1201close($out);1202}12031204# Flush output.1205$| = 1;12061207# Put an "exec" in front of the command so that the child process1208# keeps this child's process ID by being tied to the spawned shell.1209exec("exec $cmd") || die "Can't exec() $cmd: $!";1210# exec() will create a new process, but ties the existence of the1211# new process to the parent waiting perl.exe and sh.exe processes.12121213# exec() should never return back here to this process. We protect1214# ourselves by calling die() just in case something goes really bad.1215die "error: exec() has returned";1216}12171218#***************************************************************************1219# Start the ssh server daemon without forking it1220#1221# "exec" avoids the shell process sticking around1222my $rc = system("exec " . $cmd);1223if($rc == -1) {1224logmsg "\"$sshd\" failed with: $!\n";1225}1226elsif($rc & 127) {1227logmsg sprintf("\"$sshd\" died with signal %d, and %s coredump\n",1228($rc & 127), ($rc & 128)?'a':'no');1229}1230elsif($verbose && ($rc >> 8)) {1231logmsg sprintf("\"$sshd\" exited with %d\n", $rc >> 8);1232}123312341235#***************************************************************************1236# Clean up once the server has stopped1237#1238unlink(pp($hstprvkeyf), pp($hstpubkeyf), pp($hstpubmd5f), pp($hstpubsha256f),1239pp($cliprvkeyf), pp($clipubkeyf), pp($knownhosts),1240$sshdconfig, $sshconfig, $sftpconfig);12411242exit 0;124312441245