1#!/usr/bin/env perl
2#***************************************************************************
3#                                  _   _ ____  _
4#  Project                     ___| | | |  _ \| |
5#                             / __| | | | |_) | |
6#                            | (__| |_| |  _ <| |___
7#                             \___|\___/|_| \_\_____|
8#
9# Copyright (C) 1998 - 2014, 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 http://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, SFTP and SOCKS 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';
31
32#***************************************************************************
33# Variables and subs imported from sshhelp module
34#
35use sshhelp qw(
36    $sshdexe
37    $sshexe
38    $sftpsrvexe
39    $sftpexe
40    $sshkeygenexe
41    $sshdconfig
42    $sshconfig
43    $sftpconfig
44    $knownhosts
45    $sshdlog
46    $sshlog
47    $sftplog
48    $sftpcmds
49    $hstprvkeyf
50    $hstpubkeyf
51    $cliprvkeyf
52    $clipubkeyf
53    display_sshdconfig
54    display_sshconfig
55    display_sftpconfig
56    display_sshdlog
57    display_sshlog
58    display_sftplog
59    dump_array
60    find_sshd
61    find_ssh
62    find_sftpsrv
63    find_sftp
64    find_sshkeygen
65    logmsg
66    sshversioninfo
67    );
68
69#***************************************************************************
70# Subs imported from serverhelp module
71#
72use serverhelp qw(
73    server_pidfilename
74    server_logfilename
75    );
76
77
78#***************************************************************************
79
80my $verbose = 0;              # set to 1 for debugging
81my $debugprotocol = 0;        # set to 1 for protocol debugging
82my $port = 8999;              # our default SCP/SFTP server port
83my $socksport = $port + 1;    # our default SOCKS4/5 server port
84my $listenaddr = '127.0.0.1'; # default address on which to listen
85my $ipvnum = 4;               # default IP version of listener address
86my $idnum = 1;                # dafault ssh daemon instance number
87my $proto = 'ssh';            # protocol the ssh daemon speaks
88my $path = getcwd();          # current working directory
89my $logdir = $path .'/log';   # directory for log files
90my $username = $ENV{USER};    # default user
91my $pidfile;                  # ssh daemon pid file
92my $identity = 'curl_client_key'; # default identity file
93
94my $error;
95my @cfgarr;
96
97
98#***************************************************************************
99# Parse command line options
100#
101while(@ARGV) {
102    if($ARGV[0] eq '--verbose') {
103        $verbose = 1;
104    }
105    elsif($ARGV[0] eq '--debugprotocol') {
106        $verbose = 1;
107        $debugprotocol = 1;
108    }
109    elsif($ARGV[0] eq '--user') {
110        if($ARGV[1]) {
111            $username = $ARGV[1];
112            shift @ARGV;
113        }
114    }
115    elsif($ARGV[0] eq '--id') {
116        if($ARGV[1]) {
117            if($ARGV[1] =~ /^(\d+)$/) {
118                $idnum = $1 if($1 > 0);
119                shift @ARGV;
120            }
121        }
122    }
123    elsif($ARGV[0] eq '--ipv4') {
124        $ipvnum = 4;
125        $listenaddr = '127.0.0.1' if($listenaddr eq '::1');
126    }
127    elsif($ARGV[0] eq '--ipv6') {
128        $ipvnum = 6;
129        $listenaddr = '::1' if($listenaddr eq '127.0.0.1');
130    }
131    elsif($ARGV[0] eq '--addr') {
132        if($ARGV[1]) {
133            my $tmpstr = $ARGV[1];
134            if($tmpstr =~ /^(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)$/) {
135                $listenaddr = "$1.$2.$3.$4" if($ipvnum == 4);
136                shift @ARGV;
137            }
138            elsif($ipvnum == 6) {
139                $listenaddr = $tmpstr;
140                $listenaddr =~ s/^\[(.*)\]$/$1/;
141                shift @ARGV;
142            }
143        }
144    }
145    elsif($ARGV[0] eq '--pidfile') {
146        if($ARGV[1]) {
147            $pidfile = "$path/". $ARGV[1];
148            shift @ARGV;
149        }
150    }
151    elsif($ARGV[0] eq '--sshport') {
152        if($ARGV[1]) {
153            if($ARGV[1] =~ /^(\d+)$/) {
154                $port = $1;
155                shift @ARGV;
156            }
157        }
158    }
159    elsif($ARGV[0] eq '--socksport') {
160        if($ARGV[1]) {
161            if($ARGV[1] =~ /^(\d+)$/) {
162                $socksport = $1;
163                shift @ARGV;
164            }
165        }
166    }
167    else {
168        print STDERR "\nWarning: sshserver.pl unknown parameter: $ARGV[0]\n";
169    }
170    shift @ARGV;
171}
172
173
174#***************************************************************************
175# Default ssh daemon pid file name
176#
177if(!$pidfile) {
178    $pidfile = "$path/". server_pidfilename($proto, $ipvnum, $idnum);
179}
180
181
182#***************************************************************************
183# ssh, socks and sftp server log file names
184#
185$sshdlog = server_logfilename($logdir, 'ssh', $ipvnum, $idnum);
186$sftplog = server_logfilename($logdir, 'sftp', $ipvnum, $idnum);
187$sshlog  = server_logfilename($logdir, 'socks', $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, SFTP and SOCKS 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, SFTP and SOCKS 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, SFTP and SOCKS 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, SFTP and SOCKS 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 $cliprvkeyf) || (! -s $cliprvkeyf) ||
370   (! -e $clipubkeyf) || (! -s $clipubkeyf)) {
371    # Make sure all files are gone so ssh-keygen doesn't complain
372    unlink($hstprvkeyf, $hstpubkeyf, $cliprvkeyf, $clipubkeyf);
373    logmsg 'generating host keys...' if($verbose);
374    if(system "\"$sshkeygen\" -q -t dsa -f $hstprvkeyf -C 'curl test server' -N ''") {
375        logmsg 'Could not generate host key';
376        exit 1;
377    }
378    logmsg 'generating client keys...' if($verbose);
379    if(system "\"$sshkeygen\" -q -t dsa -f $cliprvkeyf -C 'curl test client' -N ''") {
380        logmsg 'Could not generate client key';
381        exit 1;
382    }
383}
384
385
386#***************************************************************************
387# Convert paths for curl's tests running on Windows using Cygwin OpenSSH
388#
389my $clipubkeyf_config = abs_path("$path/$clipubkeyf");
390my $hstprvkeyf_config = abs_path("$path/$hstprvkeyf");
391my $pidfile_config = $pidfile;
392my $sftpsrv_config = $sftpsrv;
393
394if ($^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys') {
395    # convert MinGW drive paths to Cygwin drive paths
396    $clipubkeyf_config =~ s/^\/(\w)\//\/cygdrive\/$1\//;
397    $hstprvkeyf_config =~ s/^\/(\w)\//\/cygdrive\/$1\//;
398    $pidfile_config =~ s/^\/(\w)\//\/cygdrive\/$1\//;
399    $sftpsrv_config = "internal-sftp";
400}
401
402#***************************************************************************
403#  ssh daemon configuration file options we might use and version support
404#
405#  AFSTokenPassing                  : OpenSSH 1.2.1 and later [1]
406#  AcceptEnv                        : OpenSSH 3.9.0 and later
407#  AddressFamily                    : OpenSSH 4.0.0 and later
408#  AllowGroups                      : OpenSSH 1.2.1 and later
409#  AllowTcpForwarding               : OpenSSH 2.3.0 and later
410#  AllowUsers                       : OpenSSH 1.2.1 and later
411#  AuthorizedKeysFile               : OpenSSH 2.9.9 and later
412#  AuthorizedKeysFile2              : OpenSSH 2.9.9 and later
413#  Banner                           : OpenSSH 2.5.0 and later
414#  ChallengeResponseAuthentication  : OpenSSH 2.5.0 and later
415#  Ciphers                          : OpenSSH 2.1.0 and later [3]
416#  ClientAliveCountMax              : OpenSSH 2.9.0 and later
417#  ClientAliveInterval              : OpenSSH 2.9.0 and later
418#  Compression                      : OpenSSH 3.3.0 and later
419#  DenyGroups                       : OpenSSH 1.2.1 and later
420#  DenyUsers                        : OpenSSH 1.2.1 and later
421#  ForceCommand                     : OpenSSH 4.4.0 and later [3]
422#  GatewayPorts                     : OpenSSH 2.1.0 and later
423#  GSSAPIAuthentication             : OpenSSH 3.7.0 and later [1]
424#  GSSAPICleanupCredentials         : OpenSSH 3.8.0 and later [1]
425#  GSSAPIKeyExchange                :  SunSSH 1.0.0 and later [1]
426#  GSSAPIStoreDelegatedCredentials  :  SunSSH 1.0.0 and later [1]
427#  GSSCleanupCreds                  :  SunSSH 1.0.0 and later [1]
428#  GSSUseSessionCredCache           :  SunSSH 1.0.0 and later [1]
429#  HostbasedAuthentication          : OpenSSH 2.9.0 and later
430#  HostbasedUsesNameFromPacketOnly  : OpenSSH 2.9.0 and later
431#  HostKey                          : OpenSSH 1.2.1 and later
432#  IgnoreRhosts                     : OpenSSH 1.2.1 and later
433#  IgnoreUserKnownHosts             : OpenSSH 1.2.1 and later
434#  KbdInteractiveAuthentication     : OpenSSH 2.3.0 and later
435#  KeepAlive                        : OpenSSH 1.2.1 and later
436#  KerberosAuthentication           : OpenSSH 1.2.1 and later [1]
437#  KerberosGetAFSToken              : OpenSSH 3.8.0 and later [1]
438#  KerberosOrLocalPasswd            : OpenSSH 1.2.1 and later [1]
439#  KerberosTgtPassing               : OpenSSH 1.2.1 and later [1]
440#  KerberosTicketCleanup            : OpenSSH 1.2.1 and later [1]
441#  KeyRegenerationInterval          : OpenSSH 1.2.1 and later
442#  ListenAddress                    : OpenSSH 1.2.1 and later
443#  LoginGraceTime                   : OpenSSH 1.2.1 and later
444#  LogLevel                         : OpenSSH 1.2.1 and later
445#  LookupClientHostnames            :  SunSSH 1.0.0 and later
446#  MACs                             : OpenSSH 2.5.0 and later [3]
447#  Match                            : OpenSSH 4.4.0 and later [3]
448#  MaxAuthTries                     : OpenSSH 3.9.0 and later
449#  MaxStartups                      : OpenSSH 2.2.0 and later
450#  PAMAuthenticationViaKbdInt       : OpenSSH 2.9.0 and later [2]
451#  PasswordAuthentication           : OpenSSH 1.2.1 and later
452#  PermitEmptyPasswords             : OpenSSH 1.2.1 and later
453#  PermitOpen                       : OpenSSH 4.4.0 and later [3]
454#  PermitRootLogin                  : OpenSSH 1.2.1 and later
455#  PermitTunnel                     : OpenSSH 4.3.0 and later
456#  PermitUserEnvironment            : OpenSSH 3.5.0 and later
457#  PidFile                          : OpenSSH 2.1.0 and later
458#  Port                             : OpenSSH 1.2.1 and later
459#  PrintLastLog                     : OpenSSH 2.9.0 and later
460#  PrintMotd                        : OpenSSH 1.2.1 and later
461#  Protocol                         : OpenSSH 2.1.0 and later
462#  PubkeyAuthentication             : OpenSSH 2.5.0 and later
463#  RhostsAuthentication             : OpenSSH 1.2.1 and later
464#  RhostsRSAAuthentication          : OpenSSH 1.2.1 and later
465#  RSAAuthentication                : OpenSSH 1.2.1 and later
466#  ServerKeyBits                    : OpenSSH 1.2.1 and later
467#  SkeyAuthentication               : OpenSSH 1.2.1 and later [1]
468#  StrictModes                      : OpenSSH 1.2.1 and later
469#  Subsystem                        : OpenSSH 2.2.0 and later
470#  SyslogFacility                   : OpenSSH 1.2.1 and later
471#  TCPKeepAlive                     : OpenSSH 3.8.0 and later
472#  UseDNS                           : OpenSSH 3.7.0 and later
473#  UseLogin                         : OpenSSH 1.2.1 and later
474#  UsePAM                           : OpenSSH 3.7.0 and later [1][2]
475#  UsePrivilegeSeparation           : OpenSSH 3.2.2 and later
476#  VerifyReverseMapping             : OpenSSH 3.1.0 and later
477#  X11DisplayOffset                 : OpenSSH 1.2.1 and later [3]
478#  X11Forwarding                    : OpenSSH 1.2.1 and later
479#  X11UseLocalhost                  : OpenSSH 3.1.0 and later
480#  XAuthLocation                    : OpenSSH 2.1.1 and later [3]
481#
482#  [1] Option only available if activated at compile time
483#  [2] Option specific for portable versions
484#  [3] Option not used in our ssh server config file
485
486
487#***************************************************************************
488# Initialize sshd config with options actually supported in OpenSSH 2.9.9
489#
490logmsg 'generating ssh server config file...' if($verbose);
491@cfgarr = ();
492push @cfgarr, '# This is a generated file.  Do not edit.';
493push @cfgarr, "# $sshdverstr sshd configuration file for curl testing";
494push @cfgarr, '#';
495push @cfgarr, "DenyUsers !$username";
496push @cfgarr, "AllowUsers $username";
497push @cfgarr, 'DenyGroups';
498push @cfgarr, 'AllowGroups';
499push @cfgarr, '#';
500push @cfgarr, "AuthorizedKeysFile $clipubkeyf_config";
501push @cfgarr, "AuthorizedKeysFile2 $clipubkeyf_config";
502push @cfgarr, "HostKey $hstprvkeyf_config";
503push @cfgarr, "PidFile $pidfile_config";
504push @cfgarr, '#';
505push @cfgarr, "Port $port";
506push @cfgarr, "ListenAddress $listenaddr";
507push @cfgarr, 'Protocol 2';
508push @cfgarr, '#';
509push @cfgarr, 'AllowTcpForwarding yes';
510push @cfgarr, 'Banner none';
511push @cfgarr, 'ChallengeResponseAuthentication no';
512push @cfgarr, 'ClientAliveCountMax 3';
513push @cfgarr, 'ClientAliveInterval 0';
514push @cfgarr, 'GatewayPorts no';
515push @cfgarr, 'HostbasedAuthentication no';
516push @cfgarr, 'HostbasedUsesNameFromPacketOnly no';
517push @cfgarr, 'IgnoreRhosts yes';
518push @cfgarr, 'IgnoreUserKnownHosts yes';
519push @cfgarr, 'KeyRegenerationInterval 0';
520push @cfgarr, 'LoginGraceTime 30';
521push @cfgarr, "LogLevel $loglevel";
522push @cfgarr, 'MaxStartups 5';
523push @cfgarr, 'PasswordAuthentication no';
524push @cfgarr, 'PermitEmptyPasswords no';
525push @cfgarr, 'PermitRootLogin no';
526push @cfgarr, 'PrintLastLog no';
527push @cfgarr, 'PrintMotd no';
528push @cfgarr, 'PubkeyAuthentication yes';
529push @cfgarr, 'RhostsRSAAuthentication no';
530push @cfgarr, 'RSAAuthentication no';
531push @cfgarr, 'ServerKeyBits 768';
532push @cfgarr, 'StrictModes no';
533push @cfgarr, "Subsystem sftp \"$sftpsrv_config\"";
534push @cfgarr, 'SyslogFacility AUTH';
535push @cfgarr, 'UseLogin no';
536push @cfgarr, 'X11Forwarding no';
537push @cfgarr, '#';
538
539
540#***************************************************************************
541# Write out initial sshd configuration file for curl's tests
542#
543$error = dump_array($sshdconfig, @cfgarr);
544if($error) {
545    logmsg $error;
546    exit 1;
547}
548
549
550#***************************************************************************
551# Verifies at run time if sshd supports a given configuration file option
552#
553sub sshd_supports_opt {
554    my ($option, $value) = @_;
555    my $err;
556    #
557    if((($sshdid =~ /OpenSSH/) && ($sshdvernum >= 310)) ||
558        ($sshdid =~ /SunSSH/)) {
559        # ssh daemon supports command line options -t -f and -o
560        $err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/,
561                    qx("$sshd" -t -f $sshdconfig -o $option=$value 2>&1);
562        return !$err;
563    }
564    if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 299)) {
565        # ssh daemon supports command line options -t and -f
566        $err = dump_array($sshdconfig, (@cfgarr, "$option $value"));
567        if($err) {
568            logmsg $err;
569            return 0;
570        }
571        $err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/,
572                    qx("$sshd" -t -f $sshdconfig 2>&1);
573        unlink $sshdconfig;
574        return !$err;
575    }
576    return 0;
577}
578
579
580#***************************************************************************
581# Kerberos Authentication support may have not been built into sshd
582#
583if(sshd_supports_opt('KerberosAuthentication','no')) {
584    push @cfgarr, 'KerberosAuthentication no';
585}
586if(sshd_supports_opt('KerberosGetAFSToken','no')) {
587    push @cfgarr, 'KerberosGetAFSToken no';
588}
589if(sshd_supports_opt('KerberosOrLocalPasswd','no')) {
590    push @cfgarr, 'KerberosOrLocalPasswd no';
591}
592if(sshd_supports_opt('KerberosTgtPassing','no')) {
593    push @cfgarr, 'KerberosTgtPassing no';
594}
595if(sshd_supports_opt('KerberosTicketCleanup','yes')) {
596    push @cfgarr, 'KerberosTicketCleanup yes';
597}
598
599
600#***************************************************************************
601# Andrew File System support may have not been built into sshd
602#
603if(sshd_supports_opt('AFSTokenPassing','no')) {
604    push @cfgarr, 'AFSTokenPassing no';
605}
606
607
608#***************************************************************************
609# S/Key authentication support may have not been built into sshd
610#
611if(sshd_supports_opt('SkeyAuthentication','no')) {
612    push @cfgarr, 'SkeyAuthentication no';
613}
614
615
616#***************************************************************************
617# GSSAPI Authentication support may have not been built into sshd
618#
619my $sshd_builtwith_GSSAPI;
620if(sshd_supports_opt('GSSAPIAuthentication','no')) {
621    push @cfgarr, 'GSSAPIAuthentication no';
622    $sshd_builtwith_GSSAPI = 1;
623}
624if(sshd_supports_opt('GSSAPICleanupCredentials','yes')) {
625    push @cfgarr, 'GSSAPICleanupCredentials yes';
626}
627if(sshd_supports_opt('GSSAPIKeyExchange','no')) {
628    push @cfgarr, 'GSSAPIKeyExchange no';
629}
630if(sshd_supports_opt('GSSAPIStoreDelegatedCredentials','no')) {
631    push @cfgarr, 'GSSAPIStoreDelegatedCredentials no';
632}
633if(sshd_supports_opt('GSSCleanupCreds','yes')) {
634    push @cfgarr, 'GSSCleanupCreds yes';
635}
636if(sshd_supports_opt('GSSUseSessionCredCache','no')) {
637    push @cfgarr, 'GSSUseSessionCredCache no';
638}
639push @cfgarr, '#';
640
641
642#***************************************************************************
643# Options that might be supported or not in sshd OpenSSH 2.9.9 and later
644#
645if(sshd_supports_opt('AcceptEnv','')) {
646    push @cfgarr, 'AcceptEnv';
647}
648if(sshd_supports_opt('AddressFamily','any')) {
649    # Address family must be specified before ListenAddress
650    splice @cfgarr, 14, 0, 'AddressFamily any';
651}
652if(sshd_supports_opt('Compression','no')) {
653    push @cfgarr, 'Compression no';
654}
655if(sshd_supports_opt('KbdInteractiveAuthentication','no')) {
656    push @cfgarr, 'KbdInteractiveAuthentication no';
657}
658if(sshd_supports_opt('KeepAlive','no')) {
659    push @cfgarr, 'KeepAlive no';
660}
661if(sshd_supports_opt('LookupClientHostnames','no')) {
662    push @cfgarr, 'LookupClientHostnames no';
663}
664if(sshd_supports_opt('MaxAuthTries','10')) {
665    push @cfgarr, 'MaxAuthTries 10';
666}
667if(sshd_supports_opt('PAMAuthenticationViaKbdInt','no')) {
668    push @cfgarr, 'PAMAuthenticationViaKbdInt no';
669}
670if(sshd_supports_opt('PermitTunnel','no')) {
671    push @cfgarr, 'PermitTunnel no';
672}
673if(sshd_supports_opt('PermitUserEnvironment','no')) {
674    push @cfgarr, 'PermitUserEnvironment no';
675}
676if(sshd_supports_opt('RhostsAuthentication','no')) {
677    push @cfgarr, 'RhostsAuthentication no';
678}
679if(sshd_supports_opt('TCPKeepAlive','no')) {
680    push @cfgarr, 'TCPKeepAlive no';
681}
682if(sshd_supports_opt('UseDNS','no')) {
683    push @cfgarr, 'UseDNS no';
684}
685if(sshd_supports_opt('UsePAM','no')) {
686    push @cfgarr, 'UsePAM no';
687}
688
689if($sshdid =~ /OpenSSH/) {
690    # http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6492415
691    if(sshd_supports_opt('UsePrivilegeSeparation','no')) {
692        push @cfgarr, 'UsePrivilegeSeparation no';
693    }
694}
695
696if(sshd_supports_opt('VerifyReverseMapping','no')) {
697    push @cfgarr, 'VerifyReverseMapping no';
698}
699if(sshd_supports_opt('X11UseLocalhost','yes')) {
700    push @cfgarr, 'X11UseLocalhost yes';
701}
702push @cfgarr, '#';
703
704
705#***************************************************************************
706# Write out resulting sshd configuration file for curl's tests
707#
708$error = dump_array($sshdconfig, @cfgarr);
709if($error) {
710    logmsg $error;
711    exit 1;
712}
713
714
715#***************************************************************************
716# Verify that sshd actually supports our generated configuration file
717#
718if(system "\"$sshd\" -t -f $sshdconfig > $sshdlog 2>&1") {
719    logmsg "sshd configuration file $sshdconfig failed verification";
720    display_sshdlog();
721    display_sshdconfig();
722    exit 1;
723}
724
725
726#***************************************************************************
727# Generate ssh client host key database file for curl's tests
728#
729if((! -e $knownhosts) || (! -s $knownhosts)) {
730    logmsg 'generating ssh client known hosts file...' if($verbose);
731    unlink($knownhosts);
732    if(open(DSAKEYFILE, "<$hstpubkeyf")) {
733        my @dsahostkey = do { local $/ = ' '; <DSAKEYFILE> };
734        if(close(DSAKEYFILE)) {
735            if(open(KNOWNHOSTS, ">$knownhosts")) {
736                print KNOWNHOSTS "$listenaddr ssh-dss $dsahostkey[1]\n";
737                if(!close(KNOWNHOSTS)) {
738                    $error = "Error: cannot close file $knownhosts";
739                }
740            }
741            else {
742                $error = "Error: cannot write file $knownhosts";
743            }
744        }
745        else {
746            $error = "Error: cannot close file $hstpubkeyf";
747        }
748    }
749    else {
750        $error = "Error: cannot read file $hstpubkeyf";
751    }
752    if($error) {
753        logmsg $error;
754        exit 1;
755    }
756}
757
758
759#***************************************************************************
760# Convert paths for curl's tests running on Windows using Cygwin OpenSSH
761#
762my $identity_config = abs_path("$path/$identity");
763my $knownhosts_config = abs_path("$path/$knownhosts");
764
765if ($^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys') {
766    # convert MinGW drive paths to Cygwin drive paths
767    $identity_config =~ s/^\/(\w)\//\/cygdrive\/$1\//;
768    $knownhosts_config =~ s/^\/(\w)\//\/cygdrive\/$1\//;
769}
770
771
772#***************************************************************************
773#  ssh client configuration file options we might use and version support
774#
775#  AddressFamily                     : OpenSSH 3.7.0 and later
776#  BatchMode                         : OpenSSH 1.2.1 and later
777#  BindAddress                       : OpenSSH 2.9.9 and later
778#  ChallengeResponseAuthentication   : OpenSSH 2.5.0 and later
779#  CheckHostIP                       : OpenSSH 1.2.1 and later
780#  Cipher                            : OpenSSH 1.2.1 and later [3]
781#  Ciphers                           : OpenSSH 2.1.0 and later [3]
782#  ClearAllForwardings               : OpenSSH 2.9.9 and later
783#  Compression                       : OpenSSH 1.2.1 and later
784#  CompressionLevel                  : OpenSSH 1.2.1 and later [3]
785#  ConnectionAttempts                : OpenSSH 1.2.1 and later
786#  ConnectTimeout                    : OpenSSH 3.7.0 and later
787#  ControlMaster                     : OpenSSH 3.9.0 and later
788#  ControlPath                       : OpenSSH 3.9.0 and later
789#  DisableBanner                     :  SunSSH 1.2.0 and later
790#  DynamicForward                    : OpenSSH 2.9.0 and later
791#  EnableSSHKeysign                  : OpenSSH 3.6.0 and later
792#  EscapeChar                        : OpenSSH 1.2.1 and later [3]
793#  ExitOnForwardFailure              : OpenSSH 4.4.0 and later
794#  ForwardAgent                      : OpenSSH 1.2.1 and later
795#  ForwardX11                        : OpenSSH 1.2.1 and later
796#  ForwardX11Trusted                 : OpenSSH 3.8.0 and later
797#  GatewayPorts                      : OpenSSH 1.2.1 and later
798#  GlobalKnownHostsFile              : OpenSSH 1.2.1 and later
799#  GSSAPIAuthentication              : OpenSSH 3.7.0 and later [1]
800#  GSSAPIDelegateCredentials         : OpenSSH 3.7.0 and later [1]
801#  HashKnownHosts                    : OpenSSH 4.0.0 and later
802#  Host                              : OpenSSH 1.2.1 and later
803#  HostbasedAuthentication           : OpenSSH 2.9.0 and later
804#  HostKeyAlgorithms                 : OpenSSH 2.9.0 and later [3]
805#  HostKeyAlias                      : OpenSSH 2.5.0 and later [3]
806#  HostName                          : OpenSSH 1.2.1 and later
807#  IdentitiesOnly                    : OpenSSH 3.9.0 and later
808#  IdentityFile                      : OpenSSH 1.2.1 and later
809#  IgnoreIfUnknown                   :  SunSSH 1.2.0 and later
810#  KeepAlive                         : OpenSSH 1.2.1 and later
811#  KbdInteractiveAuthentication      : OpenSSH 2.3.0 and later
812#  KbdInteractiveDevices             : OpenSSH 2.3.0 and later [3]
813#  LocalCommand                      : OpenSSH 4.3.0 and later [3]
814#  LocalForward                      : OpenSSH 1.2.1 and later [3]
815#  LogLevel                          : OpenSSH 1.2.1 and later
816#  MACs                              : OpenSSH 2.5.0 and later [3]
817#  NoHostAuthenticationForLocalhost  : OpenSSH 3.0.0 and later
818#  NumberOfPasswordPrompts           : OpenSSH 1.2.1 and later
819#  PasswordAuthentication            : OpenSSH 1.2.1 and later
820#  PermitLocalCommand                : OpenSSH 4.3.0 and later
821#  Port                              : OpenSSH 1.2.1 and later
822#  PreferredAuthentications          : OpenSSH 2.5.2 and later
823#  Protocol                          : OpenSSH 2.1.0 and later
824#  ProxyCommand                      : OpenSSH 1.2.1 and later [3]
825#  PubkeyAuthentication              : OpenSSH 2.5.0 and later
826#  RekeyLimit                        : OpenSSH 3.7.0 and later
827#  RemoteForward                     : OpenSSH 1.2.1 and later [3]
828#  RhostsRSAAuthentication           : OpenSSH 1.2.1 and later
829#  RSAAuthentication                 : OpenSSH 1.2.1 and later
830#  SendEnv                           : OpenSSH 3.9.0 and later
831#  ServerAliveCountMax               : OpenSSH 3.8.0 and later
832#  ServerAliveInterval               : OpenSSH 3.8.0 and later
833#  SmartcardDevice                   : OpenSSH 2.9.9 and later [1][3]
834#  StrictHostKeyChecking             : OpenSSH 1.2.1 and later
835#  TCPKeepAlive                      : OpenSSH 3.8.0 and later
836#  Tunnel                            : OpenSSH 4.3.0 and later
837#  TunnelDevice                      : OpenSSH 4.3.0 and later [3]
838#  UsePAM                            : OpenSSH 3.7.0 and later [1][2][3]
839#  UsePrivilegedPort                 : OpenSSH 1.2.1 and later
840#  User                              : OpenSSH 1.2.1 and later
841#  UserKnownHostsFile                : OpenSSH 1.2.1 and later
842#  VerifyHostKeyDNS                  : OpenSSH 3.8.0 and later
843#  XAuthLocation                     : OpenSSH 2.1.1 and later [3]
844#
845#  [1] Option only available if activated at compile time
846#  [2] Option specific for portable versions
847#  [3] Option not used in our ssh client config file
848
849
850#***************************************************************************
851# Initialize ssh config with options actually supported in OpenSSH 2.9.9
852#
853logmsg 'generating ssh client config file...' if($verbose);
854@cfgarr = ();
855push @cfgarr, '# This is a generated file.  Do not edit.';
856push @cfgarr, "# $sshverstr ssh client configuration file for curl testing";
857push @cfgarr, '#';
858push @cfgarr, 'Host *';
859push @cfgarr, '#';
860push @cfgarr, "Port $port";
861push @cfgarr, "HostName $listenaddr";
862push @cfgarr, "User $username";
863push @cfgarr, 'Protocol 2';
864push @cfgarr, '#';
865push @cfgarr, "BindAddress $listenaddr";
866push @cfgarr, "DynamicForward $socksport";
867push @cfgarr, '#';
868push @cfgarr, "IdentityFile $identity_config";
869push @cfgarr, "UserKnownHostsFile $knownhosts_config";
870push @cfgarr, '#';
871push @cfgarr, 'BatchMode yes';
872push @cfgarr, 'ChallengeResponseAuthentication no';
873push @cfgarr, 'CheckHostIP no';
874push @cfgarr, 'ClearAllForwardings no';
875push @cfgarr, 'Compression no';
876push @cfgarr, 'ConnectionAttempts 3';
877push @cfgarr, 'ForwardAgent no';
878push @cfgarr, 'ForwardX11 no';
879push @cfgarr, 'GatewayPorts no';
880push @cfgarr, 'GlobalKnownHostsFile /dev/null';
881push @cfgarr, 'HostbasedAuthentication no';
882push @cfgarr, 'KbdInteractiveAuthentication no';
883push @cfgarr, "LogLevel $loglevel";
884push @cfgarr, 'NumberOfPasswordPrompts 0';
885push @cfgarr, 'PasswordAuthentication no';
886push @cfgarr, 'PreferredAuthentications publickey';
887push @cfgarr, 'PubkeyAuthentication yes';
888push @cfgarr, 'RhostsRSAAuthentication no';
889push @cfgarr, 'RSAAuthentication no';
890
891# Disabled StrictHostKeyChecking since it makes the tests fail on my
892# OpenSSH_6.0p1 on Debian Linux / Daniel
893push @cfgarr, 'StrictHostKeyChecking no';
894push @cfgarr, 'UsePrivilegedPort no';
895push @cfgarr, '#';
896
897
898#***************************************************************************
899# Options supported in ssh client newer than OpenSSH 2.9.9
900#
901
902if(($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) {
903    push @cfgarr, 'AddressFamily any';
904}
905
906if((($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) ||
907   (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {
908    push @cfgarr, 'ConnectTimeout 30';
909}
910
911if(($sshid =~ /OpenSSH/) && ($sshvernum >= 390)) {
912    push @cfgarr, 'ControlMaster no';
913}
914
915if(($sshid =~ /OpenSSH/) && ($sshvernum >= 420)) {
916    push @cfgarr, 'ControlPath none';
917}
918
919if(($sshid =~ /SunSSH/) && ($sshvernum >= 120)) {
920    push @cfgarr, 'DisableBanner yes';
921}
922
923if(($sshid =~ /OpenSSH/) && ($sshvernum >= 360)) {
924    push @cfgarr, 'EnableSSHKeysign no';
925}
926
927if(($sshid =~ /OpenSSH/) && ($sshvernum >= 440)) {
928    push @cfgarr, 'ExitOnForwardFailure yes';
929}
930
931if((($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) ||
932   (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {
933    push @cfgarr, 'ForwardX11Trusted no';
934}
935
936if(($sshd_builtwith_GSSAPI) && ($sshdid eq $sshid) &&
937   ($sshdvernum == $sshvernum)) {
938    push @cfgarr, 'GSSAPIAuthentication no';
939    push @cfgarr, 'GSSAPIDelegateCredentials no';
940    if($sshid =~ /SunSSH/) {
941        push @cfgarr, 'GSSAPIKeyExchange no';
942    }
943}
944
945if((($sshid =~ /OpenSSH/) && ($sshvernum >= 400)) ||
946   (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {
947    push @cfgarr, 'HashKnownHosts no';
948}
949
950if(($sshid =~ /OpenSSH/) && ($sshvernum >= 390)) {
951    push @cfgarr, 'IdentitiesOnly yes';
952}
953
954if(($sshid =~ /SunSSH/) && ($sshvernum >= 120)) {
955    push @cfgarr, 'IgnoreIfUnknown no';
956}
957
958if((($sshid =~ /OpenSSH/) && ($sshvernum < 380)) ||
959    ($sshid =~ /SunSSH/)) {
960    push @cfgarr, 'KeepAlive no';
961}
962
963if((($sshid =~ /OpenSSH/) && ($sshvernum >= 300)) ||
964    ($sshid =~ /SunSSH/)) {
965    push @cfgarr, 'NoHostAuthenticationForLocalhost no';
966}
967
968if(($sshid =~ /OpenSSH/) && ($sshvernum >= 430)) {
969    push @cfgarr, 'PermitLocalCommand no';
970}
971
972if((($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) ||
973   (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {
974    push @cfgarr, 'RekeyLimit 1G';
975}
976
977if(($sshid =~ /OpenSSH/) && ($sshvernum >= 390)) {
978    push @cfgarr, 'SendEnv';
979}
980
981if((($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) ||
982   (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) {
983    push @cfgarr, 'ServerAliveCountMax 3';
984    push @cfgarr, 'ServerAliveInterval 0';
985}
986
987if(($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) {
988    push @cfgarr, 'TCPKeepAlive no';
989}
990
991if(($sshid =~ /OpenSSH/) && ($sshvernum >= 430)) {
992    push @cfgarr, 'Tunnel no';
993}
994
995if(($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) {
996    push @cfgarr, 'VerifyHostKeyDNS no';
997}
998
999push @cfgarr, '#';
1000
1001
1002#***************************************************************************
1003# Write out resulting ssh client configuration file for curl's tests
1004#
1005$error = dump_array($sshconfig, @cfgarr);
1006if($error) {
1007    logmsg $error;
1008    exit 1;
1009}
1010
1011
1012#***************************************************************************
1013# Initialize client sftp config with options actually supported.
1014#
1015logmsg 'generating sftp client config file...' if($verbose);
1016splice @cfgarr, 1, 1, "# $sshverstr sftp client configuration file for curl testing";
1017#
1018for(my $i = scalar(@cfgarr) - 1; $i > 0; $i--) {
1019    if($cfgarr[$i] =~ /^DynamicForward/) {
1020        splice @cfgarr, $i, 1;
1021        next;
1022    }
1023    if($cfgarr[$i] =~ /^ClearAllForwardings/) {
1024        splice @cfgarr, $i, 1, "ClearAllForwardings yes";
1025        next;
1026    }
1027}
1028
1029
1030#***************************************************************************
1031# Write out resulting sftp client configuration file for curl's tests
1032#
1033$error = dump_array($sftpconfig, @cfgarr);
1034if($error) {
1035    logmsg $error;
1036    exit 1;
1037}
1038@cfgarr = ();
1039
1040
1041#***************************************************************************
1042# Generate client sftp commands batch file for sftp server verification
1043#
1044logmsg 'generating sftp client commands file...' if($verbose);
1045push @cfgarr, 'pwd';
1046push @cfgarr, 'quit';
1047$error = dump_array($sftpcmds, @cfgarr);
1048if($error) {
1049    logmsg $error;
1050    exit 1;
1051}
1052@cfgarr = ();
1053
1054
1055#***************************************************************************
1056# Start the ssh server daemon without forking it
1057#
1058logmsg "SCP/SFTP server listening on port $port" if($verbose);
1059my $rc = system "\"$sshd\" -e -D -f $sshdconfig > $sshdlog 2>&1";
1060if($rc == -1) {
1061    logmsg "\"$sshd\" failed with: $!";
1062}
1063elsif($rc & 127) {
1064    logmsg sprintf("\"$sshd\" died with signal %d, and %s coredump",
1065                   ($rc & 127), ($rc & 128)?'a':'no');
1066}
1067elsif($verbose && ($rc >> 8)) {
1068    logmsg sprintf("\"$sshd\" exited with %d", $rc >> 8);
1069}
1070
1071
1072#***************************************************************************
1073# Clean up once the server has stopped
1074#
1075unlink($hstprvkeyf, $hstpubkeyf, $cliprvkeyf, $clipubkeyf, $knownhosts);
1076unlink($sshdconfig, $sshconfig, $sftpconfig);
1077
1078
1079exit 0;
1080