| 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; |