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