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