• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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