xf.li | 6c8fc1e | 2023-08-12 00:11:09 -0700 | [diff] [blame] | 1 | #*************************************************************************** |
| 2 | # _ _ ____ _ |
| 3 | # Project ___| | | | _ \| | |
| 4 | # / __| | | | |_) | | |
| 5 | # | (__| |_| | _ <| |___ |
| 6 | # \___|\___/|_| \_\_____| |
| 7 | # |
| 8 | # Copyright (C) 1998 - 2022, 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 | |
| 25 | package sshhelp; |
| 26 | |
| 27 | use strict; |
| 28 | use warnings; |
| 29 | use Exporter; |
| 30 | use File::Spec; |
| 31 | |
| 32 | |
| 33 | #*************************************************************************** |
| 34 | # Global symbols allowed without explicit package name |
| 35 | # |
| 36 | use 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 | # |
| 190 | sub 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 | # |
| 208 | sub 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 | # |
| 234 | sub 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 | # |
| 245 | sub 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 | # |
| 261 | sub display_sshdconfig { |
| 262 | display_file($sshdconfig); |
| 263 | } |
| 264 | |
| 265 | |
| 266 | #*************************************************************************** |
| 267 | # Display contents of the ssh client config file |
| 268 | # |
| 269 | sub display_sshconfig { |
| 270 | display_file($sshconfig); |
| 271 | } |
| 272 | |
| 273 | |
| 274 | #*************************************************************************** |
| 275 | # Display contents of the sftp client config file |
| 276 | # |
| 277 | sub display_sftpconfig { |
| 278 | display_file($sftpconfig); |
| 279 | } |
| 280 | |
| 281 | |
| 282 | #*************************************************************************** |
| 283 | # Display contents of the ssh daemon log file |
| 284 | # |
| 285 | sub 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 | # |
| 294 | sub 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 | # |
| 303 | sub 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 | # |
| 312 | sub 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 | # |
| 328 | sub 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 | # |
| 346 | sub 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 | # |
| 358 | sub 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 | # |
| 370 | sub find_sshd { |
| 371 | return find_file_spath($sshdexe); |
| 372 | } |
| 373 | |
| 374 | |
| 375 | #*************************************************************************** |
| 376 | # Find ssh client and return canonical filename |
| 377 | # |
| 378 | sub find_ssh { |
| 379 | return find_file_spath($sshexe); |
| 380 | } |
| 381 | |
| 382 | |
| 383 | #*************************************************************************** |
| 384 | # Find sftp-server plugin and return canonical filename |
| 385 | # |
| 386 | sub find_sftpsrv { |
| 387 | return find_file_spath($sftpsrvexe); |
| 388 | } |
| 389 | |
| 390 | |
| 391 | #*************************************************************************** |
| 392 | # Find sftp client and return canonical filename |
| 393 | # |
| 394 | sub find_sftp { |
| 395 | return find_file_spath($sftpexe); |
| 396 | } |
| 397 | |
| 398 | |
| 399 | #*************************************************************************** |
| 400 | # Find ssh-keygen and return canonical filename |
| 401 | # |
| 402 | sub find_sshkeygen { |
| 403 | return find_file_spath($sshkeygenexe); |
| 404 | } |
| 405 | |
| 406 | |
| 407 | #*************************************************************************** |
| 408 | # Find httptlssrv (gnutls-serv) and return canonical filename |
| 409 | # |
| 410 | sub find_httptlssrv { |
| 411 | return find_exe_file_hpath($httptlssrvexe); |
| 412 | } |
| 413 | |
| 414 | |
| 415 | #*************************************************************************** |
| 416 | # Return version info for the given ssh client or server binaries |
| 417 | # |
| 418 | sub sshversioninfo { |
| 419 | my $sshbin = $_[0]; # canonical filename |
| 420 | my $major; |
| 421 | my $minor; |
| 422 | my $patch; |
| 423 | my $sshid; |
| 424 | my $versnum; |
| 425 | my $versstr; |
| 426 | my $error; |
| 427 | |
| 428 | if(!$sshbin) { |
| 429 | $error = 'Error: Missing argument 1 for sshversioninfo()'; |
| 430 | } |
| 431 | elsif(! -x $sshbin) { |
| 432 | $error = "Error: cannot read or execute $sshbin"; |
| 433 | } |
| 434 | else { |
| 435 | my $cmd = ($sshbin =~ /$sshdexe$/) ? "\"$sshbin\" -?" : "\"$sshbin\" -V"; |
| 436 | $error = "$cmd\n"; |
| 437 | foreach my $tmpstr (qx($cmd 2>&1)) { |
| 438 | if($tmpstr =~ /OpenSSH[_-](\d+)\.(\d+)(\.(\d+))*/i) { |
| 439 | $major = $1; |
| 440 | $minor = $2; |
| 441 | $patch = $4?$4:0; |
| 442 | $sshid = 'OpenSSH'; |
| 443 | $versnum = (100*$major) + (10*$minor) + $patch; |
| 444 | $versstr = "$sshid $major.$minor.$patch"; |
| 445 | $error = undef; |
| 446 | last; |
| 447 | } |
| 448 | if($tmpstr =~ /OpenSSH[_-]for[_-]Windows[_-](\d+)\.(\d+)(\.(\d+))*/i) { |
| 449 | $major = $1; |
| 450 | $minor = $2; |
| 451 | $patch = $4?$4:0; |
| 452 | $sshid = 'OpenSSH-Windows'; |
| 453 | $versnum = (100*$major) + (10*$minor) + $patch; |
| 454 | $versstr = "$sshid $major.$minor.$patch"; |
| 455 | $error = undef; |
| 456 | last; |
| 457 | } |
| 458 | if($tmpstr =~ /Sun[_-]SSH[_-](\d+)\.(\d+)(\.(\d+))*/i) { |
| 459 | $major = $1; |
| 460 | $minor = $2; |
| 461 | $patch = $4?$4:0; |
| 462 | $sshid = 'SunSSH'; |
| 463 | $versnum = (100*$major) + (10*$minor) + $patch; |
| 464 | $versstr = "$sshid $major.$minor.$patch"; |
| 465 | $error = undef; |
| 466 | last; |
| 467 | } |
| 468 | $error .= $tmpstr; |
| 469 | } |
| 470 | chomp $error if($error); |
| 471 | } |
| 472 | return ($sshid, $versnum, $versstr, $error); |
| 473 | } |
| 474 | |
| 475 | |
| 476 | #*************************************************************************** |
| 477 | # End of library |
| 478 | 1; |