blob: 576b70635f8eaff92d5f4a9a57213c999053444a [file] [log] [blame]
xf.li6c8fc1e2023-08-12 00:11:09 -07001#!/usr/bin/env perl
2#***************************************************************************
3# _ _ ____ _
4# Project ___| | | | _ \| |
5# / __| | | | |_) | |
6# | (__| |_| | _ <| |___
7# \___|\___/|_| \_\_____|
8#
9# Copyright (C) 1998 - 2022, 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}
554push @cfgarr, '#';
555push @cfgarr, "Port $port";
556push @cfgarr, "ListenAddress $listenaddr";
557push @cfgarr, 'Protocol 2';
558push @cfgarr, '#';
559push @cfgarr, 'AllowTcpForwarding yes';
560push @cfgarr, 'Banner none';
561push @cfgarr, 'ChallengeResponseAuthentication no';
562push @cfgarr, 'ClientAliveCountMax 3';
563push @cfgarr, 'ClientAliveInterval 0';
564push @cfgarr, 'GatewayPorts no';
565push @cfgarr, 'HostbasedAuthentication no';
566push @cfgarr, 'HostbasedUsesNameFromPacketOnly no';
567push @cfgarr, 'IgnoreRhosts yes';
568push @cfgarr, 'IgnoreUserKnownHosts yes';
569push @cfgarr, 'KeyRegenerationInterval 0';
570push @cfgarr, 'LoginGraceTime 30';
571push @cfgarr, "LogLevel $loglevel";
572push @cfgarr, 'MaxStartups 5';
573push @cfgarr, 'PasswordAuthentication no';
574push @cfgarr, 'PermitEmptyPasswords no';
575push @cfgarr, 'PermitRootLogin no';
576push @cfgarr, 'PrintLastLog no';
577push @cfgarr, 'PrintMotd no';
578push @cfgarr, 'PubkeyAuthentication yes';
579push @cfgarr, 'RhostsRSAAuthentication no';
580push @cfgarr, 'RSAAuthentication no';
581push @cfgarr, 'ServerKeyBits 768';
582push @cfgarr, 'StrictModes no';
583push @cfgarr, "Subsystem sftp \"$sftpsrv_config\"";
584push @cfgarr, 'SyslogFacility AUTH';
585push @cfgarr, 'UseLogin no';
586push @cfgarr, 'X11Forwarding no';
587push @cfgarr, '#';
588
589
590#***************************************************************************
591# Write out initial sshd configuration file for curl's tests
592#
593$error = dump_array($sshdconfig, @cfgarr);
594if($error) {
595 logmsg $error;
596 exit 1;
597}
598
599
600#***************************************************************************
601# Verifies at run time if sshd supports a given configuration file option
602#
603sub sshd_supports_opt {
604 my ($option, $value) = @_;
605 my $err;
606 #
607 if((($sshdid =~ /OpenSSH/) && ($sshdvernum >= 310)) ||
608 ($sshdid =~ /SunSSH/)) {
609 # ssh daemon supports command line options -t -f and -o
610 $err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/,
611 qx("$sshd" -t -f $sshdconfig -o "$option=$value" 2>&1);
612 return !$err;
613 }
614 if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 299)) {
615 # ssh daemon supports command line options -t and -f
616 $err = dump_array($sshdconfig, (@cfgarr, "$option $value"));
617 if($err) {
618 logmsg $err;
619 return 0;
620 }
621 $err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/,
622 qx("$sshd" -t -f $sshdconfig 2>&1);
623 unlink $sshdconfig;
624 return !$err;
625 }
626 return 0;
627}
628
629
630#***************************************************************************
631# Kerberos Authentication support may have not been built into sshd
632#
633if(sshd_supports_opt('KerberosAuthentication','no')) {
634 push @cfgarr, 'KerberosAuthentication no';
635}
636if(sshd_supports_opt('KerberosGetAFSToken','no')) {
637 push @cfgarr, 'KerberosGetAFSToken no';
638}
639if(sshd_supports_opt('KerberosOrLocalPasswd','no')) {
640 push @cfgarr, 'KerberosOrLocalPasswd no';
641}
642if(sshd_supports_opt('KerberosTgtPassing','no')) {
643 push @cfgarr, 'KerberosTgtPassing no';
644}
645if(sshd_supports_opt('KerberosTicketCleanup','yes')) {
646 push @cfgarr, 'KerberosTicketCleanup yes';
647}
648
649
650#***************************************************************************
651# Andrew File System support may have not been built into sshd
652#
653if(sshd_supports_opt('AFSTokenPassing','no')) {
654 push @cfgarr, 'AFSTokenPassing no';
655}
656
657
658#***************************************************************************
659# S/Key authentication support may have not been built into sshd
660#
661if(sshd_supports_opt('SkeyAuthentication','no')) {
662 push @cfgarr, 'SkeyAuthentication no';
663}
664
665
666#***************************************************************************
667# GSSAPI Authentication support may have not been built into sshd
668#
669my $sshd_builtwith_GSSAPI;
670if(sshd_supports_opt('GSSAPIAuthentication','no')) {
671 push @cfgarr, 'GSSAPIAuthentication no';
672 $sshd_builtwith_GSSAPI = 1;
673}
674if(sshd_supports_opt('GSSAPICleanupCredentials','yes')) {
675 push @cfgarr, 'GSSAPICleanupCredentials yes';
676}
677if(sshd_supports_opt('GSSAPIKeyExchange','no')) {
678 push @cfgarr, 'GSSAPIKeyExchange no';
679}
680if(sshd_supports_opt('GSSAPIStoreDelegatedCredentials','no')) {
681 push @cfgarr, 'GSSAPIStoreDelegatedCredentials no';
682}
683if(sshd_supports_opt('GSSCleanupCreds','yes')) {
684 push @cfgarr, 'GSSCleanupCreds yes';
685}
686if(sshd_supports_opt('GSSUseSessionCredCache','no')) {
687 push @cfgarr, 'GSSUseSessionCredCache no';
688}
689push @cfgarr, '#';
690
691
692#***************************************************************************
693# Options that might be supported or not in sshd OpenSSH 2.9.9 and later
694#
695if(sshd_supports_opt('AddressFamily','any')) {
696 # Address family must be specified before ListenAddress
697 splice @cfgarr, 14, 0, 'AddressFamily any';
698}
699if(sshd_supports_opt('Compression','no')) {
700 push @cfgarr, 'Compression no';
701}
702if(sshd_supports_opt('KbdInteractiveAuthentication','no')) {
703 push @cfgarr, 'KbdInteractiveAuthentication no';
704}
705if(sshd_supports_opt('KeepAlive','no')) {
706 push @cfgarr, 'KeepAlive no';
707}
708if(sshd_supports_opt('LookupClientHostnames','no')) {
709 push @cfgarr, 'LookupClientHostnames no';
710}
711if(sshd_supports_opt('MaxAuthTries','10')) {
712 push @cfgarr, 'MaxAuthTries 10';
713}
714if(sshd_supports_opt('PAMAuthenticationViaKbdInt','no')) {
715 push @cfgarr, 'PAMAuthenticationViaKbdInt no';
716}
717if(sshd_supports_opt('PermitTunnel','no')) {
718 push @cfgarr, 'PermitTunnel no';
719}
720if(sshd_supports_opt('PermitUserEnvironment','no')) {
721 push @cfgarr, 'PermitUserEnvironment no';
722}
723if(sshd_supports_opt('RhostsAuthentication','no')) {
724 push @cfgarr, 'RhostsAuthentication no';
725}
726if(sshd_supports_opt('TCPKeepAlive','no')) {
727 push @cfgarr, 'TCPKeepAlive no';
728}
729if(sshd_supports_opt('UseDNS','no')) {
730 push @cfgarr, 'UseDNS no';
731}
732if(sshd_supports_opt('UsePAM','no')) {
733 push @cfgarr, 'UsePAM no';
734}
735
736if($sshdid =~ /OpenSSH/) {
737 # http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6492415
738 if(sshd_supports_opt('UsePrivilegeSeparation','no')) {
739 push @cfgarr, 'UsePrivilegeSeparation no';
740 }
741}
742
743if(sshd_supports_opt('VerifyReverseMapping','no')) {
744 push @cfgarr, 'VerifyReverseMapping no';
745}
746if(sshd_supports_opt('X11UseLocalhost','yes')) {
747 push @cfgarr, 'X11UseLocalhost yes';
748}
749push @cfgarr, '#';
750
751
752#***************************************************************************
753# Write out resulting sshd configuration file for curl's tests
754#
755$error = dump_array($sshdconfig, @cfgarr);
756if($error) {
757 logmsg $error;
758 exit 1;
759}
760
761
762#***************************************************************************
763# Verify that sshd actually supports our generated configuration file
764#
765if(system "\"$sshd\" -t -f $sshdconfig > $sshdlog 2>&1") {
766 logmsg "sshd configuration file $sshdconfig failed verification";
767 display_sshdlog();
768 display_sshdconfig();
769 exit 1;
770}
771
772
773#***************************************************************************
774# Generate ssh client host key database file for curl's tests
775#
776if((! -e $knownhosts) || (! -s $knownhosts)) {
777 logmsg 'generating ssh client known hosts file...' if($verbose);
778 unlink($knownhosts);
779 if(open(RSAKEYFILE, "<$hstpubkeyf")) {
780 my @rsahostkey = do { local $/ = ' '; <RSAKEYFILE> };
781 if(close(RSAKEYFILE)) {
782 if(open(KNOWNHOSTS, ">$knownhosts")) {
783 print KNOWNHOSTS "$listenaddr ssh-rsa $rsahostkey[1]\n";
784 if(!close(KNOWNHOSTS)) {
785 $error = "Error: cannot close file $knownhosts";
786 }
787 }
788 else {
789 $error = "Error: cannot write file $knownhosts";
790 }
791 }
792 else {
793 $error = "Error: cannot close file $hstpubkeyf";
794 }
795 }
796 else {
797 $error = "Error: cannot read file $hstpubkeyf";
798 }
799 if($error) {
800 logmsg $error;
801 exit 1;
802 }
803}
804
805
806#***************************************************************************
807# Convert paths for curl's tests running on Windows using Cygwin OpenSSH
808#
809my $identity_config = abs_path("$path/$identity");
810my $knownhosts_config = abs_path("$path/$knownhosts");
811
812if (pathhelp::os_is_win()) {
813 # Ensure to use MinGW/Cygwin paths
814 $identity_config = pathhelp::build_sys_abs_path($identity_config);
815 $knownhosts_config = pathhelp::build_sys_abs_path($knownhosts_config);
816}
817if ($sshdid =~ /OpenSSH-Windows/) {
818 # Ensure to use native Windows paths with OpenSSH for Windows
819 $identity_config = pathhelp::sys_native_abs_path($identity);
820 $knownhosts_config = pathhelp::sys_native_abs_path($knownhosts);
821}
822
823#***************************************************************************
824# ssh client configuration file options we might use and version support
825#
826# AddressFamily : OpenSSH 3.7.0 and later
827# BatchMode : OpenSSH 1.2.1 and later
828# BindAddress : OpenSSH 2.9.9 and later
829# ChallengeResponseAuthentication : OpenSSH 2.5.0 and later
830# CheckHostIP : OpenSSH 1.2.1 and later
831# Cipher : OpenSSH 1.2.1 and later [3]
832# Ciphers : OpenSSH 2.1.0 and later [3]
833# ClearAllForwardings : OpenSSH 2.9.9 and later
834# Compression : OpenSSH 1.2.1 and later
835# CompressionLevel : OpenSSH 1.2.1 and later [3]
836# ConnectionAttempts : OpenSSH 1.2.1 and later
837# ConnectTimeout : OpenSSH 3.7.0 and later
838# ControlMaster : OpenSSH 3.9.0 and later
839# ControlPath : OpenSSH 3.9.0 and later
840# DisableBanner : SunSSH 1.2.0 and later
841# DynamicForward : OpenSSH 2.9.0 and later
842# EnableSSHKeysign : OpenSSH 3.6.0 and later
843# EscapeChar : OpenSSH 1.2.1 and later [3]
844# ExitOnForwardFailure : OpenSSH 4.4.0 and later
845# ForwardAgent : OpenSSH 1.2.1 and later
846# ForwardX11 : OpenSSH 1.2.1 and later
847# ForwardX11Trusted : OpenSSH 3.8.0 and later
848# GatewayPorts : OpenSSH 1.2.1 and later
849# GlobalKnownHostsFile : OpenSSH 1.2.1 and later
850# GSSAPIAuthentication : OpenSSH 3.7.0 and later [1]
851# GSSAPIDelegateCredentials : OpenSSH 3.7.0 and later [1]
852# HashKnownHosts : OpenSSH 4.0.0 and later
853# Host : OpenSSH 1.2.1 and later
854# HostbasedAuthentication : OpenSSH 2.9.0 and later
855# HostKeyAlgorithms : OpenSSH 2.9.0 and later [3]
856# HostKeyAlias : OpenSSH 2.5.0 and later [3]
857# HostName : OpenSSH 1.2.1 and later
858# IdentitiesOnly : OpenSSH 3.9.0 and later
859# IdentityFile : OpenSSH 1.2.1 and later
860# IgnoreIfUnknown : SunSSH 1.2.0 and later
861# KeepAlive : OpenSSH 1.2.1 and later
862# KbdInteractiveAuthentication : OpenSSH 2.3.0 and later
863# KbdInteractiveDevices : OpenSSH 2.3.0 and later [3]
864# LocalCommand : OpenSSH 4.3.0 and later [3]
865# LocalForward : OpenSSH 1.2.1 and later [3]
866# LogLevel : OpenSSH 1.2.1 and later
867# MACs : OpenSSH 2.5.0 and later [3]
868# NoHostAuthenticationForLocalhost : OpenSSH 3.0.0 and later
869# NumberOfPasswordPrompts : OpenSSH 1.2.1 and later
870# PasswordAuthentication : OpenSSH 1.2.1 and later
871# PermitLocalCommand : OpenSSH 4.3.0 and later
872# Port : OpenSSH 1.2.1 and later
873# PreferredAuthentications : OpenSSH 2.5.2 and later
874# Protocol : OpenSSH 2.1.0 and later
875# ProxyCommand : OpenSSH 1.2.1 and later [3]
876# PubkeyAuthentication : OpenSSH 2.5.0 and later
877# RekeyLimit : OpenSSH 3.7.0 and later
878# RemoteForward : OpenSSH 1.2.1 and later [3]
879# RhostsRSAAuthentication : OpenSSH 1.2.1 and later
880# RSAAuthentication : OpenSSH 1.2.1 and later
881# ServerAliveCountMax : OpenSSH 3.8.0 and later
882# ServerAliveInterval : OpenSSH 3.8.0 and later
883# SmartcardDevice : OpenSSH 2.9.9 and later [1][3]
884# StrictHostKeyChecking : OpenSSH 1.2.1 and later
885# TCPKeepAlive : OpenSSH 3.8.0 and later
886# Tunnel : OpenSSH 4.3.0 and later
887# TunnelDevice : OpenSSH 4.3.0 and later [3]
888# UsePAM : OpenSSH 3.7.0 and later [1][2][3]
889# UsePrivilegedPort : OpenSSH 1.2.1 and later
890# User : OpenSSH 1.2.1 and later
891# UserKnownHostsFile : OpenSSH 1.2.1 and later
892# VerifyHostKeyDNS : OpenSSH 3.8.0 and later
893# XAuthLocation : OpenSSH 2.1.1 and later [3]
894#
895# [1] Option only available if activated at compile time
896# [2] Option specific for portable versions
897# [3] Option not used in our ssh client config file
898
899
900#***************************************************************************
901# Initialize ssh config with options actually supported in OpenSSH 2.9.9
902#
903logmsg 'generating ssh client config file...' if($verbose);
904@cfgarr = ();
905push @cfgarr, '# This is a generated file. Do not edit.';
906push @cfgarr, "# $sshverstr ssh client configuration file for curl testing";
907push @cfgarr, '#';
908push @cfgarr, 'Host *';
909push @cfgarr, '#';
910push @cfgarr, "Port $port";
911push @cfgarr, "HostName $listenaddr";
912push @cfgarr, "User $username";
913push @cfgarr, 'Protocol 2';
914push @cfgarr, '#';
915
916# BindAddress option is not supported by OpenSSH for Windows
917if (!($sshdid =~ /OpenSSH-Windows/)) {
918 push @cfgarr, "BindAddress $listenaddr";
919}
920
921push @cfgarr, '#';
922push @cfgarr, "IdentityFile $identity_config";
923push @cfgarr, "UserKnownHostsFile $knownhosts_config";
924push @cfgarr, '#';
925push @cfgarr, 'BatchMode yes';
926push @cfgarr, 'ChallengeResponseAuthentication no';
927push @cfgarr, 'CheckHostIP no';
928push @cfgarr, 'ClearAllForwardings no';
929push @cfgarr, 'Compression no';
930push @cfgarr, 'ConnectionAttempts 3';
931push @cfgarr, 'ForwardAgent no';
932push @cfgarr, 'ForwardX11 no';
933push @cfgarr, 'GatewayPorts no';
934push @cfgarr, 'GlobalKnownHostsFile /dev/null';
935push @cfgarr, 'HostbasedAuthentication no';
936push @cfgarr, 'KbdInteractiveAuthentication no';
937push @cfgarr, "LogLevel $loglevel";
938push @cfgarr, 'NumberOfPasswordPrompts 0';
939push @cfgarr, 'PasswordAuthentication no';
940push @cfgarr, 'PreferredAuthentications publickey';
941push @cfgarr, 'PubkeyAuthentication yes';
942
943# RSA authentication options are not supported by OpenSSH for Windows
944if (!($sshdid =~ /OpenSSH-Windows/)) {
945 push @cfgarr, 'RhostsRSAAuthentication no';
946 push @cfgarr, 'RSAAuthentication no';
947}
948
949# Disabled StrictHostKeyChecking since it makes the tests fail on my
950# OpenSSH_6.0p1 on Debian Linux / Daniel
951push @cfgarr, 'StrictHostKeyChecking no';
952push @cfgarr, 'UsePrivilegedPort no';
953push @cfgarr, '#';
954
955
956#***************************************************************************
957# Options supported in ssh client newer than OpenSSH 2.9.9
958#
959
960if(($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) {
961 push @cfgarr, 'AddressFamily any';
962}
963
964if((($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) ||
965 (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {
966 push @cfgarr, 'ConnectTimeout 30';
967}
968
969if(($sshid =~ /OpenSSH/) && ($sshvernum >= 390)) {
970 push @cfgarr, 'ControlMaster no';
971}
972
973if(($sshid =~ /OpenSSH/) && ($sshvernum >= 420)) {
974 push @cfgarr, 'ControlPath none';
975}
976
977if(($sshid =~ /SunSSH/) && ($sshvernum >= 120)) {
978 push @cfgarr, 'DisableBanner yes';
979}
980
981if(($sshid =~ /OpenSSH/) && ($sshvernum >= 360)) {
982 push @cfgarr, 'EnableSSHKeysign no';
983}
984
985if(($sshid =~ /OpenSSH/) && ($sshvernum >= 440)) {
986 push @cfgarr, 'ExitOnForwardFailure yes';
987}
988
989if((($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) ||
990 (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {
991 push @cfgarr, 'ForwardX11Trusted no';
992}
993
994if(($sshd_builtwith_GSSAPI) && ($sshdid eq $sshid) &&
995 ($sshdvernum == $sshvernum)) {
996 push @cfgarr, 'GSSAPIAuthentication no';
997 push @cfgarr, 'GSSAPIDelegateCredentials no';
998 if($sshid =~ /SunSSH/) {
999 push @cfgarr, 'GSSAPIKeyExchange no';
1000 }
1001}
1002
1003if((($sshid =~ /OpenSSH/) && ($sshvernum >= 400)) ||
1004 (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {
1005 push @cfgarr, 'HashKnownHosts no';
1006}
1007
1008if(($sshid =~ /OpenSSH/) && ($sshvernum >= 390)) {
1009 push @cfgarr, 'IdentitiesOnly yes';
1010}
1011
1012if(($sshid =~ /SunSSH/) && ($sshvernum >= 120)) {
1013 push @cfgarr, 'IgnoreIfUnknown no';
1014}
1015
1016if((($sshid =~ /OpenSSH/) && ($sshvernum < 380)) ||
1017 ($sshid =~ /SunSSH/)) {
1018 push @cfgarr, 'KeepAlive no';
1019}
1020
1021if((($sshid =~ /OpenSSH/) && ($sshvernum >= 300)) ||
1022 ($sshid =~ /SunSSH/)) {
1023 push @cfgarr, 'NoHostAuthenticationForLocalhost no';
1024}
1025
1026if(($sshid =~ /OpenSSH/) && ($sshvernum >= 430)) {
1027 push @cfgarr, 'PermitLocalCommand no';
1028}
1029
1030if((($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) ||
1031 (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {
1032 push @cfgarr, 'RekeyLimit 1G';
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 # Flush output.
1126 $| = 1;
1127
1128 # Put an "exec" in front of the command so that the child process
1129 # keeps this child's process ID by being tied to the spawned shell.
1130 exec("exec $cmd") || die "Can't exec() $cmd: $!";
1131 # exec() will create a new process, but ties the existence of the
1132 # new process to the parent waiting perl.exe and sh.exe processes.
1133
1134 # exec() should never return back here to this process. We protect
1135 # ourselves by calling die() just in case something goes really bad.
1136 die "error: exec() has returned";
1137}
1138
1139#***************************************************************************
1140# Start the ssh server daemon without forking it
1141#
1142my $rc = system($cmd);
1143if($rc == -1) {
1144 logmsg "\"$sshd\" failed with: $!";
1145}
1146elsif($rc & 127) {
1147 logmsg sprintf("\"$sshd\" died with signal %d, and %s coredump",
1148 ($rc & 127), ($rc & 128)?'a':'no');
1149}
1150elsif($verbose && ($rc >> 8)) {
1151 logmsg sprintf("\"$sshd\" exited with %d", $rc >> 8);
1152}
1153
1154
1155#***************************************************************************
1156# Clean up once the server has stopped
1157#
1158unlink($hstprvkeyf, $hstpubkeyf, $hstpubmd5f, $hstpubsha256f,
1159 $cliprvkeyf, $clipubkeyf, $knownhosts,
1160 $sshdconfig, $sshconfig, $sftpconfig);
1161
1162exit 0;