net-ssh 5.0.0.beta1 → 5.0.0.beta2

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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.rubocop_todo.yml +98 -258
  5. data/CHANGES.txt +8 -0
  6. data/Gemfile +1 -3
  7. data/Rakefile +37 -39
  8. data/lib/net/ssh.rb +26 -25
  9. data/lib/net/ssh/authentication/agent.rb +228 -225
  10. data/lib/net/ssh/authentication/certificate.rb +166 -164
  11. data/lib/net/ssh/authentication/constants.rb +17 -14
  12. data/lib/net/ssh/authentication/ed25519.rb +107 -104
  13. data/lib/net/ssh/authentication/ed25519_loader.rb +32 -28
  14. data/lib/net/ssh/authentication/key_manager.rb +5 -3
  15. data/lib/net/ssh/authentication/methods/abstract.rb +53 -47
  16. data/lib/net/ssh/authentication/methods/hostbased.rb +32 -33
  17. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -4
  18. data/lib/net/ssh/authentication/methods/none.rb +10 -10
  19. data/lib/net/ssh/authentication/methods/password.rb +13 -13
  20. data/lib/net/ssh/authentication/methods/publickey.rb +54 -55
  21. data/lib/net/ssh/authentication/pageant.rb +468 -465
  22. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +44 -0
  23. data/lib/net/ssh/authentication/session.rb +127 -123
  24. data/lib/net/ssh/buffer.rb +305 -303
  25. data/lib/net/ssh/buffered_io.rb +163 -162
  26. data/lib/net/ssh/config.rb +230 -227
  27. data/lib/net/ssh/connection/channel.rb +659 -654
  28. data/lib/net/ssh/connection/constants.rb +30 -26
  29. data/lib/net/ssh/connection/event_loop.rb +108 -104
  30. data/lib/net/ssh/connection/keepalive.rb +54 -50
  31. data/lib/net/ssh/connection/session.rb +677 -678
  32. data/lib/net/ssh/connection/term.rb +180 -176
  33. data/lib/net/ssh/errors.rb +101 -99
  34. data/lib/net/ssh/key_factory.rb +108 -108
  35. data/lib/net/ssh/known_hosts.rb +148 -154
  36. data/lib/net/ssh/loggable.rb +56 -54
  37. data/lib/net/ssh/packet.rb +82 -78
  38. data/lib/net/ssh/prompt.rb +55 -53
  39. data/lib/net/ssh/proxy/command.rb +103 -102
  40. data/lib/net/ssh/proxy/errors.rb +12 -8
  41. data/lib/net/ssh/proxy/http.rb +92 -91
  42. data/lib/net/ssh/proxy/https.rb +42 -39
  43. data/lib/net/ssh/proxy/jump.rb +50 -47
  44. data/lib/net/ssh/proxy/socks4.rb +0 -2
  45. data/lib/net/ssh/proxy/socks5.rb +11 -11
  46. data/lib/net/ssh/ruby_compat.rb +1 -0
  47. data/lib/net/ssh/service/forward.rb +364 -362
  48. data/lib/net/ssh/test.rb +85 -83
  49. data/lib/net/ssh/test/channel.rb +146 -142
  50. data/lib/net/ssh/test/extensions.rb +148 -146
  51. data/lib/net/ssh/test/kex.rb +35 -31
  52. data/lib/net/ssh/test/local_packet.rb +48 -44
  53. data/lib/net/ssh/test/packet.rb +87 -84
  54. data/lib/net/ssh/test/remote_packet.rb +35 -31
  55. data/lib/net/ssh/test/script.rb +173 -171
  56. data/lib/net/ssh/test/socket.rb +59 -55
  57. data/lib/net/ssh/transport/algorithms.rb +413 -412
  58. data/lib/net/ssh/transport/cipher_factory.rb +108 -105
  59. data/lib/net/ssh/transport/constants.rb +35 -31
  60. data/lib/net/ssh/transport/ctr.rb +1 -1
  61. data/lib/net/ssh/transport/hmac.rb +1 -1
  62. data/lib/net/ssh/transport/hmac/abstract.rb +67 -64
  63. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +1 -1
  64. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +1 -1
  65. data/lib/net/ssh/transport/identity_cipher.rb +55 -51
  66. data/lib/net/ssh/transport/kex.rb +2 -4
  67. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +47 -40
  68. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +201 -197
  69. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -56
  70. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +94 -87
  71. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +17 -10
  72. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +17 -10
  73. data/lib/net/ssh/transport/key_expander.rb +29 -25
  74. data/lib/net/ssh/transport/openssl.rb +17 -30
  75. data/lib/net/ssh/transport/packet_stream.rb +193 -192
  76. data/lib/net/ssh/transport/server_version.rb +64 -66
  77. data/lib/net/ssh/transport/session.rb +286 -284
  78. data/lib/net/ssh/transport/state.rb +198 -196
  79. data/lib/net/ssh/verifiers/lenient.rb +29 -25
  80. data/lib/net/ssh/verifiers/null.rb +13 -9
  81. data/lib/net/ssh/verifiers/secure.rb +45 -45
  82. data/lib/net/ssh/verifiers/strict.rb +20 -16
  83. data/lib/net/ssh/version.rb +55 -53
  84. data/net-ssh.gemspec +4 -4
  85. data/support/ssh_tunnel_bug.rb +2 -2
  86. metadata +25 -24
  87. metadata.gz.sig +0 -0
@@ -3,130 +3,130 @@ require 'net/ssh/prompt'
3
3
 
4
4
  require 'net/ssh/authentication/ed25519_loader'
5
5
 
6
- module Net; module SSH
7
-
8
- # A factory class for returning new Key classes. It is used for obtaining
9
- # OpenSSL key instances via their SSH names, and for loading both public and
10
- # private keys. It used used primarily by Net::SSH itself, internally, and
11
- # will rarely (if ever) be directly used by consumers of the library.
12
- #
13
- # klass = Net::SSH::KeyFactory.get("rsa")
14
- # assert klass.is_a?(OpenSSL::PKey::RSA)
15
- #
16
- # key = Net::SSH::KeyFactory.load_public_key("~/.ssh/id_dsa.pub")
17
- class KeyFactory
18
- # Specifies the mapping of SSH names to OpenSSL key classes.
19
- MAP = {
20
- "dh" => OpenSSL::PKey::DH,
21
- "rsa" => OpenSSL::PKey::RSA,
22
- "dsa" => OpenSSL::PKey::DSA,
23
- }
24
- if defined?(OpenSSL::PKey::EC)
25
- MAP["ecdsa"] = OpenSSL::PKey::EC
26
- MAP["ed25519"] = Net::SSH::Authentication::ED25519::PrivKey if defined? Net::SSH::Authentication::ED25519
27
- end
28
-
29
- class <<self
30
- # Fetch an OpenSSL key instance by its SSH name. It will be a new,
31
- # empty key of the given type.
32
- def get(name)
33
- MAP.fetch(name).new
34
- end
35
-
36
- # Loads a private key from a file. It will correctly determine
37
- # whether the file describes an RSA or DSA key, and will load it
38
- # appropriately. The new key is returned. If the key itself is
39
- # encrypted (requiring a passphrase to use), the user will be
40
- # prompted to enter their password unless passphrase works.
41
- def load_private_key(filename, passphrase=nil, ask_passphrase=true, prompt=Prompt.default)
42
- data = File.read(File.expand_path(filename))
43
- load_data_private_key(data, passphrase, ask_passphrase, filename, prompt)
6
+ module Net
7
+ module SSH
8
+
9
+ # A factory class for returning new Key classes. It is used for obtaining
10
+ # OpenSSL key instances via their SSH names, and for loading both public and
11
+ # private keys. It used used primarily by Net::SSH itself, internally, and
12
+ # will rarely (if ever) be directly used by consumers of the library.
13
+ #
14
+ # klass = Net::SSH::KeyFactory.get("rsa")
15
+ # assert klass.is_a?(OpenSSL::PKey::RSA)
16
+ #
17
+ # key = Net::SSH::KeyFactory.load_public_key("~/.ssh/id_dsa.pub")
18
+ class KeyFactory
19
+ # Specifies the mapping of SSH names to OpenSSL key classes.
20
+ MAP = {
21
+ "dh" => OpenSSL::PKey::DH,
22
+ "rsa" => OpenSSL::PKey::RSA,
23
+ "dsa" => OpenSSL::PKey::DSA
24
+ }
25
+ if defined?(OpenSSL::PKey::EC)
26
+ MAP["ecdsa"] = OpenSSL::PKey::EC
27
+ MAP["ed25519"] = Net::SSH::Authentication::ED25519::PrivKey if defined? Net::SSH::Authentication::ED25519
44
28
  end
45
29
 
46
- # Loads a private key. It will correctly determine
47
- # whether the file describes an RSA or DSA key, and will load it
48
- # appropriately. The new key is returned. If the key itself is
49
- # encrypted (requiring a passphrase to use), the user will be
50
- # prompted to enter their password unless passphrase works.
51
- def load_data_private_key(data, passphrase=nil, ask_passphrase=true, filename="", prompt=Prompt.default)
52
- key_read, error_classes = classify_key(data, filename)
30
+ class <<self
31
+ # Fetch an OpenSSL key instance by its SSH name. It will be a new,
32
+ # empty key of the given type.
33
+ def get(name)
34
+ MAP.fetch(name).new
35
+ end
53
36
 
54
- encrypted_key = data.match(/ENCRYPTED/)
55
- tries = 0
37
+ # Loads a private key from a file. It will correctly determine
38
+ # whether the file describes an RSA or DSA key, and will load it
39
+ # appropriately. The new key is returned. If the key itself is
40
+ # encrypted (requiring a passphrase to use), the user will be
41
+ # prompted to enter their password unless passphrase works.
42
+ def load_private_key(filename, passphrase=nil, ask_passphrase=true, prompt=Prompt.default)
43
+ data = File.read(File.expand_path(filename))
44
+ load_data_private_key(data, passphrase, ask_passphrase, filename, prompt)
45
+ end
56
46
 
57
- prompter = nil
58
- result =
59
- begin
60
- key_read[data, passphrase || 'invalid']
61
- rescue *error_classes
62
- if encrypted_key && ask_passphrase
63
- tries += 1
64
- if tries <= 3
65
- prompter ||= prompt.start(type: 'private_key', filename: filename, sha: Digest::SHA256.digest(data))
66
- passphrase = prompter.ask("Enter passphrase for #{filename}:", false)
67
- retry
47
+ # Loads a private key. It will correctly determine
48
+ # whether the file describes an RSA or DSA key, and will load it
49
+ # appropriately. The new key is returned. If the key itself is
50
+ # encrypted (requiring a passphrase to use), the user will be
51
+ # prompted to enter their password unless passphrase works.
52
+ def load_data_private_key(data, passphrase=nil, ask_passphrase=true, filename="", prompt=Prompt.default)
53
+ key_read, error_classes = classify_key(data, filename)
54
+
55
+ encrypted_key = data.match(/ENCRYPTED/)
56
+ tries = 0
57
+
58
+ prompter = nil
59
+ result =
60
+ begin
61
+ key_read[data, passphrase || 'invalid']
62
+ rescue *error_classes
63
+ if encrypted_key && ask_passphrase
64
+ tries += 1
65
+ if tries <= 3
66
+ prompter ||= prompt.start(type: 'private_key', filename: filename, sha: Digest::SHA256.digest(data))
67
+ passphrase = prompter.ask("Enter passphrase for #{filename}:", false)
68
+ retry
69
+ else
70
+ raise
71
+ end
68
72
  else
69
73
  raise
70
74
  end
71
- else
72
- raise
73
75
  end
74
- end
75
- prompter.success if prompter
76
- result
77
- end
76
+ prompter.success if prompter
77
+ result
78
+ end
78
79
 
79
- # Loads a public key from a file. It will correctly determine whether
80
- # the file describes an RSA or DSA key, and will load it
81
- # appropriately. The new public key is returned.
82
- def load_public_key(filename)
83
- data = File.read(File.expand_path(filename))
84
- load_data_public_key(data, filename)
85
- end
80
+ # Loads a public key from a file. It will correctly determine whether
81
+ # the file describes an RSA or DSA key, and will load it
82
+ # appropriately. The new public key is returned.
83
+ def load_public_key(filename)
84
+ data = File.read(File.expand_path(filename))
85
+ load_data_public_key(data, filename)
86
+ end
86
87
 
87
- # Loads a public key. It will correctly determine whether
88
- # the file describes an RSA or DSA key, and will load it
89
- # appropriately. The new public key is returned.
90
- def load_data_public_key(data, filename="")
91
- fields = data.split(/ /)
88
+ # Loads a public key. It will correctly determine whether
89
+ # the file describes an RSA or DSA key, and will load it
90
+ # appropriately. The new public key is returned.
91
+ def load_data_public_key(data, filename="")
92
+ fields = data.split(/ /)
92
93
 
93
- blob = nil
94
- begin
94
+ blob = nil
95
+ begin
96
+ blob = fields.shift
97
+ end while !blob.nil? && !/^(ssh-(rsa|dss|ed25519)|ecdsa-sha2-nistp\d+)(-cert-v01@openssh\.com)?$/.match(blob)
95
98
  blob = fields.shift
96
- end while !blob.nil? && !/^(ssh-(rsa|dss|ed25519)|ecdsa-sha2-nistp\d+)(-cert-v01@openssh\.com)?$/.match(blob)
97
- blob = fields.shift
98
-
99
- raise Net::SSH::Exception, "public key at #{filename} is not valid" if blob.nil?
100
99
 
101
- blob = blob.unpack("m*").first
102
- reader = Net::SSH::Buffer.new(blob)
103
- reader.read_key or raise OpenSSL::PKey::PKeyError, "not a public key #{filename.inspect}"
104
- end
100
+ raise Net::SSH::Exception, "public key at #{filename} is not valid" if blob.nil?
105
101
 
106
- private
102
+ blob = blob.unpack("m*").first
103
+ reader = Net::SSH::Buffer.new(blob)
104
+ reader.read_key or raise OpenSSL::PKey::PKeyError, "not a public key #{filename.inspect}"
105
+ end
107
106
 
108
- # Determine whether the file describes an RSA or DSA key, and return how load it
109
- # appropriately.
110
- def classify_key(data, filename)
111
- if data.match(/-----BEGIN OPENSSH PRIVATE KEY-----/)
112
- Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("OpenSSH keys only supported if ED25519 is available")
113
- return ->(key_data, passphrase) { Net::SSH::Authentication::ED25519::PrivKey.read(key_data, passphrase) }, [ArgumentError]
114
- elsif OpenSSL::PKey.respond_to?(:read)
115
- return ->(key_data, passphrase) { OpenSSL::PKey.read(key_data, passphrase) }, [ArgumentError, OpenSSL::PKey::PKeyError]
116
- elsif data.match(/-----BEGIN DSA PRIVATE KEY-----/)
117
- return ->(key_data, passphrase) { OpenSSL::PKey::DSA.new(key_data, passphrase) }, [OpenSSL::PKey::DSAError]
118
- elsif data.match(/-----BEGIN RSA PRIVATE KEY-----/)
119
- return ->(key_data, passphrase) { OpenSSL::PKey::RSA.new(key_data, passphrase) }, [OpenSSL::PKey::RSAError]
120
- elsif data.match(/-----BEGIN EC PRIVATE KEY-----/) && defined?(OpenSSL::PKey::EC)
121
- return ->(key_data, passphrase) { OpenSSL::PKey::EC.new(key_data, passphrase) }, [OpenSSL::PKey::ECError]
122
- elsif data.match(/-----BEGIN (.+) PRIVATE KEY-----/)
123
- raise OpenSSL::PKey::PKeyError, "not a supported key type '#{$1}'"
124
- else
125
- raise OpenSSL::PKey::PKeyError, "not a private key (#{filename})"
107
+ private
108
+
109
+ # Determine whether the file describes an RSA or DSA key, and return how load it
110
+ # appropriately.
111
+ def classify_key(data, filename)
112
+ if data.match(/-----BEGIN OPENSSH PRIVATE KEY-----/)
113
+ Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("OpenSSH keys only supported if ED25519 is available")
114
+ return ->(key_data, passphrase) { Net::SSH::Authentication::ED25519::PrivKey.read(key_data, passphrase) }, [ArgumentError]
115
+ elsif OpenSSL::PKey.respond_to?(:read)
116
+ return ->(key_data, passphrase) { OpenSSL::PKey.read(key_data, passphrase) }, [ArgumentError, OpenSSL::PKey::PKeyError]
117
+ elsif data.match(/-----BEGIN DSA PRIVATE KEY-----/)
118
+ return ->(key_data, passphrase) { OpenSSL::PKey::DSA.new(key_data, passphrase) }, [OpenSSL::PKey::DSAError]
119
+ elsif data.match(/-----BEGIN RSA PRIVATE KEY-----/)
120
+ return ->(key_data, passphrase) { OpenSSL::PKey::RSA.new(key_data, passphrase) }, [OpenSSL::PKey::RSAError]
121
+ elsif data.match(/-----BEGIN EC PRIVATE KEY-----/) && defined?(OpenSSL::PKey::EC)
122
+ return ->(key_data, passphrase) { OpenSSL::PKey::EC.new(key_data, passphrase) }, [OpenSSL::PKey::ECError]
123
+ elsif data.match(/-----BEGIN (.+) PRIVATE KEY-----/)
124
+ raise OpenSSL::PKey::PKeyError, "not a supported key type '#{$1}'"
125
+ else
126
+ raise OpenSSL::PKey::PKeyError, "not a private key (#{filename})"
127
+ end
126
128
  end
127
129
  end
128
130
  end
129
-
130
131
  end
131
-
132
- end; end
132
+ end
@@ -3,185 +3,179 @@ require 'openssl'
3
3
  require 'base64'
4
4
  require 'net/ssh/buffer'
5
5
 
6
- module Net; module SSH
7
-
8
- # Represents the result of a search in known hosts
9
- # see search_for
10
- class HostKeys
11
- include Enumerable
12
- attr_reader :host
13
-
14
- def initialize(host_keys, host, known_hosts, options = {})
15
- @host_keys = host_keys
16
- @host = host
17
- @known_hosts = known_hosts
18
- @options = options
19
- end
6
+ module Net
7
+ module SSH
8
+
9
+ # Represents the result of a search in known hosts
10
+ # see search_for
11
+ class HostKeys
12
+ include Enumerable
13
+ attr_reader :host
14
+
15
+ def initialize(host_keys, host, known_hosts, options = {})
16
+ @host_keys = host_keys
17
+ @host = host
18
+ @known_hosts = known_hosts
19
+ @options = options
20
+ end
20
21
 
21
- def add_host_key(key)
22
- @known_hosts.add(@host, key, @options)
23
- @host_keys.push(key)
24
- end
22
+ def add_host_key(key)
23
+ @known_hosts.add(@host, key, @options)
24
+ @host_keys.push(key)
25
+ end
25
26
 
26
- def each(&block)
27
- @host_keys.each(&block)
28
- end
27
+ def each(&block)
28
+ @host_keys.each(&block)
29
+ end
29
30
 
30
- def empty?
31
- @host_keys.empty?
31
+ def empty?
32
+ @host_keys.empty?
33
+ end
32
34
  end
33
- end
34
35
 
35
- # Searches an OpenSSH-style known-host file for a given host, and returns all
36
- # matching keys. This is used to implement host-key verification, as well as
37
- # to determine what key a user prefers to use for a given host.
38
- #
39
- # This is used internally by Net::SSH, and will never need to be used directly
40
- # by consumers of the library.
41
- class KnownHosts
42
-
43
- if defined?(OpenSSL::PKey::EC)
44
- SUPPORTED_TYPE = %w(ssh-rsa ssh-dss
45
- ecdsa-sha2-nistp256
46
- ecdsa-sha2-nistp384
47
- ecdsa-sha2-nistp521)
48
- else
49
- SUPPORTED_TYPE = %w(ssh-rsa ssh-dss)
50
- end
36
+ # Searches an OpenSSH-style known-host file for a given host, and returns all
37
+ # matching keys. This is used to implement host-key verification, as well as
38
+ # to determine what key a user prefers to use for a given host.
39
+ #
40
+ # This is used internally by Net::SSH, and will never need to be used directly
41
+ # by consumers of the library.
42
+ class KnownHosts
43
+ if defined?(OpenSSL::PKey::EC)
44
+ SUPPORTED_TYPE = %w[ssh-rsa ssh-dss
45
+ ecdsa-sha2-nistp256
46
+ ecdsa-sha2-nistp384
47
+ ecdsa-sha2-nistp521]
48
+ else
49
+ SUPPORTED_TYPE = %w[ssh-rsa ssh-dss]
50
+ end
51
51
 
52
+ class <<self
53
+ # Searches all known host files (see KnownHosts.hostfiles) for all keys
54
+ # of the given host. Returns an enumerable of keys found.
55
+ def search_for(host, options={})
56
+ HostKeys.new(search_in(hostfiles(options), host), host, self, options)
57
+ end
52
58
 
53
- class <<self
59
+ # Search for all known keys for the given host, in every file given in
60
+ # the +files+ array. Returns the list of keys.
61
+ def search_in(files, host)
62
+ files.flat_map { |file| KnownHosts.new(file).keys_for(host) }
63
+ end
54
64
 
55
- # Searches all known host files (see KnownHosts.hostfiles) for all keys
56
- # of the given host. Returns an enumerable of keys found.
57
- def search_for(host, options={})
58
- HostKeys.new(search_in(hostfiles(options), host), host, self, options)
59
- end
65
+ # Looks in the given +options+ hash for the :user_known_hosts_file and
66
+ # :global_known_hosts_file keys, and returns an array of all known
67
+ # hosts files. If the :user_known_hosts_file key is not set, the
68
+ # default is returned (~/.ssh/known_hosts and ~/.ssh/known_hosts2). If
69
+ # :global_known_hosts_file is not set, the default is used
70
+ # (/etc/ssh/ssh_known_hosts and /etc/ssh/ssh_known_hosts2).
71
+ #
72
+ # If you only want the user known host files, you can pass :user as
73
+ # the second option.
74
+ def hostfiles(options, which=:all)
75
+ files = []
60
76
 
61
- # Search for all known keys for the given host, in every file given in
62
- # the +files+ array. Returns the list of keys.
63
- def search_in(files, host)
64
- files.map { |file| KnownHosts.new(file).keys_for(host) }.flatten
65
- end
77
+ files += Array(options[:user_known_hosts_file] || %w[~/.ssh/known_hosts ~/.ssh/known_hosts2]) if which == :all || which == :user
66
78
 
67
- # Looks in the given +options+ hash for the :user_known_hosts_file and
68
- # :global_known_hosts_file keys, and returns an array of all known
69
- # hosts files. If the :user_known_hosts_file key is not set, the
70
- # default is returned (~/.ssh/known_hosts and ~/.ssh/known_hosts2). If
71
- # :global_known_hosts_file is not set, the default is used
72
- # (/etc/ssh/ssh_known_hosts and /etc/ssh/ssh_known_hosts2).
73
- #
74
- # If you only want the user known host files, you can pass :user as
75
- # the second option.
76
- def hostfiles(options, which=:all)
77
- files = []
79
+ files += Array(options[:global_known_hosts_file] || %w[/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2]) if which == :all || which == :global
78
80
 
79
- if which == :all || which == :user
80
- files += Array(options[:user_known_hosts_file] || %w(~/.ssh/known_hosts ~/.ssh/known_hosts2))
81
+ return files
81
82
  end
82
83
 
83
- if which == :all || which == :global
84
- files += Array(options[:global_known_hosts_file] || %w(/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2))
85
- end
86
-
87
- return files
88
- end
89
-
90
- # Looks in all user known host files (see KnownHosts.hostfiles) and tries to
91
- # add an entry for the given host and key to the first file it is able
92
- # to.
93
- def add(host, key, options={})
94
- hostfiles(options, :user).each do |file|
95
- begin
96
- KnownHosts.new(file).add(host, key)
97
- return
98
- rescue SystemCallError
99
- # try the next hostfile
84
+ # Looks in all user known host files (see KnownHosts.hostfiles) and tries to
85
+ # add an entry for the given host and key to the first file it is able
86
+ # to.
87
+ def add(host, key, options={})
88
+ hostfiles(options, :user).each do |file|
89
+ begin
90
+ KnownHosts.new(file).add(host, key)
91
+ return
92
+ rescue SystemCallError
93
+ # try the next hostfile
94
+ end
100
95
  end
101
96
  end
102
97
  end
103
- end
104
98
 
105
- # The host-key file name that this KnownHosts instance will use to search
106
- # for keys.
107
- attr_reader :source
99
+ # The host-key file name that this KnownHosts instance will use to search
100
+ # for keys.
101
+ attr_reader :source
108
102
 
109
- # Instantiate a new KnownHosts instance that will search the given known-hosts
110
- # file. The path is expanded file File.expand_path.
111
- def initialize(source)
112
- @source = File.expand_path(source)
113
- end
103
+ # Instantiate a new KnownHosts instance that will search the given known-hosts
104
+ # file. The path is expanded file File.expand_path.
105
+ def initialize(source)
106
+ @source = File.expand_path(source)
107
+ end
114
108
 
115
- # Returns an array of all keys that are known to be associatd with the
116
- # given host. The +host+ parameter is either the domain name or ip address
117
- # of the host, or both (comma-separated). Additionally, if a non-standard
118
- # port is being used, it may be specified by putting the host (or ip, or
119
- # both) in square brackets, and appending the port outside the brackets
120
- # after a colon. Possible formats for +host+, then, are;
121
- #
122
- # "net.ssh.test"
123
- # "1.2.3.4"
124
- # "net.ssh.test,1.2.3.4"
125
- # "[net.ssh.test]:5555"
126
- # "[1,2,3,4]:5555"
127
- # "[net.ssh.test]:5555,[1.2.3.4]:5555
128
- def keys_for(host)
129
- keys = []
130
- return keys unless File.readable?(source)
131
-
132
- entries = host.split(/,/)
133
-
134
- File.open(source) do |file|
135
- scanner = StringScanner.new("")
136
- file.each_line do |line|
137
- scanner.string = line
138
-
139
- scanner.skip(/\s*/)
140
- next if scanner.match?(/$|#/)
141
-
142
- hostlist = scanner.scan(/\S+/).split(/,/)
143
- found = entries.all? { |entry| hostlist.include?(entry) } ||
144
- known_host_hash?(hostlist, entries, scanner)
145
- next unless found
146
-
147
- scanner.skip(/\s*/)
148
- type = scanner.scan(/\S+/)
149
-
150
- next unless SUPPORTED_TYPE.include?(type)
151
-
152
- scanner.skip(/\s*/)
153
- blob = scanner.rest.unpack("m*").first
154
- keys << Net::SSH::Buffer.new(blob).read_key
109
+ # Returns an array of all keys that are known to be associatd with the
110
+ # given host. The +host+ parameter is either the domain name or ip address
111
+ # of the host, or both (comma-separated). Additionally, if a non-standard
112
+ # port is being used, it may be specified by putting the host (or ip, or
113
+ # both) in square brackets, and appending the port outside the brackets
114
+ # after a colon. Possible formats for +host+, then, are;
115
+ #
116
+ # "net.ssh.test"
117
+ # "1.2.3.4"
118
+ # "net.ssh.test,1.2.3.4"
119
+ # "[net.ssh.test]:5555"
120
+ # "[1,2,3,4]:5555"
121
+ # "[net.ssh.test]:5555,[1.2.3.4]:5555
122
+ def keys_for(host)
123
+ keys = []
124
+ return keys unless File.readable?(source)
125
+
126
+ entries = host.split(/,/)
127
+
128
+ File.open(source) do |file|
129
+ scanner = StringScanner.new("")
130
+ file.each_line do |line|
131
+ scanner.string = line
132
+
133
+ scanner.skip(/\s*/)
134
+ next if scanner.match?(/$|#/)
135
+
136
+ hostlist = scanner.scan(/\S+/).split(/,/)
137
+ found = entries.all? { |entry| hostlist.include?(entry) } ||
138
+ known_host_hash?(hostlist, entries)
139
+ next unless found
140
+
141
+ scanner.skip(/\s*/)
142
+ type = scanner.scan(/\S+/)
143
+
144
+ next unless SUPPORTED_TYPE.include?(type)
145
+
146
+ scanner.skip(/\s*/)
147
+ blob = scanner.rest.unpack("m*").first
148
+ keys << Net::SSH::Buffer.new(blob).read_key
149
+ end
155
150
  end
156
- end
157
151
 
158
- keys
159
- end
152
+ keys
153
+ end
160
154
 
161
- # Indicates whether one of the entries matches an hostname that has been
162
- # stored as a HMAC-SHA1 hash in the known hosts.
163
- def known_host_hash?(hostlist, entries, scanner)
164
- if hostlist.size == 1 && hostlist.first =~ /\A\|1(\|.+){2}\z/
165
- chunks = hostlist.first.split(/\|/)
166
- salt = Base64.decode64(chunks[2])
167
- digest = OpenSSL::Digest.new('sha1')
168
- entries.each do |entry|
169
- hmac = OpenSSL::HMAC.digest(digest, salt, entry)
170
- return true if Base64.encode64(hmac).chomp == chunks[3]
155
+ # Indicates whether one of the entries matches an hostname that has been
156
+ # stored as a HMAC-SHA1 hash in the known hosts.
157
+ def known_host_hash?(hostlist, entries)
158
+ if hostlist.size == 1 && hostlist.first =~ /\A\|1(\|.+){2}\z/
159
+ chunks = hostlist.first.split(/\|/)
160
+ salt = Base64.decode64(chunks[2])
161
+ digest = OpenSSL::Digest.new('sha1')
162
+ entries.each do |entry|
163
+ hmac = OpenSSL::HMAC.digest(digest, salt, entry)
164
+ return true if Base64.encode64(hmac).chomp == chunks[3]
165
+ end
171
166
  end
167
+ false
172
168
  end
173
- false
174
- end
175
169
 
176
- # Tries to append an entry to the current source file for the given host
177
- # and key. If it is unable to (because the file is not writable, for
178
- # instance), an exception will be raised.
179
- def add(host, key)
180
- File.open(source, "a") do |file|
181
- blob = [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").gsub(/\s/, "")
182
- file.puts "#{host} #{key.ssh_type} #{blob}"
170
+ # Tries to append an entry to the current source file for the given host
171
+ # and key. If it is unable to (because the file is not writable, for
172
+ # instance), an exception will be raised.
173
+ def add(host, key)
174
+ File.open(source, "a") do |file|
175
+ blob = [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").gsub(/\s/, "")
176
+ file.puts "#{host} #{key.ssh_type} #{blob}"
177
+ end
183
178
  end
184
179
  end
185
-
186
180
  end
187
- end; end
181
+ end