#/usr/bin/env perl # $Id$ # Starts sshd for use in the SCP, SFTP and SOCKS curl test harness tests. # Also creates the ssh configuration files (this could be moved to a # separate script). # Options: # -u user # -v # target_port use strict; use File::Spec; my $verbose=0; # set to 1 for debugging my $showfiles=0; my $port = 8999; # just our default, weird enough my $listenaddr = "127.0.0.1"; # address on which to listen my $path = `pwd`; chomp $path; my $exeext; if ($^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys' || $^O eq 'dos' || $^O eq 'os2') { $exeext = '.exe'; } # Where to look for sftp-server my @sftppath = qw( /usr/lib/openssh /usr/libexec/openssh /usr/libexec /usr/local/libexec /opt/local/libexec /usr/lib/ssh /usr/libexec/ssh /usr/sbin /usr/lib /usr/lib/ssh/openssh /usr/lib64/ssh /usr/lib64/misc /usr/lib/misc /usr/local/sbin /usr/freeware/bin /opt/ssh/sbin /opt/ssh/libexec ); my $username = $ENV{USER}; # Find a file somewhere in the given path sub searchpath { my $fn = $_[0] . $exeext; shift; my @path = @_; foreach (@path) { my $file = File::Spec->catfile($_, $fn); if (-e $file) { return $file; } } } # Display contents of the given file. sub displayfile { my ($file) = @_; print "=== Start of file $file\n"; if(open(SINGLE, "<$file")) { while(my $string = ) { print "$string"; } close(SINGLE); } print "=== End of file $file\n"; } # Parse options do { if($ARGV[0] eq "-v") { $verbose=1; } elsif($ARGV[0] eq "-u") { $username=$ARGV[1]; shift @ARGV; } elsif($ARGV[0] eq "-l") { $listenaddr=$ARGV[1]; shift @ARGV; } elsif($ARGV[0] =~ /^(\d+)$/) { $port = $1; } } while(shift @ARGV); my $conffile="curl_sshd_config"; # sshd configuration data my $conffile_ssh="curl_ssh_config"; # ssh configuration data my $knownhostsfile="curl_client_knownhosts"; # ssh knownhosts file # Searching for sshd and sftp-server will be done first # in the PATH and afterwards in other common locations. my @spath; push(@spath, File::Spec->path()); push(@spath, @sftppath); # sshd insists on being called with an absolute path. my $sshd = searchpath("sshd", @spath); if (!$sshd) { print "sshd$exeext not found\n"; exit 1; } if ($verbose) { print STDERR "SSH server found at $sshd\n"; } my $sftp = searchpath("sftp-server", @spath); if (!$sftp) { print "Could not find sftp-server$exeext plugin\n"; exit 1; } if ($verbose) { print STDERR "SFTP server plugin found at $sftp\n"; } if ($username eq "root") { print "Will not run ssh daemon as root to mitigate security risks\n"; exit 1; } # Find out sshd version. my $tmpstr; my $ssh_daemon; my $ssh_ver_major; my $ssh_ver_minor; my $ssh_ver_patch; chomp($tmpstr = qx($sshd -V 2>&1 | grep OpenSSH)); if ($tmpstr =~ /OpenSSH[_-](\d+)\.(\d+)(\.(\d+))*/) { ($ssh_ver_major, $ssh_ver_minor, $ssh_ver_patch) = ($1, $2, $4); $ssh_daemon = 'OpenSSH'; if(10 * $ssh_ver_major + $ssh_ver_minor == 36) { $showfiles=1; } } if(!$ssh_daemon) { chomp($tmpstr = qx($sshd -V 2>&1 | grep Sun_SSH)); if($tmpstr =~ /Sun[_-]SSH[_-](\d+)\.(\d+)/) { ($ssh_ver_major, $ssh_ver_minor) = ($1, $2); $ssh_daemon = 'SunSSH'; if(10 * $ssh_ver_major + $ssh_ver_minor == 11) { $showfiles=1; } } } if ($verbose) { print STDERR "ssh_daemon: $ssh_daemon\n"; print STDERR "ssh_ver_major: $ssh_ver_major\n"; print STDERR "ssh_ver_minor: $ssh_ver_minor\n"; print STDERR "ssh_ver_patch: $ssh_ver_patch\n"; } # Verify minimum SSH daemon version. my $sshd_ver_ok = 1; if(($ssh_daemon =~ /OpenSSH/) && (10 * $ssh_ver_major + $ssh_ver_minor < 36)) { print "SSH server daemon found is OpenSSH $ssh_ver_major.$ssh_ver_minor\n"; $sshd_ver_ok = 0; } if(($ssh_daemon =~ /SunSSH/) && (10 * $ssh_ver_major + $ssh_ver_minor < 11)) { print "SSH server daemon found is SunSSH $ssh_ver_major.$ssh_ver_minor\n"; $sshd_ver_ok = 0; } if(!$ssh_daemon) { print "SSH server daemon found is not OpenSSH nor SunSSH\n"; chomp($tmpstr = qx($sshd -V 2>&1)); print "$tmpstr\n"; $sshd_ver_ok = 0; } if(!$sshd_ver_ok) { print "SCP, SFTP and SOCKS tests require OpenSSH 3.7 or later\n"; exit 1; } # Initialize sshd configuration file for curl's tests. open(CONF, ">$conffile") || die "Could not write $conffile"; print CONF "# This is a generated file! Do not edit!\n"; print CONF "# OpenSSH sshd configuration file for curl testing\n"; close CONF; # Support for some options might have not been built into sshd. On some # platforms specifying an unsupported option prevents sshd from starting. # Check here for possible unsupported options, avoiding its use in sshd. sub sshd_supports_opt($) { my ($option) = @_; my $err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/, qx($sshd -t -f $conffile -o $option=no 2>&1); return !$err; } my $supports_UsePAM = sshd_supports_opt('UsePAM'); my $supports_UseDNS = sshd_supports_opt('UseDNS'); my $supports_ChReAu = sshd_supports_opt('ChallengeResponseAuthentication'); if ($verbose) { print STDERR "sshd supports UsePAM: "; print STDERR $supports_UsePAM ? "yes\n" : "no\n"; print STDERR "sshd supports UseDNS: "; print STDERR $supports_UseDNS ? "yes\n" : "no\n"; print STDERR "sshd supports ChallengeResponseAuthentication: "; print STDERR $supports_ChReAu ? "yes\n" : "no\n"; } if (! -e "curl_client_key.pub") { if ($verbose) { print STDERR "Generating host and client keys...\n"; } # Make sure all files are gone so ssh-keygen doesn't complain unlink("curl_host_dsa_key", "curl_client_key","curl_host_dsa_key.pub", "curl_client_key.pub"); system "ssh-keygen -q -t dsa -f curl_host_dsa_key -C 'curl test server' -N ''" and die "Could not generate host key"; system "ssh-keygen -q -t dsa -f curl_client_key -C 'curl test client' -N ''" and die "Could not generate client key"; } open(FILE, ">>$conffile") || die "Could not write $conffile"; print FILE <>$conffile")) { print FILE "$string\n"; close FILE; } } if ($supports_UsePAM) { set_sshd_option('UsePAM no'); } if ($supports_UseDNS) { set_sshd_option('UseDNS no'); } if ($supports_ChReAu) { set_sshd_option('ChallengeResponseAuthentication no'); } # Now, set up some configuration files for the ssh client open(DSAKEYFILE, " }; close DSAKEYFILE || die "Could not close DSAKEYFILE"; open(KNOWNHOSTS, ">$knownhostsfile") || die "Could not write $knownhostsfile"; print KNOWNHOSTS "[$listenaddr]:$port ssh-dss $dsahostkey[1]\n" || die 'Could not write to KNOWNHOSTS'; close KNOWNHOSTS || die "Could not close KNOWNHOSTS"; open(SSHFILE, ">$conffile_ssh") || die "Could not write $conffile_ssh"; print SSHFILE < log/sshd.log 2>&1") { # This is likely due to missing support for UsePam print "$sshd is too old and is not supported\n"; displayfile("log/sshd.log"); displayfile("$conffile"); unlink "log/sshd.log"; unlink $conffile; exit 1; } # Start the server my $cmdretval = system "$sshd -e -D -f $conffile > log/sshd.log 2>&1"; my $cmdnoexec = $!; if ($cmdretval == -1) { print "$sshd failed with: \n"; print "$cmdnoexec \n"; displayfile("log/sshd.log"); displayfile("$conffile"); } elsif ($cmdretval & 127) { printf("$sshd died with signal %d, and %s coredump.\n", ($cmdretval & 127), ($cmdretval & 128)?"a":"no"); displayfile("log/sshd.log"); displayfile("$conffile"); } elsif ($verbose && ($cmdretval >> 8)) { printf("$sshd exited with %d \n", $cmdretval >> 8); } if($showfiles) { displayfile("log/sshd.log"); displayfile("$conffile"); } unlink $conffile; exit $cmdretval >> 8;