1#*************************************************************************** 2# _ _ ____ _ 3# Project ___| | | | _ \| | 4# / __| | | | |_) | | 5# | (__| |_| | _ <| |___ 6# \___|\___/|_| \_\_____| 7# 8# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9# 10# This software is licensed as described in the file COPYING, which 11# you should have received as part of this distribution. The terms 12# are also available at https://curl.se/docs/copyright.html. 13# 14# You may opt to use, copy, modify, merge, publish, distribute and/or sell 15# copies of the Software, and permit persons to whom the Software is 16# furnished to do so, under the terms of the COPYING file. 17# 18# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19# KIND, either express or implied. 20# 21# SPDX-License-Identifier: curl 22# 23#*************************************************************************** 24 25package sshhelp; 26 27use strict; 28use warnings; 29use Exporter; 30use File::Spec; 31 32 33#*************************************************************************** 34# Global symbols allowed without explicit package name 35# 36use vars qw( 37 @ISA 38 @EXPORT_OK 39 $sshdexe 40 $sshexe 41 $sftpsrvexe 42 $sftpexe 43 $sshkeygenexe 44 $httptlssrvexe 45 $sshdconfig 46 $sshconfig 47 $sftpconfig 48 $knownhosts 49 $sshdlog 50 $sshlog 51 $sftplog 52 $sftpcmds 53 $hstprvkeyf 54 $hstpubkeyf 55 $hstpubmd5f 56 $hstpubsha256f 57 $cliprvkeyf 58 $clipubkeyf 59 @sftppath 60 @httptlssrvpath 61 ); 62 63 64#*************************************************************************** 65# Inherit Exporter's capabilities 66# 67@ISA = qw(Exporter); 68 69 70#*************************************************************************** 71# Global symbols this module will export upon request 72# 73@EXPORT_OK = qw( 74 $sshdexe 75 $sshexe 76 $sftpsrvexe 77 $sftpexe 78 $sshkeygenexe 79 $sshdconfig 80 $sshconfig 81 $sftpconfig 82 $knownhosts 83 $sshdlog 84 $sshlog 85 $sftplog 86 $sftpcmds 87 $hstprvkeyf 88 $hstpubkeyf 89 $hstpubmd5f 90 $hstpubsha256f 91 $cliprvkeyf 92 $clipubkeyf 93 display_sshdconfig 94 display_sshconfig 95 display_sftpconfig 96 display_sshdlog 97 display_sshlog 98 display_sftplog 99 dump_array 100 exe_ext 101 find_sshd 102 find_ssh 103 find_sftpsrv 104 find_sftp 105 find_sshkeygen 106 find_httptlssrv 107 logmsg 108 sshversioninfo 109 ); 110 111 112#*************************************************************************** 113# Global variables initialization 114# 115$sshdexe = 'sshd' .exe_ext('SSH'); # base name and ext of ssh daemon 116$sshexe = 'ssh' .exe_ext('SSH'); # base name and ext of ssh client 117$sftpsrvexe = 'sftp-server' .exe_ext('SSH'); # base name and ext of sftp-server 118$sftpexe = 'sftp' .exe_ext('SSH'); # base name and ext of sftp client 119$sshkeygenexe = 'ssh-keygen' .exe_ext('SSH'); # base name and ext of ssh-keygen 120$httptlssrvexe = 'gnutls-serv' .exe_ext('SSH'); # base name and ext of gnutls-serv 121$sshdconfig = 'curl_sshd_config'; # ssh daemon config file 122$sshconfig = 'curl_ssh_config'; # ssh client config file 123$sftpconfig = 'curl_sftp_config'; # sftp client config file 124$sshdlog = undef; # ssh daemon log file 125$sshlog = undef; # ssh client log file 126$sftplog = undef; # sftp client log file 127$sftpcmds = 'curl_sftp_cmds'; # sftp client commands batch file 128$knownhosts = 'curl_client_knownhosts'; # ssh knownhosts file 129$hstprvkeyf = 'curl_host_rsa_key'; # host private key file 130$hstpubkeyf = 'curl_host_rsa_key.pub'; # host public key file 131$hstpubmd5f = 'curl_host_rsa_key.pub_md5'; # md5 hash of host public key 132$hstpubsha256f = 'curl_host_rsa_key.pub_sha256'; # sha256 hash of host public key 133$cliprvkeyf = 'curl_client_key'; # client private key file 134$clipubkeyf = 'curl_client_key.pub'; # client public key file 135 136 137#*************************************************************************** 138# Absolute paths where to look for sftp-server plugin, when not in PATH 139# 140@sftppath = qw( 141 /usr/lib/openssh 142 /usr/libexec/openssh 143 /usr/libexec 144 /usr/local/libexec 145 /opt/local/libexec 146 /usr/lib/ssh 147 /usr/libexec/ssh 148 /usr/sbin 149 /usr/lib 150 /usr/lib/ssh/openssh 151 /usr/lib64/ssh 152 /usr/lib64/misc 153 /usr/lib/misc 154 /usr/local/sbin 155 /usr/freeware/bin 156 /usr/freeware/sbin 157 /usr/freeware/libexec 158 /opt/ssh/sbin 159 /opt/ssh/libexec 160 ); 161 162 163#*************************************************************************** 164# Absolute paths where to look for httptlssrv (gnutls-serv), when not in PATH 165# 166@httptlssrvpath = qw( 167 /usr/sbin 168 /usr/libexec 169 /usr/lib 170 /usr/lib/misc 171 /usr/lib64/misc 172 /usr/local/bin 173 /usr/local/sbin 174 /usr/local/libexec 175 /opt/local/bin 176 /opt/local/sbin 177 /opt/local/libexec 178 /usr/freeware/bin 179 /usr/freeware/sbin 180 /usr/freeware/libexec 181 /opt/gnutls/bin 182 /opt/gnutls/sbin 183 /opt/gnutls/libexec 184 ); 185 186 187#*************************************************************************** 188# Return file extension for executable files on this operating system 189# 190sub exe_ext { 191 my ($component, @arr) = @_; 192 if ($ENV{'CURL_TEST_EXE_EXT'}) { 193 return $ENV{'CURL_TEST_EXE_EXT'}; 194 } 195 if ($ENV{'CURL_TEST_EXE_EXT_'.$component}) { 196 return $ENV{'CURL_TEST_EXE_EXT_'.$component}; 197 } 198 if ($^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys' || 199 $^O eq 'dos' || $^O eq 'os2') { 200 return '.exe'; 201 } 202} 203 204 205#*************************************************************************** 206# Create or overwrite the given file with lines from an array of strings 207# 208sub dump_array { 209 my ($filename, @arr) = @_; 210 my $error; 211 212 if(!$filename) { 213 $error = 'Error: Missing argument 1 for dump_array()'; 214 } 215 elsif(open(TEXTFH, ">$filename")) { 216 foreach my $line (@arr) { 217 $line .= "\n" unless($line =~ /\n$/); 218 print TEXTFH $line; 219 } 220 if(!close(TEXTFH)) { 221 $error = "Error: cannot close file $filename"; 222 } 223 } 224 else { 225 $error = "Error: cannot write file $filename"; 226 } 227 return $error; 228} 229 230 231#*************************************************************************** 232# Display a message 233# 234sub logmsg { 235 my ($line) = @_; 236 chomp $line if($line); 237 $line .= "\n"; 238 print "$line"; 239} 240 241 242#*************************************************************************** 243# Display contents of the given file 244# 245sub display_file { 246 my $filename = $_[0]; 247 print "=== Start of file $filename\n"; 248 if(open(DISPLAYFH, "<$filename")) { 249 while(my $line = <DISPLAYFH>) { 250 print "$line"; 251 } 252 close DISPLAYFH; 253 } 254 print "=== End of file $filename\n"; 255} 256 257 258#*************************************************************************** 259# Display contents of the ssh daemon config file 260# 261sub display_sshdconfig { 262 display_file($sshdconfig); 263} 264 265 266#*************************************************************************** 267# Display contents of the ssh client config file 268# 269sub display_sshconfig { 270 display_file($sshconfig); 271} 272 273 274#*************************************************************************** 275# Display contents of the sftp client config file 276# 277sub display_sftpconfig { 278 display_file($sftpconfig); 279} 280 281 282#*************************************************************************** 283# Display contents of the ssh daemon log file 284# 285sub display_sshdlog { 286 die "error: \$sshdlog uninitialized" if(not defined $sshdlog); 287 display_file($sshdlog); 288} 289 290 291#*************************************************************************** 292# Display contents of the ssh client log file 293# 294sub display_sshlog { 295 die "error: \$sshlog uninitialized" if(not defined $sshlog); 296 display_file($sshlog); 297} 298 299 300#*************************************************************************** 301# Display contents of the sftp client log file 302# 303sub display_sftplog { 304 die "error: \$sftplog uninitialized" if(not defined $sftplog); 305 display_file($sftplog); 306} 307 308 309#*************************************************************************** 310# Find a file somewhere in the given path 311# 312sub find_file { 313 my $fn = $_[0]; 314 shift; 315 my @path = @_; 316 foreach (@path) { 317 my $file = File::Spec->catfile($_, $fn); 318 if(-e $file && ! -d $file) { 319 return $file; 320 } 321 } 322} 323 324 325#*************************************************************************** 326# Find an executable file somewhere in the given path 327# 328sub find_exe_file { 329 my $fn = $_[0]; 330 shift; 331 my @path = @_; 332 my $xext = exe_ext('SSH'); 333 foreach (@path) { 334 my $file = File::Spec->catfile($_, $fn); 335 if(-e $file && ! -d $file) { 336 return $file if(-x $file); 337 return $file if(($xext) && (lc($file) =~ /\Q$xext\E$/)); 338 } 339 } 340} 341 342 343#*************************************************************************** 344# Find a file in environment path or in our sftppath 345# 346sub find_file_spath { 347 my $filename = $_[0]; 348 my @spath; 349 push(@spath, File::Spec->path()); 350 push(@spath, @sftppath); 351 return find_file($filename, @spath); 352} 353 354 355#*************************************************************************** 356# Find an executable file in environment path or in our httptlssrvpath 357# 358sub find_exe_file_hpath { 359 my $filename = $_[0]; 360 my @hpath; 361 push(@hpath, File::Spec->path()); 362 push(@hpath, @httptlssrvpath); 363 return find_exe_file($filename, @hpath); 364} 365 366 367#*************************************************************************** 368# Find ssh daemon and return canonical filename 369# 370sub find_sshd { 371 return find_file_spath($sshdexe); 372} 373 374 375#*************************************************************************** 376# Find ssh client and return canonical filename 377# 378sub find_ssh { 379 return find_file_spath($sshexe); 380} 381 382 383#*************************************************************************** 384# Find sftp-server plugin and return canonical filename 385# 386sub find_sftpsrv { 387 return find_file_spath($sftpsrvexe); 388} 389 390 391#*************************************************************************** 392# Find sftp client and return canonical filename 393# 394sub find_sftp { 395 return find_file_spath($sftpexe); 396} 397 398 399#*************************************************************************** 400# Find ssh-keygen and return canonical filename 401# 402sub find_sshkeygen { 403 return find_file_spath($sshkeygenexe); 404} 405 406 407#*************************************************************************** 408# Find httptlssrv (gnutls-serv) and return canonical filename 409# 410sub find_httptlssrv { 411 my $p = find_exe_file_hpath($httptlssrvexe); 412 if($p) { 413 my @o = `"$p" -l`; 414 my $found; 415 for(@o) { 416 if(/Key exchange: SRP/) { 417 $found = 1; 418 last; 419 } 420 } 421 return $p if($found); 422 } 423} 424 425 426#*************************************************************************** 427# Return version info for the given ssh client or server binaries 428# 429sub sshversioninfo { 430 my $sshbin = $_[0]; # canonical filename 431 my $major; 432 my $minor; 433 my $patch; 434 my $sshid; 435 my $versnum; 436 my $versstr; 437 my $error; 438 439 if(!$sshbin) { 440 $error = 'Error: Missing argument 1 for sshversioninfo()'; 441 } 442 elsif(! -x $sshbin) { 443 $error = "Error: cannot read or execute $sshbin"; 444 } 445 else { 446 my $cmd = ($sshbin =~ /$sshdexe$/) ? "\"$sshbin\" -?" : "\"$sshbin\" -V"; 447 $error = "$cmd\n"; 448 foreach my $tmpstr (qx($cmd 2>&1)) { 449 if($tmpstr =~ /OpenSSH[_-](\d+)\.(\d+)(\.(\d+))*/i) { 450 $major = $1; 451 $minor = $2; 452 $patch = $4?$4:0; 453 $sshid = 'OpenSSH'; 454 $versnum = (100*$major) + (10*$minor) + $patch; 455 $versstr = "$sshid $major.$minor.$patch"; 456 $error = undef; 457 last; 458 } 459 if($tmpstr =~ /OpenSSH[_-]for[_-]Windows[_-](\d+)\.(\d+)(\.(\d+))*/i) { 460 $major = $1; 461 $minor = $2; 462 $patch = $4?$4:0; 463 $sshid = 'OpenSSH-Windows'; 464 $versnum = (100*$major) + (10*$minor) + $patch; 465 $versstr = "$sshid $major.$minor.$patch"; 466 $error = undef; 467 last; 468 } 469 if($tmpstr =~ /Sun[_-]SSH[_-](\d+)\.(\d+)(\.(\d+))*/i) { 470 $major = $1; 471 $minor = $2; 472 $patch = $4?$4:0; 473 $sshid = 'SunSSH'; 474 $versnum = (100*$major) + (10*$minor) + $patch; 475 $versstr = "$sshid $major.$minor.$patch"; 476 $error = undef; 477 last; 478 } 479 $error .= $tmpstr; 480 } 481 chomp $error if($error); 482 } 483 return ($sshid, $versnum, $versstr, $error); 484} 485 486 487#*************************************************************************** 488# End of library 4891; 490