File Coverage

blib/lib/Net/SSH/Perl/Util/Hosts.pm
Criterion Covered Total %
statement 27 153 17.6
branch 0 82 0.0
condition 0 30 0.0
subroutine 9 13 69.2
pod n/a
total 36 278 12.9


line stmt bran cond sub pod time code
1             # $Id: Hosts.pm,v 1.10 2008/10/21 15:41:02 turnstep Exp $
2              
3             package Net::SSH::Perl::Util::Hosts;
4 4     4   32 use strict;
  4         12  
  4         135  
5 4     4   29 use warnings;
  4         11  
  4         138  
6              
7 4     4   30 use Net::SSH::Perl::Constants qw( :hosts );
  4         11  
  4         40  
8 4     4   1363 use Crypt::Misc qw( encode_b64 decode_b64 );
  4         28837  
  4         406  
9 4     4   1504 use Crypt::Mac::HMAC qw( hmac );
  4         5179  
  4         269  
10 4     4   1733 use Socket;
  4         15244  
  4         2063  
11              
12 4     4   42 use Carp qw( croak );
  4         10  
  4         211  
13              
14 4     4   32 use constant SALT_LEN => 20;
  4         11  
  4         4159  
15              
16             sub _check_host_in_hostfile {
17 0     0     my($host, $port, $hostfile, $key) = @_;
18 0           my $key_class = ref($key);
19              
20 0 0 0       if (defined $port && $port != 22) {
21 0           $host = "[$host]:$port";
22             }
23              
24             # ssh returns HOST_NEW if the host file can't be opened
25 0 0         open my $fh, '<', $hostfile or return HOST_NEW;
26 0           local($_, $/);
27 0           $/ = "\n";
28 0           my $status = HOST_NEW;
29 0           HOST: while (<$fh>) {
30 0           chomp;
31 0           my ($hosts, $keyblob) = split /\s+/, $_, 2;
32 0 0         next unless $keyblob;
33 0           my $fkey;
34             ## Trap errors for any potentially unsupported key types
35 0           eval {
36 0           $fkey = $key_class->extract_public($keyblob);
37             };
38 0 0         next if $@;
39              
40 0           my $checkhost = $host;
41              
42             ## Check for hashed entries
43 0 0         if (index($hosts, '|') == 0) {
44 0 0         if ($hosts !~ /^\|1\|(.+?)\|/) {
45 0           warn qq{Cannot parse line $. of $hostfile\n};
46 0           next;
47             }
48 0           my $salt = $1;
49              
50 0           my $rawsalt = decode_b64($salt);
51 0           my $hash = encode_b64(hmac('SHA1',$rawsalt,$host));
52 0           $checkhost = "|1|$salt|$hash";
53             }
54              
55 0           for my $h (split /,/, $hosts) {
56 0 0 0       if ($h eq $checkhost && $key->ssh_name eq $fkey->ssh_name) {
57 0 0         $status = $key->equal($fkey) ? HOST_OK : HOST_CHANGED;
58             last HOST
59 0           }
60             }
61             }
62 0           close $fh;
63 0           $status;
64             }
65              
66             sub _all_keys_for_host {
67 0     0     my($host, $port, $hostfile) = @_;
68 0           my $ip;
69 0 0         if ($host =~ /[a-zA-Z]+/) {
70 0           $ip = inet_ntoa(inet_aton($host));
71             }
72 0 0 0       if (defined $port && $port != 22) {
73 0           $host = "[$host]:$port";
74 0           $ip = "[$ip]:$port";
75             }
76              
77 0 0         open my $fh, '<', $hostfile or return 0;
78 0           local($_, $/);
79 0           $/ = "\n";
80 0           my @keys;
81 0           while (<$fh>) {
82 0           chomp;
83 0           my ($hosts, $keyblob) = split /\s+/, $_, 2;
84 0           my @hosts_to_check = ($host);
85 0 0         push @hosts_to_check, $ip if $ip;
86              
87 0           foreach my $checkhost (@hosts_to_check) {
88             ## Check for hashed entries
89 0 0         if (index($hosts, '|') == 0) {
90 0 0         if ($hosts !~ /^\|1\|(.+?)\|/) {
91 0           warn qq{Cannot parse line $. of $hostfile\n};
92             next
93 0           }
94 0           my $salt = $1;
95            
96 0           my $rawsalt = decode_b64($salt);
97 0           my $hash = encode_b64(hmac('SHA1',$rawsalt,$host));
98 0           $checkhost = "|1|$salt|$hash";
99             }
100 0           for my $h (split /,/, $hosts) {
101 0 0         if ($h eq $checkhost) {
102 0           my $fkey;
103 0           eval { $fkey = Net::SSH::Perl::Key->extract_public($keyblob) };
  0            
104 0 0         push @keys, $fkey if $fkey;
105             }
106             }
107             }
108             }
109 0           close $fh;
110 0 0         return wantarray ? @keys : \@keys
111             }
112              
113             sub _add_host_to_hostfile {
114 0     0     my($host, $port, $hostfile, $key, $hash_flag) = @_;
115 0 0         unless (-e $hostfile) {
116 0           require File::Basename;
117 0           my $dir = File::Basename::dirname($hostfile);
118 0 0         unless (-d $dir) {
119 0           require File::Path;
120 0 0         File::Path::mkpath([ $dir ])
121             or die "Can't create directory $dir: $!";
122             }
123             }
124              
125 0           my $ip;
126 0 0         if ($host =~ /[a-zA-Z]+/) {
127 0           $ip = inet_ntoa(inet_aton($host));
128 0 0 0       $ip = "[$ip]:$port" if $ip && defined $port && $port != 22;
      0        
129             }
130 0 0 0       $host = "[$host]:$port" if defined $port && $port != 22;
131              
132 0           my $data;
133 0 0         open my $fh, '>>', $hostfile or croak "Can't write to $hostfile: $!";
134 0 0         if ($hash_flag) {
135 4     4   37 use Crypt::PRNG qw( random_bytes );
  4         12  
  4         3001  
136 0           my @entries = ($host);
137 0 0         push @entries, $ip if $ip;
138 0           foreach my $entry (@entries) {
139 0           my $rawsalt = random_bytes(SALT_LEN);
140 0           my $salt = encode_b64($rawsalt);
141 0           my $hash = encode_b64(hmac('SHA1', $rawsalt, $entry));
142 0           $data .= join(' ', "|1|$salt|$hash", $key->dump_public, "\n");
143             }
144             }
145             else {
146 0 0         $host = "$host,$ip" if $ip;
147 0           $data = join(' ', $host, $key->dump_public, "\n");
148             }
149 0           print $fh $data;
150 0 0         close $fh or croak "Can't close $hostfile: $!";
151             }
152              
153             sub _remove_host_from_hostfile {
154 0     0     my($host, $port, $hostfile, $key) = @_;
155 0 0         return unless -e $hostfile;
156              
157 0           my $ip;
158 0 0         if ($host =~ /[a-zA-Z]+/) {
159 0           $ip = inet_ntoa(inet_aton($host));
160 0 0 0       $ip = "[$ip]:$port" if $ip && defined $port && $port != 22;
      0        
161             }
162 0 0 0       $host = "[$host]:$port" if defined $port && $port != 22;
163              
164 0 0         open my $fh, '<', $hostfile or croak "Can't open $hostfile: $!";
165 0 0         open my $fhw, '>', "$hostfile.new" or croak "Can't open $hostfile.new for writing: $!";
166              
167 0           LINE: while (<$fh>) {
168 0           chomp;
169 0           my ($hosts, $keyblob) = split /\s+/, $_, 2;
170 0           my $fkey;
171             ## Trap errors for any potentially unsupported key types
172 0           eval {
173 0           $fkey = Net::SSH::Perl::Key->extract_public($keyblob);
174             };
175             # keep it if we don't know what it is
176 0 0         if ($@) {
177 0           print $fhw $_,"\n";
178 0           next LINE;
179             }
180              
181 0           my @hosts_to_check = ($host);
182 0 0         push @hosts_to_check, $ip if $ip;
183              
184 0           foreach my $checkhost (@hosts_to_check) {
185             ## Check for hashed entries
186 0 0         if (index($hosts, '|') == 0) {
187 0 0         if ($hosts !~ /^\|1\|(.+?)\|/) {
188 0           warn qq{Cannot parse line $. of $hostfile\n};
189 0           next;
190             }
191 0           my $salt = $1;
192            
193 0           my $rawsalt = decode_b64($salt);
194 0           my $hash = encode_b64(hmac('SHA1',$rawsalt,$checkhost));
195 0           $checkhost = "|1|$salt|$hash";
196             }
197              
198 0           for my $h (split /,/, $hosts) {
199 0 0 0       if ($h eq $checkhost && $key->equal($fkey)) {
200 0           next LINE;
201             }
202             }
203             }
204 0           print $fhw $_,"\n";
205             }
206 0 0         close $fhw or croak "Can't close $hostfile.new: $!";
207 0 0         close $fh or croak "Can't close $hostfile: $!";
208 0           rename "$hostfile.new", $hostfile;
209             }
210              
211             1;