Skip to content

Commit 8e90737

Browse files
committed
Add support for hashed host lists
1 parent 7b59efa commit 8e90737

File tree

1 file changed

+54
-18
lines changed

1 file changed

+54
-18
lines changed

lib/sshkit/backends/netssh.rb

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,32 @@ def initialize(path)
3030
end
3131

3232
def keys_for(hostlist)
33-
keys = hosts_keys || parse_file
33+
keys, hashes = hosts_keys, hosts_hashes
34+
parse_file unless keys && hashes
35+
keys, hashes = hosts_keys, hosts_hashes
36+
3437
hostlist.split(',').each do |host|
35-
if key_list = keys[host]
36-
return key_list
38+
key_list = keys[host]
39+
return key_list if key_list
40+
41+
hashes.each do |(hmac, salt), key|
42+
if OpenSSL::HMAC.digest(sha1, salt, host) == hmac
43+
return key
44+
end
3745
end
3846
end
47+
3948
[]
4049
end
4150

4251
private
4352

4453
attr_reader :path
45-
attr_accessor :hosts_keys
54+
attr_accessor :hosts_keys, :hosts_hashes
55+
56+
def sha1
57+
@sha1 ||= OpenSSL::Digest.new('sha1')
58+
end
4659

4760
def parse_file
4861
synchronize do
@@ -51,35 +64,58 @@ def parse_file
5164
return self.hosts_keys = {} unless File.readable?(path)
5265

5366
new_keys = {}
67+
new_hashes = []
5468
File.open(path) do |file|
5569
scanner = StringScanner.new("")
5670
file.each_line do |line|
5771
scanner.string = line
58-
hostlist, key = parse_line(scanner)
59-
next unless key
60-
61-
hostlist.each do |host|
62-
(new_keys[host] ||= []) << key
63-
end
72+
parse_line(scanner, new_keys, new_hashes)
6473
end
6574
end
66-
return self.hosts_keys = new_keys
75+
self.hosts_keys = new_keys
76+
self.hosts_hashes = new_hashes
77+
end
78+
end
79+
80+
def parse_line(scanner, hosts_keys, hosts_hashes)
81+
return if empty_line?(scanner)
82+
83+
hostlist = parse_hostlist(scanner)
84+
return unless supported_type?(scanner)
85+
key = parse_key(scanner)
86+
87+
if hostlist.size == 1 && hostlist.first =~ /\A\|1(\|.+){2}\z/
88+
hosts_hashes << [parse_host_hash(hostlist.first), key]
89+
else
90+
hostlist.each do |host|
91+
(hosts_keys[host] ||= []) << key
92+
end
6793
end
6894
end
6995

70-
def parse_line(scanner)
96+
def parse_host_hash(line)
97+
_, _, salt, hmac = line.split('|')
98+
[Base64.decode64(hmac), Base64.decode64(salt)]
99+
end
100+
101+
def empty_line?(scanner)
71102
scanner.skip(/\s*/)
72-
return if scanner.match?(/$|#/)
103+
scanner.match?(/$|#/)
104+
end
73105

74-
hostlist = scanner.scan(/\S+/).split(',')
106+
def parse_hostlist(scanner)
75107
scanner.skip(/\s*/)
76-
type = scanner.scan(/\S+/)
108+
scanner.scan(/\S+/).split(',')
109+
end
77110

78-
return unless Net::SSH::KnownHosts::SUPPORTED_TYPE.include?(type)
111+
def supported_type?(scanner)
112+
scanner.skip(/\s*/)
113+
Net::SSH::KnownHosts::SUPPORTED_TYPE.include?(scanner.scan(/\S+/))
114+
end
79115

116+
def parse_key(scanner)
80117
scanner.skip(/\s*/)
81-
blob = scanner.rest.unpack("m*").first
82-
return hostlist, Net::SSH::Buffer.new(blob).read_key
118+
Net::SSH::Buffer.new(scanner.rest.unpack("m*").first).read_key
83119
end
84120
end
85121

0 commit comments

Comments
 (0)