net-ssh 3.2.0.rc2 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +2 -2
- data/.dockerignore +6 -0
- data/.github/config/rubocop_linter_action.yml +4 -0
- data/.github/workflows/ci-with-docker.yml +44 -0
- data/.github/workflows/ci.yml +87 -0
- data/.github/workflows/rubocop.yml +16 -0
- data/.gitignore +13 -0
- data/.rubocop.yml +22 -0
- data/.rubocop_todo.yml +1081 -0
- data/CHANGES.txt +228 -7
- data/Dockerfile +27 -0
- data/Dockerfile.openssl3 +17 -0
- data/Gemfile +13 -0
- data/Gemfile.noed25519 +12 -0
- data/ISSUE_TEMPLATE.md +30 -0
- data/Manifest +4 -5
- data/README.md +297 -0
- data/Rakefile +125 -74
- data/SECURITY.md +4 -0
- data/appveyor.yml +58 -0
- data/docker-compose.yml +23 -0
- data/lib/net/ssh/authentication/agent.rb +279 -18
- data/lib/net/ssh/authentication/certificate.rb +183 -0
- data/lib/net/ssh/authentication/constants.rb +17 -15
- data/lib/net/ssh/authentication/ed25519.rb +186 -0
- data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
- data/lib/net/ssh/authentication/key_manager.rb +86 -39
- data/lib/net/ssh/authentication/methods/abstract.rb +67 -48
- data/lib/net/ssh/authentication/methods/hostbased.rb +34 -37
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +13 -13
- data/lib/net/ssh/authentication/methods/none.rb +16 -19
- data/lib/net/ssh/authentication/methods/password.rb +27 -17
- data/lib/net/ssh/authentication/methods/publickey.rb +96 -55
- data/lib/net/ssh/authentication/pageant.rb +471 -367
- data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
- data/lib/net/ssh/authentication/session.rb +131 -121
- data/lib/net/ssh/buffer.rb +399 -300
- data/lib/net/ssh/buffered_io.rb +154 -150
- data/lib/net/ssh/config.rb +308 -185
- data/lib/net/ssh/connection/channel.rb +635 -613
- data/lib/net/ssh/connection/constants.rb +29 -29
- data/lib/net/ssh/connection/event_loop.rb +123 -0
- data/lib/net/ssh/connection/keepalive.rb +55 -51
- data/lib/net/ssh/connection/session.rb +620 -551
- data/lib/net/ssh/connection/term.rb +125 -123
- data/lib/net/ssh/errors.rb +101 -99
- data/lib/net/ssh/key_factory.rb +197 -105
- data/lib/net/ssh/known_hosts.rb +214 -127
- data/lib/net/ssh/loggable.rb +50 -49
- data/lib/net/ssh/packet.rb +83 -79
- data/lib/net/ssh/prompt.rb +50 -81
- data/lib/net/ssh/proxy/command.rb +105 -90
- data/lib/net/ssh/proxy/errors.rb +12 -10
- data/lib/net/ssh/proxy/http.rb +82 -79
- data/lib/net/ssh/proxy/https.rb +50 -0
- data/lib/net/ssh/proxy/jump.rb +54 -0
- data/lib/net/ssh/proxy/socks4.rb +2 -6
- data/lib/net/ssh/proxy/socks5.rb +14 -17
- data/lib/net/ssh/service/forward.rb +370 -317
- data/lib/net/ssh/test/channel.rb +145 -136
- data/lib/net/ssh/test/extensions.rb +131 -110
- data/lib/net/ssh/test/kex.rb +34 -32
- data/lib/net/ssh/test/local_packet.rb +46 -44
- data/lib/net/ssh/test/packet.rb +89 -70
- data/lib/net/ssh/test/remote_packet.rb +32 -30
- data/lib/net/ssh/test/script.rb +156 -142
- data/lib/net/ssh/test/socket.rb +49 -48
- data/lib/net/ssh/test.rb +82 -77
- data/lib/net/ssh/transport/algorithms.rb +441 -360
- data/lib/net/ssh/transport/cipher_factory.rb +96 -98
- data/lib/net/ssh/transport/constants.rb +32 -24
- data/lib/net/ssh/transport/ctr.rb +42 -22
- data/lib/net/ssh/transport/hmac/abstract.rb +81 -63
- data/lib/net/ssh/transport/hmac/md5.rb +0 -2
- data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
- data/lib/net/ssh/transport/hmac/none.rb +0 -2
- data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
- data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
- data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
- data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
- data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
- data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
- data/lib/net/ssh/transport/hmac.rb +14 -12
- data/lib/net/ssh/transport/identity_cipher.rb +54 -52
- data/lib/net/ssh/transport/kex/abstract.rb +130 -0
- data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
- data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
- data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +119 -213
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -61
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
- data/lib/net/ssh/transport/kex.rb +15 -12
- data/lib/net/ssh/transport/key_expander.rb +24 -20
- data/lib/net/ssh/transport/openssl.rb +161 -124
- data/lib/net/ssh/transport/packet_stream.rb +225 -185
- data/lib/net/ssh/transport/server_version.rb +55 -56
- data/lib/net/ssh/transport/session.rb +306 -255
- data/lib/net/ssh/transport/state.rb +178 -176
- data/lib/net/ssh/verifiers/accept_new.rb +33 -0
- data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
- data/lib/net/ssh/verifiers/always.rb +58 -0
- data/lib/net/ssh/verifiers/never.rb +19 -0
- data/lib/net/ssh/version.rb +55 -53
- data/lib/net/ssh.rb +110 -47
- data/net-ssh-public_cert.pem +18 -18
- data/net-ssh.gemspec +36 -205
- data/support/ssh_tunnel_bug.rb +5 -5
- data.tar.gz.sig +0 -0
- metadata +153 -118
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -18
- data/README.rdoc +0 -182
- data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
- data/lib/net/ssh/authentication/agent/socket.rb +0 -178
- data/lib/net/ssh/ruby_compat.rb +0 -46
- data/lib/net/ssh/verifiers/lenient.rb +0 -30
- data/lib/net/ssh/verifiers/null.rb +0 -12
- data/lib/net/ssh/verifiers/secure.rb +0 -52
- data/lib/net/ssh/verifiers/strict.rb +0 -24
- data/setup.rb +0 -1585
- data/support/arcfour_check.rb +0 -20
- data/test/README.txt +0 -18
- data/test/authentication/methods/common.rb +0 -28
- data/test/authentication/methods/test_abstract.rb +0 -51
- data/test/authentication/methods/test_hostbased.rb +0 -114
- data/test/authentication/methods/test_keyboard_interactive.rb +0 -121
- data/test/authentication/methods/test_none.rb +0 -41
- data/test/authentication/methods/test_password.rb +0 -95
- data/test/authentication/methods/test_publickey.rb +0 -148
- data/test/authentication/test_agent.rb +0 -232
- data/test/authentication/test_key_manager.rb +0 -240
- data/test/authentication/test_session.rb +0 -107
- data/test/common.rb +0 -125
- data/test/configs/auth_off +0 -5
- data/test/configs/auth_on +0 -4
- data/test/configs/empty +0 -0
- data/test/configs/eqsign +0 -3
- data/test/configs/exact_match +0 -8
- data/test/configs/host_plus +0 -10
- data/test/configs/multihost +0 -4
- data/test/configs/negative_match +0 -6
- data/test/configs/nohost +0 -19
- data/test/configs/numeric_host +0 -4
- data/test/configs/proxy_remote_user +0 -2
- data/test/configs/send_env +0 -2
- data/test/configs/substitutes +0 -8
- data/test/configs/wild_cards +0 -14
- data/test/connection/test_channel.rb +0 -487
- data/test/connection/test_session.rb +0 -564
- data/test/integration/README.txt +0 -17
- data/test/integration/Vagrantfile +0 -12
- data/test/integration/common.rb +0 -63
- data/test/integration/playbook.yml +0 -56
- data/test/integration/test_forward.rb +0 -637
- data/test/integration/test_id_rsa_keys.rb +0 -96
- data/test/integration/test_proxy.rb +0 -93
- data/test/known_hosts/github +0 -1
- data/test/known_hosts/github_hash +0 -1
- data/test/manual/test_pageant.rb +0 -37
- data/test/start/test_connection.rb +0 -53
- data/test/start/test_options.rb +0 -57
- data/test/start/test_transport.rb +0 -28
- data/test/start/test_user_nil.rb +0 -27
- data/test/test_all.rb +0 -12
- data/test/test_buffer.rb +0 -433
- data/test/test_buffered_io.rb +0 -63
- data/test/test_config.rb +0 -268
- data/test/test_key_factory.rb +0 -191
- data/test/test_known_hosts.rb +0 -66
- data/test/transport/hmac/test_md5.rb +0 -41
- data/test/transport/hmac/test_md5_96.rb +0 -27
- data/test/transport/hmac/test_none.rb +0 -34
- data/test/transport/hmac/test_ripemd160.rb +0 -36
- data/test/transport/hmac/test_sha1.rb +0 -36
- data/test/transport/hmac/test_sha1_96.rb +0 -27
- data/test/transport/hmac/test_sha2_256.rb +0 -37
- data/test/transport/hmac/test_sha2_256_96.rb +0 -27
- data/test/transport/hmac/test_sha2_512.rb +0 -37
- data/test/transport/hmac/test_sha2_512_96.rb +0 -27
- data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
- data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -150
- data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -96
- data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -19
- data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
- data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
- data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
- data/test/transport/test_algorithms.rb +0 -328
- data/test/transport/test_cipher_factory.rb +0 -443
- data/test/transport/test_hmac.rb +0 -34
- data/test/transport/test_identity_cipher.rb +0 -40
- data/test/transport/test_packet_stream.rb +0 -1762
- data/test/transport/test_server_version.rb +0 -74
- data/test/transport/test_session.rb +0 -331
- data/test/transport/test_state.rb +0 -181
- data/test/verifiers/test_secure.rb +0 -40
data/lib/net/ssh/known_hosts.rb
CHANGED
|
@@ -1,89 +1,150 @@
|
|
|
1
1
|
require 'strscan'
|
|
2
2
|
require 'openssl'
|
|
3
3
|
require 'base64'
|
|
4
|
+
require 'delegate'
|
|
4
5
|
require 'net/ssh/buffer'
|
|
6
|
+
require 'net/ssh/authentication/ed25519_loader'
|
|
5
7
|
|
|
6
|
-
module Net
|
|
8
|
+
module Net
|
|
9
|
+
module SSH
|
|
10
|
+
module HostKeyEntries
|
|
11
|
+
# regular public key entry
|
|
12
|
+
class PubKey < Delegator
|
|
13
|
+
def initialize(key, comment: nil) # rubocop:disable Lint/MissingSuper
|
|
14
|
+
@key = key
|
|
15
|
+
@comment = comment
|
|
16
|
+
end
|
|
7
17
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
attr_reader :host
|
|
18
|
+
def ssh_type
|
|
19
|
+
@key.ssh_type
|
|
20
|
+
end
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@known_hosts = known_hosts
|
|
17
|
-
@options = options
|
|
18
|
-
end
|
|
22
|
+
def ssh_types
|
|
23
|
+
[ssh_type]
|
|
24
|
+
end
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
end
|
|
24
|
-
end
|
|
26
|
+
def to_blob
|
|
27
|
+
@key.to_blob
|
|
28
|
+
end
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# This is used internally by Net::SSH, and will never need to be used directly
|
|
31
|
-
# by consumers of the library.
|
|
32
|
-
class KnownHosts
|
|
30
|
+
def __getobj__
|
|
31
|
+
Kernel.warn("Calling Net::SSH::Buffer methods on HostKeyEntries PubKey is deprecated")
|
|
32
|
+
@key
|
|
33
|
+
end
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
def matches_key?(server_key)
|
|
36
|
+
@key.ssh_type == server_key.ssh_type && @key.to_blob == server_key.to_blob
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @cert-authority entry
|
|
41
|
+
class CertAuthority
|
|
42
|
+
def ssh_types
|
|
43
|
+
%w[
|
|
44
|
+
ecdsa-sha2-nistp256-cert-v01@openssh.com
|
|
45
|
+
ecdsa-sha2-nistp384-cert-v01@openssh.com
|
|
46
|
+
ecdsa-sha2-nistp521-cert-v01@openssh.com
|
|
47
|
+
ssh-ed25519-cert-v01@openssh.com
|
|
48
|
+
ssh-rsa-cert-v01@openssh.com
|
|
49
|
+
ssh-rsa-cert-v00@openssh.com
|
|
50
|
+
]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def initialize(key, comment: nil)
|
|
54
|
+
@key = key
|
|
55
|
+
@comment = comment
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def matches_key?(server_key)
|
|
59
|
+
if ssh_types.include?(server_key.ssh_type)
|
|
60
|
+
server_key.signature_valid? && (server_key.signature_key.to_blob == @key.to_blob)
|
|
61
|
+
else
|
|
62
|
+
false
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
41
66
|
end
|
|
42
67
|
|
|
68
|
+
# Represents the result of a search in known hosts
|
|
69
|
+
# see search_for
|
|
70
|
+
class HostKeys
|
|
71
|
+
include Enumerable
|
|
72
|
+
attr_reader :host
|
|
43
73
|
|
|
44
|
-
|
|
74
|
+
def initialize(host_keys, host, known_hosts, options = {})
|
|
75
|
+
@host_keys = host_keys
|
|
76
|
+
@host = host
|
|
77
|
+
@known_hosts = known_hosts
|
|
78
|
+
@options = options
|
|
79
|
+
end
|
|
45
80
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
HostKeys.new(search_in(hostfiles(options), host), host, self, options)
|
|
81
|
+
def add_host_key(key)
|
|
82
|
+
@known_hosts.add(@host, key, @options)
|
|
83
|
+
@host_keys.push(key)
|
|
50
84
|
end
|
|
51
85
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def search_in(files, host)
|
|
55
|
-
files.map { |file| KnownHosts.new(file).keys_for(host) }.flatten
|
|
86
|
+
def each(&block)
|
|
87
|
+
@host_keys.each(&block)
|
|
56
88
|
end
|
|
57
89
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
90
|
+
def empty?
|
|
91
|
+
@host_keys.empty?
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Searches an OpenSSH-style known-host file for a given host, and returns all
|
|
96
|
+
# matching keys. This is used to implement host-key verification, as well as
|
|
97
|
+
# to determine what key a user prefers to use for a given host.
|
|
98
|
+
#
|
|
99
|
+
# This is used internally by Net::SSH, and will never need to be used directly
|
|
100
|
+
# by consumers of the library.
|
|
101
|
+
class KnownHosts
|
|
102
|
+
SUPPORTED_TYPE = %w[ssh-rsa ssh-dss
|
|
103
|
+
ecdsa-sha2-nistp256
|
|
104
|
+
ecdsa-sha2-nistp384
|
|
105
|
+
ecdsa-sha2-nistp521]
|
|
69
106
|
|
|
70
|
-
|
|
71
|
-
|
|
107
|
+
SUPPORTED_TYPE.push('ssh-ed25519') if Net::SSH::Authentication::ED25519Loader::LOADED
|
|
108
|
+
|
|
109
|
+
class << self
|
|
110
|
+
# Searches all known host files (see KnownHosts.hostfiles) for all keys
|
|
111
|
+
# of the given host. Returns an enumerable of keys found.
|
|
112
|
+
def search_for(host, options = {})
|
|
113
|
+
HostKeys.new(search_in(hostfiles(options), host, options), host, self, options)
|
|
72
114
|
end
|
|
73
115
|
|
|
74
|
-
|
|
75
|
-
|
|
116
|
+
# Search for all known keys for the given host, in every file given in
|
|
117
|
+
# the +files+ array. Returns the list of keys.
|
|
118
|
+
def search_in(files, host, options = {})
|
|
119
|
+
files.flat_map { |file| KnownHosts.new(file).keys_for(host, options) }
|
|
76
120
|
end
|
|
77
121
|
|
|
78
|
-
|
|
79
|
-
|
|
122
|
+
# Looks in the given +options+ hash for the :user_known_hosts_file and
|
|
123
|
+
# :global_known_hosts_file keys, and returns an array of all known
|
|
124
|
+
# hosts files. If the :user_known_hosts_file key is not set, the
|
|
125
|
+
# default is returned (~/.ssh/known_hosts and ~/.ssh/known_hosts2). If
|
|
126
|
+
# :global_known_hosts_file is not set, the default is used
|
|
127
|
+
# (/etc/ssh/ssh_known_hosts and /etc/ssh/ssh_known_hosts2).
|
|
128
|
+
#
|
|
129
|
+
# If you only want the user known host files, you can pass :user as
|
|
130
|
+
# the second option.
|
|
131
|
+
def hostfiles(options, which = :all)
|
|
132
|
+
files = []
|
|
133
|
+
|
|
134
|
+
files += Array(options[:user_known_hosts_file] || %w[~/.ssh/known_hosts ~/.ssh/known_hosts2]) if which == :all || which == :user
|
|
135
|
+
|
|
136
|
+
if which == :all || which == :global
|
|
137
|
+
files += Array(options[:global_known_hosts_file] || %w[/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2])
|
|
138
|
+
end
|
|
80
139
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
140
|
+
return files
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Looks in all user known host files (see KnownHosts.hostfiles) and tries to
|
|
144
|
+
# add an entry for the given host and key to the first file it is able
|
|
145
|
+
# to.
|
|
146
|
+
def add(host, key, options = {})
|
|
147
|
+
hostfiles(options, :user).each do |file|
|
|
87
148
|
KnownHosts.new(file).add(host, key)
|
|
88
149
|
return
|
|
89
150
|
rescue SystemCallError
|
|
@@ -91,88 +152,114 @@ module Net; module SSH
|
|
|
91
152
|
end
|
|
92
153
|
end
|
|
93
154
|
end
|
|
94
|
-
end
|
|
95
155
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
156
|
+
# The host-key file name that this KnownHosts instance will use to search
|
|
157
|
+
# for keys.
|
|
158
|
+
attr_reader :source
|
|
99
159
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
160
|
+
# Instantiate a new KnownHosts instance that will search the given known-hosts
|
|
161
|
+
# file. The path is expanded file File.expand_path.
|
|
162
|
+
def initialize(source)
|
|
163
|
+
@source = File.expand_path(source)
|
|
164
|
+
end
|
|
105
165
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
166
|
+
# Returns an array of all keys that are known to be associatd with the
|
|
167
|
+
# given host. The +host+ parameter is either the domain name or ip address
|
|
168
|
+
# of the host, or both (comma-separated). Additionally, if a non-standard
|
|
169
|
+
# port is being used, it may be specified by putting the host (or ip, or
|
|
170
|
+
# both) in square brackets, and appending the port outside the brackets
|
|
171
|
+
# after a colon. Possible formats for +host+, then, are;
|
|
172
|
+
#
|
|
173
|
+
# "net.ssh.test"
|
|
174
|
+
# "1.2.3.4"
|
|
175
|
+
# "net.ssh.test,1.2.3.4"
|
|
176
|
+
# "[net.ssh.test]:5555"
|
|
177
|
+
# "[1,2,3,4]:5555"
|
|
178
|
+
# "[net.ssh.test]:5555,[1.2.3.4]:5555
|
|
179
|
+
def keys_for(host, options = {})
|
|
180
|
+
keys = []
|
|
181
|
+
return keys unless File.readable?(source)
|
|
182
|
+
|
|
183
|
+
entries = host.split(/,/)
|
|
184
|
+
host_name = entries[0]
|
|
185
|
+
host_ip = entries[1]
|
|
122
186
|
|
|
123
|
-
|
|
187
|
+
File.open(source) do |file|
|
|
188
|
+
file.each_line do |line|
|
|
189
|
+
if line.start_with?('@')
|
|
190
|
+
marker, hosts, type, key_content, comment = line.split(' ')
|
|
191
|
+
else
|
|
192
|
+
marker = nil
|
|
193
|
+
hosts, type, key_content, comment = line.split(' ')
|
|
194
|
+
end
|
|
124
195
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
file.each_line do |line|
|
|
128
|
-
scanner.string = line
|
|
196
|
+
# Skip empty line or one that is commented
|
|
197
|
+
next if hosts.nil? || hosts.start_with?('#')
|
|
129
198
|
|
|
130
|
-
|
|
131
|
-
next if scanner.match?(/$|#/)
|
|
199
|
+
hostlist = hosts.split(',')
|
|
132
200
|
|
|
133
|
-
|
|
134
|
-
found = entries.all? { |entry| hostlist.include?(entry) } ||
|
|
135
|
-
known_host_hash?(hostlist, entries, scanner)
|
|
136
|
-
next unless found
|
|
201
|
+
next unless SUPPORTED_TYPE.include?(type)
|
|
137
202
|
|
|
138
|
-
|
|
139
|
-
|
|
203
|
+
found = hostlist.any? { |pattern| match(host_name, pattern) } || known_host_hash?(hostlist, entries)
|
|
204
|
+
next unless found
|
|
140
205
|
|
|
141
|
-
|
|
206
|
+
found = hostlist.include?(host_ip) if options[:check_host_ip] && entries.size > 1 && hostlist.size > 1
|
|
207
|
+
next unless found
|
|
142
208
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
209
|
+
blob = key_content.unpack("m*").first
|
|
210
|
+
raw_key = Net::SSH::Buffer.new(blob).read_key
|
|
211
|
+
|
|
212
|
+
keys <<
|
|
213
|
+
if marker == "@cert-authority"
|
|
214
|
+
HostKeyEntries::CertAuthority.new(raw_key, comment: comment)
|
|
215
|
+
else
|
|
216
|
+
HostKeyEntries::PubKey.new(raw_key, comment: comment)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
146
219
|
end
|
|
220
|
+
|
|
221
|
+
keys
|
|
147
222
|
end
|
|
148
223
|
|
|
149
|
-
|
|
150
|
-
|
|
224
|
+
def match(host, pattern)
|
|
225
|
+
if pattern.include?('*') || pattern.include?('?')
|
|
226
|
+
# see man 8 sshd for pattern details
|
|
227
|
+
pattern_regexp = pattern.split('*', -1).map do |x|
|
|
228
|
+
x.split('?', -1).map do |y|
|
|
229
|
+
Regexp.escape(y)
|
|
230
|
+
end.join('.')
|
|
231
|
+
end.join('.*')
|
|
151
232
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if hostlist.size == 1 && hostlist.first =~ /\A\|1(\|.+){2}\z/
|
|
156
|
-
chunks = hostlist.first.split(/\|/)
|
|
157
|
-
salt = Base64.decode64(chunks[2])
|
|
158
|
-
digest = OpenSSL::Digest.new('sha1')
|
|
159
|
-
entries.each do |entry|
|
|
160
|
-
hmac = OpenSSL::HMAC.digest(digest, salt, entry)
|
|
161
|
-
return true if Base64.encode64(hmac).chomp == chunks[3]
|
|
233
|
+
host =~ Regexp.new("\\A#{pattern_regexp}\\z")
|
|
234
|
+
else
|
|
235
|
+
host == pattern
|
|
162
236
|
end
|
|
163
237
|
end
|
|
164
|
-
false
|
|
165
|
-
end
|
|
166
238
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
239
|
+
# Indicates whether one of the entries matches an hostname that has been
|
|
240
|
+
# stored as a HMAC-SHA1 hash in the known hosts.
|
|
241
|
+
def known_host_hash?(hostlist, entries)
|
|
242
|
+
if hostlist.size == 1 && hostlist.first =~ /\A\|1(\|.+){2}\z/
|
|
243
|
+
chunks = hostlist.first.split(/\|/)
|
|
244
|
+
salt = Base64.decode64(chunks[2])
|
|
245
|
+
digest = OpenSSL::Digest.new('sha1')
|
|
246
|
+
entries.each do |entry|
|
|
247
|
+
hmac = OpenSSL::HMAC.digest(digest, salt, entry)
|
|
248
|
+
return true if Base64.encode64(hmac).chomp == chunks[3]
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
false
|
|
174
252
|
end
|
|
175
|
-
end
|
|
176
253
|
|
|
254
|
+
# Tries to append an entry to the current source file for the given host
|
|
255
|
+
# and key. If it is unable to (because the file is not writable, for
|
|
256
|
+
# instance), an exception will be raised.
|
|
257
|
+
def add(host, key)
|
|
258
|
+
File.open(source, "a") do |file|
|
|
259
|
+
blob = [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").gsub(/\s/, "")
|
|
260
|
+
file.puts "#{host} #{key.ssh_type} #{blob}"
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
177
264
|
end
|
|
178
|
-
end
|
|
265
|
+
end
|
data/lib/net/ssh/loggable.rb
CHANGED
|
@@ -1,61 +1,62 @@
|
|
|
1
|
-
module Net
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
1
|
+
module Net
|
|
2
|
+
module SSH
|
|
3
|
+
# A simple module to make logging easier to deal with. It assumes that the
|
|
4
|
+
# logger instance (if not nil) quacks like a Logger object (in Ruby's
|
|
5
|
+
# standard library). Although used primarily internally by Net::SSH, it
|
|
6
|
+
# can easily be used to add Net::SSH-like logging to your own programs.
|
|
7
|
+
#
|
|
8
|
+
# class MyClass
|
|
9
|
+
# include Net::SSH::Loggable
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# Net::SSH.start(...) do |ssh|
|
|
13
|
+
# obj = MyClass.new
|
|
14
|
+
# obj.logger = ssh.logger
|
|
15
|
+
# ...
|
|
16
|
+
# end
|
|
17
|
+
module Loggable
|
|
18
|
+
# The logger instance that will be used to log messages. If nil, nothing
|
|
19
|
+
# will be logged.
|
|
20
|
+
attr_accessor :logger
|
|
21
|
+
|
|
22
|
+
# Displays the result of yielding if the log level is Logger::DEBUG or
|
|
23
|
+
# greater.
|
|
24
|
+
def debug
|
|
25
|
+
logger.add(Logger::DEBUG, nil, facility) { yield } if logger && logger.debug?
|
|
26
|
+
end
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
# Displays the result of yielding if the log level is Logger::INFO or
|
|
29
|
+
# greater.
|
|
30
|
+
def info
|
|
31
|
+
logger.add(Logger::INFO, nil, facility) { yield } if logger && logger.info?
|
|
32
|
+
end
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
# Displays the result of yielding if the log level is Logger::WARN or
|
|
35
|
+
# greater. (Called lwarn to avoid shadowing with Kernel#warn.)
|
|
36
|
+
def lwarn
|
|
37
|
+
logger.add(Logger::WARN, nil, facility) { yield } if logger && logger.warn?
|
|
38
|
+
end
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
# Displays the result of yielding if the log level is Logger:ERROR or
|
|
41
|
+
# greater.
|
|
42
|
+
def error
|
|
43
|
+
logger.add(Logger::ERROR, nil, facility) { yield } if logger && logger.error?
|
|
44
|
+
end
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
# Displays the result of yielding if the log level is Logger::FATAL or
|
|
47
|
+
# greater.
|
|
48
|
+
def fatal
|
|
49
|
+
logger.add(Logger::FATAL, nil, facility) { yield } if logger && logger.fatal?
|
|
50
|
+
end
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
private
|
|
53
53
|
|
|
54
54
|
# Sets the "facility" value, used for reporting where a log message
|
|
55
55
|
# originates. It defaults to the name of class with the object_id
|
|
56
56
|
# appended.
|
|
57
57
|
def facility
|
|
58
|
-
@facility ||= self.class.
|
|
58
|
+
@facility ||= self.class.to_s.gsub(/::/, ".").gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase + "[%x]" % object_id
|
|
59
59
|
end
|
|
60
|
+
end
|
|
60
61
|
end
|
|
61
|
-
end
|
|
62
|
+
end
|