1#!/usr/bin/env perl 2#*************************************************************************** 3# _ _ ____ _ 4# Project ___| | | | _ \| | 5# / __| | | | |_) | | 6# | (__| |_| | _ <| |___ 7# \___|\___/|_| \_\_____| 8# 9# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 10# 11# This software is licensed as described in the file COPYING, which 12# you should have received as part of this distribution. The terms 13# are also available at https://curl.se/docs/copyright.html. 14# 15# You may opt to use, copy, modify, merge, publish, distribute and/or sell 16# copies of the Software, and permit persons to whom the Software is 17# furnished to do so, under the terms of the COPYING file. 18# 19# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 20# KIND, either express or implied. 21# 22# SPDX-License-Identifier: curl 23# 24#*************************************************************************** 25 26# Starts sshd for use in the SCP and SFTP curl test harness tests. 27# Also creates the ssh configuration files needed for these tests. 28 29use strict; 30use warnings; 31use Cwd; 32use Cwd 'abs_path'; 33use Digest::MD5; 34use Digest::MD5 'md5_hex'; 35use Digest::SHA; 36use Digest::SHA 'sha256_base64'; 37use MIME::Base64; 38 39#*************************************************************************** 40# Variables and subs imported from sshhelp module 41# 42use sshhelp qw( 43 $sshdexe 44 $sshexe 45 $sftpsrvexe 46 $sftpexe 47 $sshkeygenexe 48 $sshdconfig 49 $sshconfig 50 $sftpconfig 51 $knownhosts 52 $sshdlog 53 $sshlog 54 $sftplog 55 $sftpcmds 56 $hstprvkeyf 57 $hstpubkeyf 58 $hstpubmd5f 59 $hstpubsha256f 60 $cliprvkeyf 61 $clipubkeyf 62 display_sshdconfig 63 display_sshconfig 64 display_sftpconfig 65 display_sshdlog 66 display_sshlog 67 display_sftplog 68 dump_array 69 find_sshd 70 find_ssh 71 find_sftpsrv 72 find_sftp 73 find_sshkeygen 74 logmsg 75 sshversioninfo 76 ); 77 78#*************************************************************************** 79# Subs imported from serverhelp module 80# 81use serverhelp qw( 82 server_pidfilename 83 server_logfilename 84 ); 85 86use pathhelp; 87 88#*************************************************************************** 89 90my $verbose = 0; # set to 1 for debugging 91my $debugprotocol = 0; # set to 1 for protocol debugging 92my $port = 8999; # our default SCP/SFTP server port 93my $listenaddr = '127.0.0.1'; # default address on which to listen 94my $ipvnum = 4; # default IP version of listener address 95my $idnum = 1; # default ssh daemon instance number 96my $proto = 'ssh'; # protocol the ssh daemon speaks 97my $path = getcwd(); # current working directory 98my $logdir = $path .'/log'; # directory for log files 99my $username = $ENV{USER}; # default user 100my $pidfile; # ssh daemon pid file 101my $identity = 'curl_client_key'; # default identity file 102 103my $error; 104my @cfgarr; 105 106 107#*************************************************************************** 108# Parse command line options 109# 110while(@ARGV) { 111 if($ARGV[0] eq '--verbose') { 112 $verbose = 1; 113 } 114 elsif($ARGV[0] eq '--debugprotocol') { 115 $verbose = 1; 116 $debugprotocol = 1; 117 } 118 elsif($ARGV[0] eq '--user') { 119 if($ARGV[1]) { 120 $username = $ARGV[1]; 121 shift @ARGV; 122 } 123 } 124 elsif($ARGV[0] eq '--id') { 125 if($ARGV[1]) { 126 if($ARGV[1] =~ /^(\d+)$/) { 127 $idnum = $1 if($1 > 0); 128 shift @ARGV; 129 } 130 } 131 } 132 elsif($ARGV[0] eq '--ipv4') { 133 $ipvnum = 4; 134 $listenaddr = '127.0.0.1' if($listenaddr eq '::1'); 135 } 136 elsif($ARGV[0] eq '--ipv6') { 137 $ipvnum = 6; 138 $listenaddr = '::1' if($listenaddr eq '127.0.0.1'); 139 } 140 elsif($ARGV[0] eq '--addr') { 141 if($ARGV[1]) { 142 my $tmpstr = $ARGV[1]; 143 if($tmpstr =~ /^(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)$/) { 144 $listenaddr = "$1.$2.$3.$4" if($ipvnum == 4); 145 shift @ARGV; 146 } 147 elsif($ipvnum == 6) { 148 $listenaddr = $tmpstr; 149 $listenaddr =~ s/^\[(.*)\]$/$1/; 150 shift @ARGV; 151 } 152 } 153 } 154 elsif($ARGV[0] eq '--pidfile') { 155 if($ARGV[1]) { 156 $pidfile = "$path/". $ARGV[1]; 157 shift @ARGV; 158 } 159 } 160 elsif($ARGV[0] eq '--sshport') { 161 if($ARGV[1]) { 162 if($ARGV[1] =~ /^(\d+)$/) { 163 $port = $1; 164 shift @ARGV; 165 } 166 } 167 } 168 else { 169 print STDERR "\nWarning: sshserver.pl unknown parameter: $ARGV[0]\n"; 170 } 171 shift @ARGV; 172} 173 174 175#*************************************************************************** 176# Default ssh daemon pid file name 177# 178if(!$pidfile) { 179 $pidfile = "$path/". server_pidfilename($proto, $ipvnum, $idnum); 180} 181 182 183#*************************************************************************** 184# ssh and sftp server log file names 185# 186$sshdlog = server_logfilename($logdir, 'ssh', $ipvnum, $idnum); 187$sftplog = server_logfilename($logdir, 'sftp', $ipvnum, $idnum); 188 189 190#*************************************************************************** 191# Logging level for ssh server and client 192# 193my $loglevel = $debugprotocol?'DEBUG3':'DEBUG2'; 194 195 196#*************************************************************************** 197# Validate username 198# 199if(!$username) { 200 $error = 'Will not run ssh server without a user name'; 201} 202elsif($username eq 'root') { 203 $error = 'Will not run ssh server as root to mitigate security risks'; 204} 205if($error) { 206 logmsg $error; 207 exit 1; 208} 209 210 211#*************************************************************************** 212# Find out ssh daemon canonical file name 213# 214my $sshd = find_sshd(); 215if(!$sshd) { 216 logmsg "cannot find $sshdexe"; 217 exit 1; 218} 219 220 221#*************************************************************************** 222# Find out ssh daemon version info 223# 224my ($sshdid, $sshdvernum, $sshdverstr, $sshderror) = sshversioninfo($sshd); 225if(!$sshdid) { 226 # Not an OpenSSH or SunSSH ssh daemon 227 logmsg $sshderror if($verbose); 228 logmsg 'SCP and SFTP tests require OpenSSH 2.9.9 or later'; 229 exit 1; 230} 231logmsg "ssh server found $sshd is $sshdverstr" if($verbose); 232 233 234#*************************************************************************** 235# ssh daemon command line options we might use and version support 236# 237# -e: log stderr : OpenSSH 2.9.0 and later 238# -f: sshd config file : OpenSSH 1.2.1 and later 239# -D: no daemon forking : OpenSSH 2.5.0 and later 240# -o: command-line option : OpenSSH 3.1.0 and later 241# -t: test config file : OpenSSH 2.9.9 and later 242# -?: sshd version info : OpenSSH 1.2.1 and later 243# 244# -e: log stderr : SunSSH 1.0.0 and later 245# -f: sshd config file : SunSSH 1.0.0 and later 246# -D: no daemon forking : SunSSH 1.0.0 and later 247# -o: command-line option : SunSSH 1.0.0 and later 248# -t: test config file : SunSSH 1.0.0 and later 249# -?: sshd version info : SunSSH 1.0.0 and later 250 251 252#*************************************************************************** 253# Verify minimum ssh daemon version 254# 255if((($sshdid =~ /OpenSSH/) && ($sshdvernum < 299)) || 256 (($sshdid =~ /SunSSH/) && ($sshdvernum < 100))) { 257 logmsg 'SCP and SFTP tests require OpenSSH 2.9.9 or later'; 258 exit 1; 259} 260 261 262#*************************************************************************** 263# Find out sftp server plugin canonical file name 264# 265my $sftpsrv = find_sftpsrv(); 266if(!$sftpsrv) { 267 logmsg "cannot find $sftpsrvexe"; 268 exit 1; 269} 270logmsg "sftp server plugin found $sftpsrv" if($verbose); 271 272 273#*************************************************************************** 274# Find out sftp client canonical file name 275# 276my $sftp = find_sftp(); 277if(!$sftp) { 278 logmsg "cannot find $sftpexe"; 279 exit 1; 280} 281logmsg "sftp client found $sftp" if($verbose); 282 283 284#*************************************************************************** 285# Find out ssh keygen canonical file name 286# 287my $sshkeygen = find_sshkeygen(); 288if(!$sshkeygen) { 289 logmsg "cannot find $sshkeygenexe"; 290 exit 1; 291} 292logmsg "ssh keygen found $sshkeygen" if($verbose); 293 294 295#*************************************************************************** 296# Find out ssh client canonical file name 297# 298my $ssh = find_ssh(); 299if(!$ssh) { 300 logmsg "cannot find $sshexe"; 301 exit 1; 302} 303 304 305#*************************************************************************** 306# Find out ssh client version info 307# 308my ($sshid, $sshvernum, $sshverstr, $ssherror) = sshversioninfo($ssh); 309if(!$sshid) { 310 # Not an OpenSSH or SunSSH ssh client 311 logmsg $ssherror if($verbose); 312 logmsg 'SCP and SFTP tests require OpenSSH 2.9.9 or later'; 313 exit 1; 314} 315logmsg "ssh client found $ssh is $sshverstr" if($verbose); 316 317 318#*************************************************************************** 319# ssh client command line options we might use and version support 320# 321# -D: dynamic app port forwarding : OpenSSH 2.9.9 and later 322# -F: ssh config file : OpenSSH 2.9.9 and later 323# -N: no shell/command : OpenSSH 2.1.0 and later 324# -p: connection port : OpenSSH 1.2.1 and later 325# -v: verbose messages : OpenSSH 1.2.1 and later 326# -vv: increase verbosity : OpenSSH 2.3.0 and later 327# -V: ssh version info : OpenSSH 1.2.1 and later 328# 329# -D: dynamic app port forwarding : SunSSH 1.0.0 and later 330# -F: ssh config file : SunSSH 1.0.0 and later 331# -N: no shell/command : SunSSH 1.0.0 and later 332# -p: connection port : SunSSH 1.0.0 and later 333# -v: verbose messages : SunSSH 1.0.0 and later 334# -vv: increase verbosity : SunSSH 1.0.0 and later 335# -V: ssh version info : SunSSH 1.0.0 and later 336 337 338#*************************************************************************** 339# Verify minimum ssh client version 340# 341if((($sshid =~ /OpenSSH/) && ($sshvernum < 299)) || 342 (($sshid =~ /SunSSH/) && ($sshvernum < 100))) { 343 logmsg 'SCP and SFTP tests require OpenSSH 2.9.9 or later'; 344 exit 1; 345} 346 347 348#*************************************************************************** 349# ssh keygen command line options we actually use and version support 350# 351# -C: identity comment : OpenSSH 1.2.1 and later 352# -f: key filename : OpenSSH 1.2.1 and later 353# -N: new passphrase : OpenSSH 1.2.1 and later 354# -q: quiet keygen : OpenSSH 1.2.1 and later 355# -t: key type : OpenSSH 2.5.0 and later 356# 357# -C: identity comment : SunSSH 1.0.0 and later 358# -f: key filename : SunSSH 1.0.0 and later 359# -N: new passphrase : SunSSH 1.0.0 and later 360# -q: quiet keygen : SunSSH 1.0.0 and later 361# -t: key type : SunSSH 1.0.0 and later 362 363 364#*************************************************************************** 365# Generate host and client key files for curl's tests 366# 367if((! -e $hstprvkeyf) || (! -s $hstprvkeyf) || 368 (! -e $hstpubkeyf) || (! -s $hstpubkeyf) || 369 (! -e $hstpubmd5f) || (! -s $hstpubmd5f) || 370 (! -e $hstpubsha256f) || (! -s $hstpubsha256f) || 371 (! -e $cliprvkeyf) || (! -s $cliprvkeyf) || 372 (! -e $clipubkeyf) || (! -s $clipubkeyf)) { 373 # Make sure all files are gone so ssh-keygen doesn't complain 374 unlink($hstprvkeyf, $hstpubkeyf, $hstpubmd5f, $hstpubsha256f, 375 $cliprvkeyf, $clipubkeyf); 376 logmsg 'generating host keys...' if($verbose); 377 if(system "\"$sshkeygen\" -q -t rsa -f $hstprvkeyf -C 'curl test server' -N ''") { 378 logmsg 'Could not generate host key'; 379 exit 1; 380 } 381 logmsg 'generating client keys...' if($verbose); 382 if(system "\"$sshkeygen\" -q -t rsa -f $cliprvkeyf -C 'curl test client' -N ''") { 383 logmsg 'Could not generate client key'; 384 exit 1; 385 } 386 # Make sure that permissions are restricted so openssh doesn't complain 387 system "chmod 600 $hstprvkeyf"; 388 system "chmod 600 $cliprvkeyf"; 389 # Save md5 and sha256 hashes of public host key 390 open(RSAKEYFILE, "<$hstpubkeyf"); 391 my @rsahostkey = do { local $/ = ' '; <RSAKEYFILE> }; 392 close(RSAKEYFILE); 393 if(!$rsahostkey[1]) { 394 logmsg 'Failed parsing base64 encoded RSA host key'; 395 exit 1; 396 } 397 open(PUBMD5FILE, ">$hstpubmd5f"); 398 print PUBMD5FILE md5_hex(decode_base64($rsahostkey[1])); 399 close(PUBMD5FILE); 400 if((! -e $hstpubmd5f) || (! -s $hstpubmd5f)) { 401 logmsg 'Failed writing md5 hash of RSA host key'; 402 exit 1; 403 } 404 open(PUBSHA256FILE, ">$hstpubsha256f"); 405 print PUBSHA256FILE sha256_base64(decode_base64($rsahostkey[1])); 406 close(PUBSHA256FILE); 407 if((! -e $hstpubsha256f) || (! -s $hstpubsha256f)) { 408 logmsg 'Failed writing sha256 hash of RSA host key'; 409 exit 1; 410 } 411} 412 413 414#*************************************************************************** 415# Convert paths for curl's tests running on Windows with Cygwin/Msys OpenSSH 416# 417my $clipubkeyf_config = abs_path("$path/$clipubkeyf"); 418my $hstprvkeyf_config = abs_path("$path/$hstprvkeyf"); 419my $pidfile_config = $pidfile; 420my $sftpsrv_config = $sftpsrv; 421 422if (pathhelp::os_is_win()) { 423 # Ensure to use MinGW/Cygwin paths 424 $clipubkeyf_config = pathhelp::build_sys_abs_path($clipubkeyf_config); 425 $hstprvkeyf_config = pathhelp::build_sys_abs_path($hstprvkeyf_config); 426 $pidfile_config = pathhelp::build_sys_abs_path($pidfile_config); 427 $sftpsrv_config = "internal-sftp"; 428} 429if ($sshdid =~ /OpenSSH-Windows/) { 430 # Ensure to use native Windows paths with OpenSSH for Windows 431 $clipubkeyf_config = pathhelp::sys_native_abs_path($clipubkeyf); 432 $hstprvkeyf_config = pathhelp::sys_native_abs_path($hstprvkeyf); 433 $pidfile_config = pathhelp::sys_native_abs_path($pidfile); 434 $sftpsrv_config = pathhelp::sys_native_abs_path($sftpsrv); 435 436 $sshdconfig = pathhelp::sys_native_abs_path($sshdconfig); 437 $sshconfig = pathhelp::sys_native_abs_path($sshconfig); 438 $sftpconfig = pathhelp::sys_native_abs_path($sftpconfig); 439} 440 441#*************************************************************************** 442# ssh daemon configuration file options we might use and version support 443# 444# AFSTokenPassing : OpenSSH 1.2.1 and later [1] 445# AddressFamily : OpenSSH 4.0.0 and later 446# AllowTcpForwarding : OpenSSH 2.3.0 and later 447# AllowUsers : OpenSSH 1.2.1 and later 448# AuthorizedKeysFile : OpenSSH 2.9.9 and later 449# AuthorizedKeysFile2 : OpenSSH 2.9.9 and later 450# Banner : OpenSSH 2.5.0 and later 451# ChallengeResponseAuthentication : OpenSSH 2.5.0 and later 452# Ciphers : OpenSSH 2.1.0 and later [3] 453# ClientAliveCountMax : OpenSSH 2.9.0 and later 454# ClientAliveInterval : OpenSSH 2.9.0 and later 455# Compression : OpenSSH 3.3.0 and later 456# DenyUsers : OpenSSH 1.2.1 and later 457# ForceCommand : OpenSSH 4.4.0 and later [3] 458# GatewayPorts : OpenSSH 2.1.0 and later 459# GSSAPIAuthentication : OpenSSH 3.7.0 and later [1] 460# GSSAPICleanupCredentials : OpenSSH 3.8.0 and later [1] 461# GSSAPIKeyExchange : SunSSH 1.0.0 and later [1] 462# GSSAPIStoreDelegatedCredentials : SunSSH 1.0.0 and later [1] 463# GSSCleanupCreds : SunSSH 1.0.0 and later [1] 464# GSSUseSessionCredCache : SunSSH 1.0.0 and later [1] 465# HostbasedAuthentication : OpenSSH 2.9.0 and later 466# HostbasedUsesNameFromPacketOnly : OpenSSH 2.9.0 and later 467# HostKey : OpenSSH 1.2.1 and later 468# IgnoreRhosts : OpenSSH 1.2.1 and later 469# IgnoreUserKnownHosts : OpenSSH 1.2.1 and later 470# KbdInteractiveAuthentication : OpenSSH 2.3.0 and later 471# KeepAlive : OpenSSH 1.2.1 and later 472# KerberosAuthentication : OpenSSH 1.2.1 and later [1] 473# KerberosGetAFSToken : OpenSSH 3.8.0 and later [1] 474# KerberosOrLocalPasswd : OpenSSH 1.2.1 and later [1] 475# KerberosTgtPassing : OpenSSH 1.2.1 and later [1] 476# KerberosTicketCleanup : OpenSSH 1.2.1 and later [1] 477# KeyRegenerationInterval : OpenSSH 1.2.1 and later 478# ListenAddress : OpenSSH 1.2.1 and later 479# LoginGraceTime : OpenSSH 1.2.1 and later 480# LogLevel : OpenSSH 1.2.1 and later 481# LookupClientHostnames : SunSSH 1.0.0 and later 482# MACs : OpenSSH 2.5.0 and later [3] 483# Match : OpenSSH 4.4.0 and later [3] 484# MaxAuthTries : OpenSSH 3.9.0 and later 485# MaxStartups : OpenSSH 2.2.0 and later 486# PAMAuthenticationViaKbdInt : OpenSSH 2.9.0 and later [2] 487# PasswordAuthentication : OpenSSH 1.2.1 and later 488# PermitEmptyPasswords : OpenSSH 1.2.1 and later 489# PermitOpen : OpenSSH 4.4.0 and later [3] 490# PermitRootLogin : OpenSSH 1.2.1 and later 491# PermitTunnel : OpenSSH 4.3.0 and later 492# PermitUserEnvironment : OpenSSH 3.5.0 and later 493# PidFile : OpenSSH 2.1.0 and later 494# Port : OpenSSH 1.2.1 and later 495# PrintLastLog : OpenSSH 2.9.0 and later 496# PrintMotd : OpenSSH 1.2.1 and later 497# Protocol : OpenSSH 2.1.0 and later 498# PubkeyAuthentication : OpenSSH 2.5.0 and later 499# RhostsAuthentication : OpenSSH 1.2.1 and later 500# RhostsRSAAuthentication : OpenSSH 1.2.1 and later 501# RSAAuthentication : OpenSSH 1.2.1 and later 502# ServerKeyBits : OpenSSH 1.2.1 and later 503# SkeyAuthentication : OpenSSH 1.2.1 and later [1] 504# StrictModes : OpenSSH 1.2.1 and later 505# Subsystem : OpenSSH 2.2.0 and later 506# SyslogFacility : OpenSSH 1.2.1 and later 507# TCPKeepAlive : OpenSSH 3.8.0 and later 508# UseDNS : OpenSSH 3.7.0 and later 509# UseLogin : OpenSSH 1.2.1 and later 510# UsePAM : OpenSSH 3.7.0 and later [1][2] 511# UsePrivilegeSeparation : OpenSSH 3.2.2 and later 512# VerifyReverseMapping : OpenSSH 3.1.0 and later 513# X11DisplayOffset : OpenSSH 1.2.1 and later [3] 514# X11Forwarding : OpenSSH 1.2.1 and later 515# X11UseLocalhost : OpenSSH 3.1.0 and later 516# XAuthLocation : OpenSSH 2.1.1 and later [3] 517# 518# [1] Option only available if activated at compile time 519# [2] Option specific for portable versions 520# [3] Option not used in our ssh server config file 521 522 523#*************************************************************************** 524# Initialize sshd config with options actually supported in OpenSSH 2.9.9 525# 526logmsg 'generating ssh server config file...' if($verbose); 527@cfgarr = (); 528push @cfgarr, '# This is a generated file. Do not edit.'; 529push @cfgarr, "# $sshdverstr sshd configuration file for curl testing"; 530push @cfgarr, '#'; 531 532# AllowUsers and DenyUsers options should use lowercase on Windows 533# and do not support quotes around values for some unknown reason. 534if ($sshdid =~ /OpenSSH-Windows/) { 535 my $username_lc = lc $username; 536 if (exists $ENV{USERDOMAIN}) { 537 my $userdomain_lc = lc $ENV{USERDOMAIN}; 538 $username_lc = "$userdomain_lc\\$username_lc"; 539 } 540 $username_lc =~ s/ /\?/g; # replace space with ? 541 push @cfgarr, "DenyUsers !$username_lc"; 542 push @cfgarr, "AllowUsers $username_lc"; 543} else { 544 push @cfgarr, "DenyUsers !$username"; 545 push @cfgarr, "AllowUsers $username"; 546} 547 548push @cfgarr, "AuthorizedKeysFile $clipubkeyf_config"; 549push @cfgarr, "AuthorizedKeysFile2 $clipubkeyf_config"; 550push @cfgarr, "HostKey $hstprvkeyf_config"; 551if ($sshdid !~ /OpenSSH-Windows/) { 552 push @cfgarr, "PidFile $pidfile_config"; 553} 554if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 880)) { 555 push @cfgarr, 'HostKeyAlgorithms +ssh-rsa'; 556 push @cfgarr, 'PubkeyAcceptedKeyTypes +ssh-rsa'; 557} 558push @cfgarr, '#'; 559push @cfgarr, "Port $port"; 560push @cfgarr, "ListenAddress $listenaddr"; 561push @cfgarr, 'Protocol 2'; 562push @cfgarr, '#'; 563push @cfgarr, 'AllowTcpForwarding yes'; 564push @cfgarr, 'Banner none'; 565push @cfgarr, 'ChallengeResponseAuthentication no'; 566push @cfgarr, 'ClientAliveCountMax 3'; 567push @cfgarr, 'ClientAliveInterval 0'; 568push @cfgarr, 'GatewayPorts no'; 569push @cfgarr, 'HostbasedAuthentication no'; 570push @cfgarr, 'HostbasedUsesNameFromPacketOnly no'; 571push @cfgarr, 'IgnoreRhosts yes'; 572push @cfgarr, 'IgnoreUserKnownHosts yes'; 573push @cfgarr, 'KeyRegenerationInterval 0'; 574push @cfgarr, 'LoginGraceTime 30'; 575push @cfgarr, "LogLevel $loglevel"; 576push @cfgarr, 'MaxStartups 5'; 577push @cfgarr, 'PasswordAuthentication no'; 578push @cfgarr, 'PermitEmptyPasswords no'; 579push @cfgarr, 'PermitRootLogin no'; 580push @cfgarr, 'PrintLastLog no'; 581push @cfgarr, 'PrintMotd no'; 582push @cfgarr, 'PubkeyAuthentication yes'; 583push @cfgarr, 'RhostsRSAAuthentication no'; 584push @cfgarr, 'RSAAuthentication no'; 585push @cfgarr, 'ServerKeyBits 768'; 586push @cfgarr, 'StrictModes no'; 587push @cfgarr, "Subsystem sftp \"$sftpsrv_config\""; 588push @cfgarr, 'SyslogFacility AUTH'; 589push @cfgarr, 'UseLogin no'; 590push @cfgarr, 'X11Forwarding no'; 591push @cfgarr, '#'; 592 593 594#*************************************************************************** 595# Write out initial sshd configuration file for curl's tests 596# 597$error = dump_array($sshdconfig, @cfgarr); 598if($error) { 599 logmsg $error; 600 exit 1; 601} 602 603 604#*************************************************************************** 605# Verifies at run time if sshd supports a given configuration file option 606# 607sub sshd_supports_opt { 608 my ($option, $value) = @_; 609 my $err; 610 # 611 if((($sshdid =~ /OpenSSH/) && ($sshdvernum >= 310)) || 612 ($sshdid =~ /SunSSH/)) { 613 # ssh daemon supports command line options -t -f and -o 614 $err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/, 615 qx("$sshd" -t -f $sshdconfig -o "$option=$value" 2>&1); 616 return !$err; 617 } 618 if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 299)) { 619 # ssh daemon supports command line options -t and -f 620 $err = dump_array($sshdconfig, (@cfgarr, "$option $value")); 621 if($err) { 622 logmsg $err; 623 return 0; 624 } 625 $err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/, 626 qx("$sshd" -t -f $sshdconfig 2>&1); 627 unlink $sshdconfig; 628 return !$err; 629 } 630 return 0; 631} 632 633 634#*************************************************************************** 635# Kerberos Authentication support may have not been built into sshd 636# 637if(sshd_supports_opt('KerberosAuthentication','no')) { 638 push @cfgarr, 'KerberosAuthentication no'; 639} 640if(sshd_supports_opt('KerberosGetAFSToken','no')) { 641 push @cfgarr, 'KerberosGetAFSToken no'; 642} 643if(sshd_supports_opt('KerberosOrLocalPasswd','no')) { 644 push @cfgarr, 'KerberosOrLocalPasswd no'; 645} 646if(sshd_supports_opt('KerberosTgtPassing','no')) { 647 push @cfgarr, 'KerberosTgtPassing no'; 648} 649if(sshd_supports_opt('KerberosTicketCleanup','yes')) { 650 push @cfgarr, 'KerberosTicketCleanup yes'; 651} 652 653 654#*************************************************************************** 655# Andrew File System support may have not been built into sshd 656# 657if(sshd_supports_opt('AFSTokenPassing','no')) { 658 push @cfgarr, 'AFSTokenPassing no'; 659} 660 661 662#*************************************************************************** 663# S/Key authentication support may have not been built into sshd 664# 665if(sshd_supports_opt('SkeyAuthentication','no')) { 666 push @cfgarr, 'SkeyAuthentication no'; 667} 668 669 670#*************************************************************************** 671# GSSAPI Authentication support may have not been built into sshd 672# 673my $sshd_builtwith_GSSAPI; 674if(sshd_supports_opt('GSSAPIAuthentication','no')) { 675 push @cfgarr, 'GSSAPIAuthentication no'; 676 $sshd_builtwith_GSSAPI = 1; 677} 678if(sshd_supports_opt('GSSAPICleanupCredentials','yes')) { 679 push @cfgarr, 'GSSAPICleanupCredentials yes'; 680} 681if(sshd_supports_opt('GSSAPIKeyExchange','no')) { 682 push @cfgarr, 'GSSAPIKeyExchange no'; 683} 684if(sshd_supports_opt('GSSAPIStoreDelegatedCredentials','no')) { 685 push @cfgarr, 'GSSAPIStoreDelegatedCredentials no'; 686} 687if(sshd_supports_opt('GSSCleanupCreds','yes')) { 688 push @cfgarr, 'GSSCleanupCreds yes'; 689} 690if(sshd_supports_opt('GSSUseSessionCredCache','no')) { 691 push @cfgarr, 'GSSUseSessionCredCache no'; 692} 693push @cfgarr, '#'; 694 695 696#*************************************************************************** 697# Options that might be supported or not in sshd OpenSSH 2.9.9 and later 698# 699if(sshd_supports_opt('AddressFamily','any')) { 700 # Address family must be specified before ListenAddress 701 splice @cfgarr, 14, 0, 'AddressFamily any'; 702} 703if(sshd_supports_opt('Compression','no')) { 704 push @cfgarr, 'Compression no'; 705} 706if(sshd_supports_opt('KbdInteractiveAuthentication','no')) { 707 push @cfgarr, 'KbdInteractiveAuthentication no'; 708} 709if(sshd_supports_opt('KeepAlive','no')) { 710 push @cfgarr, 'KeepAlive no'; 711} 712if(sshd_supports_opt('LookupClientHostnames','no')) { 713 push @cfgarr, 'LookupClientHostnames no'; 714} 715if(sshd_supports_opt('MaxAuthTries','10')) { 716 push @cfgarr, 'MaxAuthTries 10'; 717} 718if(sshd_supports_opt('PAMAuthenticationViaKbdInt','no')) { 719 push @cfgarr, 'PAMAuthenticationViaKbdInt no'; 720} 721if(sshd_supports_opt('PermitTunnel','no')) { 722 push @cfgarr, 'PermitTunnel no'; 723} 724if(sshd_supports_opt('PermitUserEnvironment','no')) { 725 push @cfgarr, 'PermitUserEnvironment no'; 726} 727if(sshd_supports_opt('RhostsAuthentication','no')) { 728 push @cfgarr, 'RhostsAuthentication no'; 729} 730if(sshd_supports_opt('TCPKeepAlive','no')) { 731 push @cfgarr, 'TCPKeepAlive no'; 732} 733if(sshd_supports_opt('UseDNS','no')) { 734 push @cfgarr, 'UseDNS no'; 735} 736if(sshd_supports_opt('UsePAM','no')) { 737 push @cfgarr, 'UsePAM no'; 738} 739 740if($sshdid =~ /OpenSSH/) { 741 # http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6492415 742 if(sshd_supports_opt('UsePrivilegeSeparation','no')) { 743 push @cfgarr, 'UsePrivilegeSeparation no'; 744 } 745} 746 747if(sshd_supports_opt('VerifyReverseMapping','no')) { 748 push @cfgarr, 'VerifyReverseMapping no'; 749} 750if(sshd_supports_opt('X11UseLocalhost','yes')) { 751 push @cfgarr, 'X11UseLocalhost yes'; 752} 753push @cfgarr, '#'; 754 755 756#*************************************************************************** 757# Write out resulting sshd configuration file for curl's tests 758# 759$error = dump_array($sshdconfig, @cfgarr); 760if($error) { 761 logmsg $error; 762 exit 1; 763} 764 765 766#*************************************************************************** 767# Verify that sshd actually supports our generated configuration file 768# 769if(system "\"$sshd\" -t -f $sshdconfig > $sshdlog 2>&1") { 770 logmsg "sshd configuration file $sshdconfig failed verification"; 771 display_sshdlog(); 772 display_sshdconfig(); 773 exit 1; 774} 775 776 777#*************************************************************************** 778# Generate ssh client host key database file for curl's tests 779# 780if((! -e $knownhosts) || (! -s $knownhosts)) { 781 logmsg 'generating ssh client known hosts file...' if($verbose); 782 unlink($knownhosts); 783 if(open(RSAKEYFILE, "<$hstpubkeyf")) { 784 my @rsahostkey = do { local $/ = ' '; <RSAKEYFILE> }; 785 if(close(RSAKEYFILE)) { 786 if(open(KNOWNHOSTS, ">$knownhosts")) { 787 print KNOWNHOSTS "$listenaddr ssh-rsa $rsahostkey[1]\n"; 788 if(!close(KNOWNHOSTS)) { 789 $error = "Error: cannot close file $knownhosts"; 790 } 791 } 792 else { 793 $error = "Error: cannot write file $knownhosts"; 794 } 795 } 796 else { 797 $error = "Error: cannot close file $hstpubkeyf"; 798 } 799 } 800 else { 801 $error = "Error: cannot read file $hstpubkeyf"; 802 } 803 if($error) { 804 logmsg $error; 805 exit 1; 806 } 807} 808 809 810#*************************************************************************** 811# Convert paths for curl's tests running on Windows using Cygwin OpenSSH 812# 813my $identity_config = abs_path("$path/$identity"); 814my $knownhosts_config = abs_path("$path/$knownhosts"); 815 816if (pathhelp::os_is_win()) { 817 # Ensure to use MinGW/Cygwin paths 818 $identity_config = pathhelp::build_sys_abs_path($identity_config); 819 $knownhosts_config = pathhelp::build_sys_abs_path($knownhosts_config); 820} 821if ($sshdid =~ /OpenSSH-Windows/) { 822 # Ensure to use native Windows paths with OpenSSH for Windows 823 $identity_config = pathhelp::sys_native_abs_path($identity); 824 $knownhosts_config = pathhelp::sys_native_abs_path($knownhosts); 825} 826 827#*************************************************************************** 828# ssh client configuration file options we might use and version support 829# 830# AddressFamily : OpenSSH 3.7.0 and later 831# BatchMode : OpenSSH 1.2.1 and later 832# BindAddress : OpenSSH 2.9.9 and later 833# ChallengeResponseAuthentication : OpenSSH 2.5.0 and later 834# CheckHostIP : OpenSSH 1.2.1 and later 835# Cipher : OpenSSH 1.2.1 and later [3] 836# Ciphers : OpenSSH 2.1.0 and later [3] 837# ClearAllForwardings : OpenSSH 2.9.9 and later 838# Compression : OpenSSH 1.2.1 and later 839# CompressionLevel : OpenSSH 1.2.1 and later [3] 840# ConnectionAttempts : OpenSSH 1.2.1 and later 841# ConnectTimeout : OpenSSH 3.7.0 and later 842# ControlMaster : OpenSSH 3.9.0 and later 843# ControlPath : OpenSSH 3.9.0 and later 844# DisableBanner : SunSSH 1.2.0 and later 845# DynamicForward : OpenSSH 2.9.0 and later 846# EnableSSHKeysign : OpenSSH 3.6.0 and later 847# EscapeChar : OpenSSH 1.2.1 and later [3] 848# ExitOnForwardFailure : OpenSSH 4.4.0 and later 849# ForwardAgent : OpenSSH 1.2.1 and later 850# ForwardX11 : OpenSSH 1.2.1 and later 851# ForwardX11Trusted : OpenSSH 3.8.0 and later 852# GatewayPorts : OpenSSH 1.2.1 and later 853# GlobalKnownHostsFile : OpenSSH 1.2.1 and later 854# GSSAPIAuthentication : OpenSSH 3.7.0 and later [1] 855# GSSAPIDelegateCredentials : OpenSSH 3.7.0 and later [1] 856# HashKnownHosts : OpenSSH 4.0.0 and later 857# Host : OpenSSH 1.2.1 and later 858# HostbasedAuthentication : OpenSSH 2.9.0 and later 859# HostKeyAlgorithms : OpenSSH 2.9.0 and later [3] 860# HostKeyAlias : OpenSSH 2.5.0 and later [3] 861# HostName : OpenSSH 1.2.1 and later 862# IdentitiesOnly : OpenSSH 3.9.0 and later 863# IdentityFile : OpenSSH 1.2.1 and later 864# IgnoreIfUnknown : SunSSH 1.2.0 and later 865# KeepAlive : OpenSSH 1.2.1 and later 866# KbdInteractiveAuthentication : OpenSSH 2.3.0 and later 867# KbdInteractiveDevices : OpenSSH 2.3.0 and later [3] 868# LocalCommand : OpenSSH 4.3.0 and later [3] 869# LocalForward : OpenSSH 1.2.1 and later [3] 870# LogLevel : OpenSSH 1.2.1 and later 871# MACs : OpenSSH 2.5.0 and later [3] 872# NoHostAuthenticationForLocalhost : OpenSSH 3.0.0 and later 873# NumberOfPasswordPrompts : OpenSSH 1.2.1 and later 874# PasswordAuthentication : OpenSSH 1.2.1 and later 875# PermitLocalCommand : OpenSSH 4.3.0 and later 876# Port : OpenSSH 1.2.1 and later 877# PreferredAuthentications : OpenSSH 2.5.2 and later 878# Protocol : OpenSSH 2.1.0 and later 879# ProxyCommand : OpenSSH 1.2.1 and later [3] 880# PubkeyAuthentication : OpenSSH 2.5.0 and later 881# RekeyLimit : OpenSSH 3.7.0 and later 882# RemoteForward : OpenSSH 1.2.1 and later [3] 883# RhostsRSAAuthentication : OpenSSH 1.2.1 and later 884# RSAAuthentication : OpenSSH 1.2.1 and later 885# ServerAliveCountMax : OpenSSH 3.8.0 and later 886# ServerAliveInterval : OpenSSH 3.8.0 and later 887# SmartcardDevice : OpenSSH 2.9.9 and later [1][3] 888# StrictHostKeyChecking : OpenSSH 1.2.1 and later 889# TCPKeepAlive : OpenSSH 3.8.0 and later 890# Tunnel : OpenSSH 4.3.0 and later 891# TunnelDevice : OpenSSH 4.3.0 and later [3] 892# UsePAM : OpenSSH 3.7.0 and later [1][2][3] 893# UsePrivilegedPort : OpenSSH 1.2.1 and later 894# User : OpenSSH 1.2.1 and later 895# UserKnownHostsFile : OpenSSH 1.2.1 and later 896# VerifyHostKeyDNS : OpenSSH 3.8.0 and later 897# XAuthLocation : OpenSSH 2.1.1 and later [3] 898# 899# [1] Option only available if activated at compile time 900# [2] Option specific for portable versions 901# [3] Option not used in our ssh client config file 902 903 904#*************************************************************************** 905# Initialize ssh config with options actually supported in OpenSSH 2.9.9 906# 907logmsg 'generating ssh client config file...' if($verbose); 908@cfgarr = (); 909push @cfgarr, '# This is a generated file. Do not edit.'; 910push @cfgarr, "# $sshverstr ssh client configuration file for curl testing"; 911push @cfgarr, '#'; 912push @cfgarr, 'Host *'; 913push @cfgarr, '#'; 914push @cfgarr, "Port $port"; 915push @cfgarr, "HostName $listenaddr"; 916push @cfgarr, "User $username"; 917push @cfgarr, 'Protocol 2'; 918push @cfgarr, '#'; 919 920# BindAddress option is not supported by OpenSSH for Windows 921if (!($sshdid =~ /OpenSSH-Windows/)) { 922 push @cfgarr, "BindAddress $listenaddr"; 923} 924 925push @cfgarr, '#'; 926push @cfgarr, "IdentityFile $identity_config"; 927push @cfgarr, "UserKnownHostsFile $knownhosts_config"; 928push @cfgarr, '#'; 929push @cfgarr, 'BatchMode yes'; 930push @cfgarr, 'ChallengeResponseAuthentication no'; 931push @cfgarr, 'CheckHostIP no'; 932push @cfgarr, 'ClearAllForwardings no'; 933push @cfgarr, 'Compression no'; 934push @cfgarr, 'ConnectionAttempts 3'; 935push @cfgarr, 'ForwardAgent no'; 936push @cfgarr, 'ForwardX11 no'; 937push @cfgarr, 'GatewayPorts no'; 938push @cfgarr, 'GlobalKnownHostsFile /dev/null'; 939push @cfgarr, 'HostbasedAuthentication no'; 940push @cfgarr, 'KbdInteractiveAuthentication no'; 941push @cfgarr, "LogLevel $loglevel"; 942push @cfgarr, 'NumberOfPasswordPrompts 0'; 943push @cfgarr, 'PasswordAuthentication no'; 944push @cfgarr, 'PreferredAuthentications publickey'; 945push @cfgarr, 'PubkeyAuthentication yes'; 946 947# RSA authentication options are not supported by OpenSSH for Windows 948if (!($sshdid =~ /OpenSSH-Windows/)) { 949 push @cfgarr, 'RhostsRSAAuthentication no'; 950 push @cfgarr, 'RSAAuthentication no'; 951} 952 953# Disabled StrictHostKeyChecking since it makes the tests fail on my 954# OpenSSH_6.0p1 on Debian Linux / Daniel 955push @cfgarr, 'StrictHostKeyChecking no'; 956push @cfgarr, 'UsePrivilegedPort no'; 957push @cfgarr, '#'; 958 959 960#*************************************************************************** 961# Options supported in ssh client newer than OpenSSH 2.9.9 962# 963 964if(($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) { 965 push @cfgarr, 'AddressFamily any'; 966} 967 968if((($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) || 969 (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) { 970 push @cfgarr, 'ConnectTimeout 30'; 971} 972 973if(($sshid =~ /OpenSSH/) && ($sshvernum >= 390)) { 974 push @cfgarr, 'ControlMaster no'; 975} 976 977if(($sshid =~ /OpenSSH/) && ($sshvernum >= 420)) { 978 push @cfgarr, 'ControlPath none'; 979} 980 981if(($sshid =~ /SunSSH/) && ($sshvernum >= 120)) { 982 push @cfgarr, 'DisableBanner yes'; 983} 984 985if(($sshid =~ /OpenSSH/) && ($sshvernum >= 360)) { 986 push @cfgarr, 'EnableSSHKeysign no'; 987} 988 989if(($sshid =~ /OpenSSH/) && ($sshvernum >= 440)) { 990 push @cfgarr, 'ExitOnForwardFailure yes'; 991} 992 993if((($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) || 994 (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) { 995 push @cfgarr, 'ForwardX11Trusted no'; 996} 997 998if(($sshd_builtwith_GSSAPI) && ($sshdid eq $sshid) && 999 ($sshdvernum == $sshvernum)) { 1000 push @cfgarr, 'GSSAPIAuthentication no'; 1001 push @cfgarr, 'GSSAPIDelegateCredentials no'; 1002 if($sshid =~ /SunSSH/) { 1003 push @cfgarr, 'GSSAPIKeyExchange no'; 1004 } 1005} 1006 1007if((($sshid =~ /OpenSSH/) && ($sshvernum >= 400)) || 1008 (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) { 1009 push @cfgarr, 'HashKnownHosts no'; 1010} 1011 1012if(($sshid =~ /OpenSSH/) && ($sshvernum >= 390)) { 1013 push @cfgarr, 'IdentitiesOnly yes'; 1014} 1015 1016if(($sshid =~ /SunSSH/) && ($sshvernum >= 120)) { 1017 push @cfgarr, 'IgnoreIfUnknown no'; 1018} 1019 1020if((($sshid =~ /OpenSSH/) && ($sshvernum < 380)) || 1021 ($sshid =~ /SunSSH/)) { 1022 push @cfgarr, 'KeepAlive no'; 1023} 1024 1025if((($sshid =~ /OpenSSH/) && ($sshvernum >= 300)) || 1026 ($sshid =~ /SunSSH/)) { 1027 push @cfgarr, 'NoHostAuthenticationForLocalhost no'; 1028} 1029 1030if(($sshid =~ /OpenSSH/) && ($sshvernum >= 430)) { 1031 push @cfgarr, 'PermitLocalCommand no'; 1032} 1033 1034if((($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) || 1035 (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) { 1036 push @cfgarr, 'RekeyLimit 1G'; 1037} 1038 1039if((($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) || 1040 (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) { 1041 push @cfgarr, 'ServerAliveCountMax 3'; 1042 push @cfgarr, 'ServerAliveInterval 0'; 1043} 1044 1045if(($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) { 1046 push @cfgarr, 'TCPKeepAlive no'; 1047} 1048 1049if(($sshid =~ /OpenSSH/) && ($sshvernum >= 430)) { 1050 push @cfgarr, 'Tunnel no'; 1051} 1052 1053if(($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) { 1054 push @cfgarr, 'VerifyHostKeyDNS no'; 1055} 1056 1057push @cfgarr, '#'; 1058 1059 1060#*************************************************************************** 1061# Write out resulting ssh client configuration file for curl's tests 1062# 1063$error = dump_array($sshconfig, @cfgarr); 1064if($error) { 1065 logmsg $error; 1066 exit 1; 1067} 1068 1069 1070#*************************************************************************** 1071# Initialize client sftp config with options actually supported. 1072# 1073logmsg 'generating sftp client config file...' if($verbose); 1074splice @cfgarr, 1, 1, "# $sshverstr sftp client configuration file for curl testing"; 1075# 1076for(my $i = scalar(@cfgarr) - 1; $i > 0; $i--) { 1077 if($cfgarr[$i] =~ /^DynamicForward/) { 1078 splice @cfgarr, $i, 1; 1079 next; 1080 } 1081 if($cfgarr[$i] =~ /^ClearAllForwardings/) { 1082 splice @cfgarr, $i, 1, "ClearAllForwardings yes"; 1083 next; 1084 } 1085} 1086 1087 1088#*************************************************************************** 1089# Write out resulting sftp client configuration file for curl's tests 1090# 1091$error = dump_array($sftpconfig, @cfgarr); 1092if($error) { 1093 logmsg $error; 1094 exit 1; 1095} 1096@cfgarr = (); 1097 1098 1099#*************************************************************************** 1100# Generate client sftp commands batch file for sftp server verification 1101# 1102logmsg 'generating sftp client commands file...' if($verbose); 1103push @cfgarr, 'pwd'; 1104push @cfgarr, 'quit'; 1105$error = dump_array($sftpcmds, @cfgarr); 1106if($error) { 1107 logmsg $error; 1108 exit 1; 1109} 1110@cfgarr = (); 1111 1112#*************************************************************************** 1113# Prepare command line of ssh server daemon 1114# 1115my $cmd = "\"$sshd\" -e -D -f $sshdconfig > $sshdlog 2>&1"; 1116logmsg "SCP/SFTP server listening on port $port" if($verbose); 1117logmsg "RUN: $cmd" if($verbose); 1118 1119#*************************************************************************** 1120# Start the ssh server daemon on Windows without forking it 1121# 1122if ($sshdid =~ /OpenSSH-Windows/) { 1123 # Fake pidfile for ssh server on Windows. 1124 if(open(OUT, ">$pidfile")) { 1125 print OUT $$ . "\n"; 1126 close(OUT); 1127 } 1128 1129 # Flush output. 1130 $| = 1; 1131 1132 # Put an "exec" in front of the command so that the child process 1133 # keeps this child's process ID by being tied to the spawned shell. 1134 exec("exec $cmd") || die "Can't exec() $cmd: $!"; 1135 # exec() will create a new process, but ties the existence of the 1136 # new process to the parent waiting perl.exe and sh.exe processes. 1137 1138 # exec() should never return back here to this process. We protect 1139 # ourselves by calling die() just in case something goes really bad. 1140 die "error: exec() has returned"; 1141} 1142 1143#*************************************************************************** 1144# Start the ssh server daemon without forking it 1145# 1146my $rc = system($cmd); 1147if($rc == -1) { 1148 logmsg "\"$sshd\" failed with: $!"; 1149} 1150elsif($rc & 127) { 1151 logmsg sprintf("\"$sshd\" died with signal %d, and %s coredump", 1152 ($rc & 127), ($rc & 128)?'a':'no'); 1153} 1154elsif($verbose && ($rc >> 8)) { 1155 logmsg sprintf("\"$sshd\" exited with %d", $rc >> 8); 1156} 1157 1158 1159#*************************************************************************** 1160# Clean up once the server has stopped 1161# 1162unlink($hstprvkeyf, $hstpubkeyf, $hstpubmd5f, $hstpubsha256f, 1163 $cliprvkeyf, $clipubkeyf, $knownhosts, 1164 $sshdconfig, $sshconfig, $sftpconfig); 1165 1166exit 0; 1167