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