net-ssh 5.0.0.beta1 → 5.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -2,143 +2,145 @@ require 'net/ssh/buffer'
2
2
  require 'net/ssh/loggable'
3
3
  require 'net/ssh/ruby_compat'
4
4
 
5
- module Net; module SSH
6
-
7
- # This module is used to extend sockets and other IO objects, to allow
8
- # them to be buffered for both read and write. This abstraction makes it
9
- # quite easy to write a select-based event loop
10
- # (see Net::SSH::Connection::Session#listen_to).
11
- #
12
- # The general idea is that instead of calling #read directly on an IO that
13
- # has been extended with this module, you call #fill (to add pending input
14
- # to the internal read buffer), and then #read_available (to read from that
15
- # buffer). Likewise, you don't call #write directly, you call #enqueue to
16
- # add data to the write buffer, and then #send_pending or #wait_for_pending_sends
17
- # to actually send the data across the wire.
18
- #
19
- # In this way you can easily use the object as an argument to IO.select,
20
- # calling #fill when it is available for read, or #send_pending when it is
21
- # available for write, and then call #enqueue and #read_available during
22
- # the idle times.
23
- #
24
- # socket = TCPSocket.new(address, port)
25
- # socket.extend(Net::SSH::BufferedIo)
26
- #
27
- # ssh.listen_to(socket)
28
- #
29
- # ssh.loop do
30
- # if socket.available > 0
31
- # puts socket.read_available
32
- # socket.enqueue("response\n")
33
- # end
34
- # end
35
- #
36
- # Note that this module must be used to extend an instance, and should not
37
- # be included in a class. If you do want to use it via an include, then you
38
- # must make sure to invoke the private #initialize_buffered_io method in
39
- # your class' #initialize method:
40
- #
41
- # class Foo < IO
42
- # include Net::SSH::BufferedIo
43
- #
44
- # def initialize
45
- # initialize_buffered_io
46
- # # ...
47
- # end
48
- # end
49
- module BufferedIo
50
- include Loggable
51
-
52
- # Called when the #extend is called on an object, with this module as the
53
- # argument. It ensures that the modules instance variables are all properly
54
- # initialized.
55
- def self.extended(object) #:nodoc:
56
- # need to use __send__ because #send is overridden in Socket
57
- object.__send__(:initialize_buffered_io)
58
- end
59
-
60
- # Tries to read up to +n+ bytes of data from the remote end, and appends
61
- # the data to the input buffer. It returns the number of bytes read, or 0
62
- # if no data was available to be read.
63
- def fill(n=8192)
64
- input.consume!
65
- data = recv(n)
66
- debug { "read #{data.length} bytes" }
67
- input.append(data)
68
- return data.length
69
- rescue EOFError => e
70
- @input_errors << e
71
- return 0
72
- end
73
-
74
- # Read up to +length+ bytes from the input buffer. If +length+ is nil,
75
- # all available data is read from the buffer. (See #available.)
76
- def read_available(length=nil)
77
- input.read(length || available)
78
- end
79
-
80
- # Returns the number of bytes available to be read from the input buffer.
81
- # (See #read_available.)
82
- def available
83
- input.available
84
- end
85
-
86
- # Enqueues data in the output buffer, to be written when #send_pending
87
- # is called. Note that the data is _not_ sent immediately by this method!
88
- def enqueue(data)
89
- output.append(data)
90
- end
91
-
92
- # Returns +true+ if there is data waiting in the output buffer, and
93
- # +false+ otherwise.
94
- def pending_write?
95
- output.length > 0
96
- end
97
-
98
- # Sends as much of the pending output as possible. Returns +true+ if any
99
- # data was sent, and +false+ otherwise.
100
- def send_pending
101
- if output.length > 0
102
- sent = send(output.to_s, 0)
103
- debug { "sent #{sent} bytes" }
104
- output.consume!(sent)
105
- return sent > 0
106
- else
107
- return false
5
+ module Net
6
+ module SSH
7
+
8
+ # This module is used to extend sockets and other IO objects, to allow
9
+ # them to be buffered for both read and write. This abstraction makes it
10
+ # quite easy to write a select-based event loop
11
+ # (see Net::SSH::Connection::Session#listen_to).
12
+ #
13
+ # The general idea is that instead of calling #read directly on an IO that
14
+ # has been extended with this module, you call #fill (to add pending input
15
+ # to the internal read buffer), and then #read_available (to read from that
16
+ # buffer). Likewise, you don't call #write directly, you call #enqueue to
17
+ # add data to the write buffer, and then #send_pending or #wait_for_pending_sends
18
+ # to actually send the data across the wire.
19
+ #
20
+ # In this way you can easily use the object as an argument to IO.select,
21
+ # calling #fill when it is available for read, or #send_pending when it is
22
+ # available for write, and then call #enqueue and #read_available during
23
+ # the idle times.
24
+ #
25
+ # socket = TCPSocket.new(address, port)
26
+ # socket.extend(Net::SSH::BufferedIo)
27
+ #
28
+ # ssh.listen_to(socket)
29
+ #
30
+ # ssh.loop do
31
+ # if socket.available > 0
32
+ # puts socket.read_available
33
+ # socket.enqueue("response\n")
34
+ # end
35
+ # end
36
+ #
37
+ # Note that this module must be used to extend an instance, and should not
38
+ # be included in a class. If you do want to use it via an include, then you
39
+ # must make sure to invoke the private #initialize_buffered_io method in
40
+ # your class' #initialize method:
41
+ #
42
+ # class Foo < IO
43
+ # include Net::SSH::BufferedIo
44
+ #
45
+ # def initialize
46
+ # initialize_buffered_io
47
+ # # ...
48
+ # end
49
+ # end
50
+ module BufferedIo
51
+ include Loggable
52
+
53
+ # Called when the #extend is called on an object, with this module as the
54
+ # argument. It ensures that the modules instance variables are all properly
55
+ # initialized.
56
+ def self.extended(object) #:nodoc:
57
+ # need to use __send__ because #send is overridden in Socket
58
+ object.__send__(:initialize_buffered_io)
108
59
  end
109
- end
110
-
111
- # Calls #send_pending repeatedly, if necessary, blocking until the output
112
- # buffer is empty.
113
- def wait_for_pending_sends
114
- send_pending
115
- while output.length > 0
116
- result = IO.select(nil, [self]) or next
117
- next unless result[1].any?
60
+
61
+ # Tries to read up to +n+ bytes of data from the remote end, and appends
62
+ # the data to the input buffer. It returns the number of bytes read, or 0
63
+ # if no data was available to be read.
64
+ def fill(n=8192)
65
+ input.consume!
66
+ data = recv(n)
67
+ debug { "read #{data.length} bytes" }
68
+ input.append(data)
69
+ return data.length
70
+ rescue EOFError => e
71
+ @input_errors << e
72
+ return 0
73
+ end
74
+
75
+ # Read up to +length+ bytes from the input buffer. If +length+ is nil,
76
+ # all available data is read from the buffer. (See #available.)
77
+ def read_available(length=nil)
78
+ input.read(length || available)
79
+ end
80
+
81
+ # Returns the number of bytes available to be read from the input buffer.
82
+ # (See #read_available.)
83
+ def available
84
+ input.available
85
+ end
86
+
87
+ # Enqueues data in the output buffer, to be written when #send_pending
88
+ # is called. Note that the data is _not_ sent immediately by this method!
89
+ def enqueue(data)
90
+ output.append(data)
91
+ end
92
+
93
+ # Returns +true+ if there is data waiting in the output buffer, and
94
+ # +false+ otherwise.
95
+ def pending_write?
96
+ output.length > 0
97
+ end
98
+
99
+ # Sends as much of the pending output as possible. Returns +true+ if any
100
+ # data was sent, and +false+ otherwise.
101
+ def send_pending
102
+ if output.length > 0
103
+ sent = send(output.to_s, 0)
104
+ debug { "sent #{sent} bytes" }
105
+ output.consume!(sent)
106
+ return sent > 0
107
+ else
108
+ return false
109
+ end
110
+ end
111
+
112
+ # Calls #send_pending repeatedly, if necessary, blocking until the output
113
+ # buffer is empty.
114
+ def wait_for_pending_sends
118
115
  send_pending
116
+ while output.length > 0
117
+ result = IO.select(nil, [self]) or next
118
+ next unless result[1].any?
119
+ send_pending
120
+ end
119
121
  end
120
- end
121
-
122
- public # these methods are primarily for use in tests
123
-
122
+
123
+ public # these methods are primarily for use in tests
124
+
124
125
  def write_buffer #:nodoc:
125
126
  output.to_s
126
127
  end
127
-
128
+
128
129
  def read_buffer #:nodoc:
129
130
  input.to_s
130
131
  end
131
-
132
- private
133
-
132
+
133
+ private
134
+
134
135
  #--
135
136
  # Can't use attr_reader here (after +private+) without incurring the
136
137
  # wrath of "ruby -w". We hates it.
137
138
  #++
138
-
139
+
139
140
  def input; @input; end
140
- def output; @output; end
141
141
 
142
+ def output; @output; end
143
+
142
144
  # Initializes the intput and output buffers for this object. This method
143
145
  # is called automatically when the module is mixed into an object via
144
146
  # Object#extend (see Net::SSH::BufferedIo.extended), but must be called
@@ -150,54 +152,53 @@ module Net; module SSH
150
152
  @output = Net::SSH::Buffer.new
151
153
  @output_errors = []
152
154
  end
153
- end
154
-
155
-
155
+ end
156
156
 
157
- # Fixes for two issues by Miklós Fazekas:
158
- #
159
- # * if client closes a forwarded connection, but the server is
160
- # reading, net-ssh terminates with IOError socket closed.
161
- # * if client force closes (RST) a forwarded connection, but
162
- # server is reading, net-ssh terminates with [an exception]
163
- #
164
- # See:
165
- #
166
- # http://net-ssh.lighthouseapp.com/projects/36253/tickets/7
167
- # http://github.com/net-ssh/net-ssh/tree/portfwfix
168
- #
169
- module ForwardedBufferedIo
170
- def fill(n=8192)
171
- begin
172
- super(n)
173
- rescue Errno::ECONNRESET => e
174
- debug { "connection was reset => shallowing exception:#{e}" }
175
- return 0
176
- rescue IOError => e
177
- if e.message =~ /closed/ then
157
+ # Fixes for two issues by Miklós Fazekas:
158
+ #
159
+ # * if client closes a forwarded connection, but the server is
160
+ # reading, net-ssh terminates with IOError socket closed.
161
+ # * if client force closes (RST) a forwarded connection, but
162
+ # server is reading, net-ssh terminates with [an exception]
163
+ #
164
+ # See:
165
+ #
166
+ # http://net-ssh.lighthouseapp.com/projects/36253/tickets/7
167
+ # http://github.com/net-ssh/net-ssh/tree/portfwfix
168
+ #
169
+ module ForwardedBufferedIo
170
+ def fill(n=8192)
171
+ begin
172
+ super(n)
173
+ rescue Errno::ECONNRESET => e
178
174
  debug { "connection was reset => shallowing exception:#{e}" }
179
175
  return 0
180
- else
181
- raise
176
+ rescue IOError => e
177
+ if e.message =~ /closed/ then
178
+ debug { "connection was reset => shallowing exception:#{e}" }
179
+ return 0
180
+ else
181
+ raise
182
+ end
182
183
  end
183
184
  end
184
- end
185
-
186
- def send_pending
187
- begin
188
- super
189
- rescue Errno::ECONNRESET => e
190
- debug { "connection was reset => shallowing exception:#{e}" }
191
- return 0
192
- rescue IOError => e
193
- if e.message =~ /closed/ then
185
+
186
+ def send_pending
187
+ begin
188
+ super
189
+ rescue Errno::ECONNRESET => e
194
190
  debug { "connection was reset => shallowing exception:#{e}" }
195
191
  return 0
196
- else
197
- raise
192
+ rescue IOError => e
193
+ if e.message =~ /closed/ then
194
+ debug { "connection was reset => shallowing exception:#{e}" }
195
+ return 0
196
+ else
197
+ raise
198
+ end
198
199
  end
199
200
  end
200
201
  end
201
- end
202
202
 
203
- end; end
203
+ end
204
+ end
@@ -1,189 +1,190 @@
1
- module Net; module SSH
1
+ module Net
2
+ module SSH
2
3
 
3
- # The Net::SSH::Config class is used to parse OpenSSH configuration files,
4
- # and translates that syntax into the configuration syntax that Net::SSH
5
- # understands. This lets Net::SSH scripts read their configuration (to
6
- # some extent) from OpenSSH configuration files (~/.ssh/config, /etc/ssh_config,
7
- # and so forth).
8
- #
9
- # Only a subset of OpenSSH configuration options are understood:
10
- #
11
- # * ChallengeResponseAuthentication => maps to the :auth_methods option challenge-response (then coleasced into keyboard-interactive)
12
- # * KbdInteractiveAuthentication => maps to the :auth_methods keyboard-interactive
13
- # * Ciphers => maps to the :encryption option
14
- # * Compression => :compression
15
- # * CompressionLevel => :compression_level
16
- # * ConnectTimeout => maps to the :timeout option
17
- # * ForwardAgent => :forward_agent
18
- # * GlobalKnownHostsFile => :global_known_hosts_file
19
- # * HostBasedAuthentication => maps to the :auth_methods option
20
- # * HostKeyAlgorithms => maps to :host_key option
21
- # * HostKeyAlias => :host_key_alias
22
- # * HostName => :host_name
23
- # * IdentityFile => maps to the :keys option
24
- # * IdentitiesOnly => :keys_only
25
- # * Macs => maps to the :hmac option
26
- # * PasswordAuthentication => maps to the :auth_methods option password
27
- # * Port => :port
28
- # * PreferredAuthentications => maps to the :auth_methods option
29
- # * ProxyCommand => maps to the :proxy option
30
- # * ProxyJump => maps to the :proxy option
31
- # * PubKeyAuthentication => maps to the :auth_methods option
32
- # * RekeyLimit => :rekey_limit
33
- # * User => :user
34
- # * UserKnownHostsFile => :user_known_hosts_file
35
- # * NumberOfPasswordPrompts => :number_of_password_prompts
36
- #
37
- # Note that you will never need to use this class directly--you can control
38
- # whether the OpenSSH configuration files are read by passing the :config
39
- # option to Net::SSH.start. (They are, by default.)
40
- class Config
41
- class << self
42
- @@default_files = %w(~/.ssh/config /etc/ssh_config /etc/ssh/ssh_config)
43
- # The following defaults follow the openssh client ssh_config defaults.
44
- # http://lwn.net/Articles/544640/
45
- # "hostbased" is off and "none" is not supported but we allow it since
46
- # it's used by some clients to query the server for allowed auth methods
47
- @@default_auth_methods = %w(none publickey password keyboard-interactive)
4
+ # The Net::SSH::Config class is used to parse OpenSSH configuration files,
5
+ # and translates that syntax into the configuration syntax that Net::SSH
6
+ # understands. This lets Net::SSH scripts read their configuration (to
7
+ # some extent) from OpenSSH configuration files (~/.ssh/config, /etc/ssh_config,
8
+ # and so forth).
9
+ #
10
+ # Only a subset of OpenSSH configuration options are understood:
11
+ #
12
+ # * ChallengeResponseAuthentication => maps to the :auth_methods option challenge-response (then coleasced into keyboard-interactive)
13
+ # * KbdInteractiveAuthentication => maps to the :auth_methods keyboard-interactive
14
+ # * Ciphers => maps to the :encryption option
15
+ # * Compression => :compression
16
+ # * CompressionLevel => :compression_level
17
+ # * ConnectTimeout => maps to the :timeout option
18
+ # * ForwardAgent => :forward_agent
19
+ # * GlobalKnownHostsFile => :global_known_hosts_file
20
+ # * HostBasedAuthentication => maps to the :auth_methods option
21
+ # * HostKeyAlgorithms => maps to :host_key option
22
+ # * HostKeyAlias => :host_key_alias
23
+ # * HostName => :host_name
24
+ # * IdentityFile => maps to the :keys option
25
+ # * IdentitiesOnly => :keys_only
26
+ # * Macs => maps to the :hmac option
27
+ # * PasswordAuthentication => maps to the :auth_methods option password
28
+ # * Port => :port
29
+ # * PreferredAuthentications => maps to the :auth_methods option
30
+ # * ProxyCommand => maps to the :proxy option
31
+ # * ProxyJump => maps to the :proxy option
32
+ # * PubKeyAuthentication => maps to the :auth_methods option
33
+ # * RekeyLimit => :rekey_limit
34
+ # * User => :user
35
+ # * UserKnownHostsFile => :user_known_hosts_file
36
+ # * NumberOfPasswordPrompts => :number_of_password_prompts
37
+ # * FingerprintHash => :fingerprint_hash
38
+ #
39
+ # Note that you will never need to use this class directly--you can control
40
+ # whether the OpenSSH configuration files are read by passing the :config
41
+ # option to Net::SSH.start. (They are, by default.)
42
+ class Config
43
+ class << self
44
+ @@default_files = %w[~/.ssh/config /etc/ssh_config /etc/ssh/ssh_config]
45
+ # The following defaults follow the openssh client ssh_config defaults.
46
+ # http://lwn.net/Articles/544640/
47
+ # "hostbased" is off and "none" is not supported but we allow it since
48
+ # it's used by some clients to query the server for allowed auth methods
49
+ @@default_auth_methods = %w[none publickey password keyboard-interactive]
48
50
 
49
- # Returns an array of locations of OpenSSH configuration files
50
- # to parse by default.
51
- def default_files
52
- @@default_files.clone
53
- end
51
+ # Returns an array of locations of OpenSSH configuration files
52
+ # to parse by default.
53
+ def default_files
54
+ @@default_files.clone
55
+ end
54
56
 
55
- def default_auth_methods
56
- @@default_auth_methods.clone
57
- end
57
+ def default_auth_methods
58
+ @@default_auth_methods.clone
59
+ end
58
60
 
59
- # Loads the configuration data for the given +host+ from all of the
60
- # given +files+ (defaulting to the list of files returned by
61
- # #default_files), translates the resulting hash into the options
62
- # recognized by Net::SSH, and returns them.
63
- def for(host, files=expandable_default_files)
64
- translate(files.inject({}) { |settings, file|
65
- load(file, host, settings)
66
- })
67
- end
61
+ # Loads the configuration data for the given +host+ from all of the
62
+ # given +files+ (defaulting to the list of files returned by
63
+ # #default_files), translates the resulting hash into the options
64
+ # recognized by Net::SSH, and returns them.
65
+ def for(host, files=expandable_default_files)
66
+ translate(files.inject({}) { |settings, file|
67
+ load(file, host, settings)
68
+ })
69
+ end
68
70
 
69
- # Load the OpenSSH configuration settings in the given +file+ for the
70
- # given +host+. If +settings+ is given, the options are merged into
71
- # that hash, with existing values taking precedence over newly parsed
72
- # ones. Returns a hash containing the OpenSSH options. (See
73
- # #translate for how to convert the OpenSSH options into Net::SSH
74
- # options.)
75
- def load(path, host, settings={}, base_dir = nil)
76
- file = File.expand_path(path)
77
- base_dir ||= File.dirname(file)
78
- return settings unless File.readable?(file)
71
+ # Load the OpenSSH configuration settings in the given +file+ for the
72
+ # given +host+. If +settings+ is given, the options are merged into
73
+ # that hash, with existing values taking precedence over newly parsed
74
+ # ones. Returns a hash containing the OpenSSH options. (See
75
+ # #translate for how to convert the OpenSSH options into Net::SSH
76
+ # options.)
77
+ def load(path, host, settings={}, base_dir = nil)
78
+ file = File.expand_path(path)
79
+ base_dir ||= File.dirname(file)
80
+ return settings unless File.readable?(file)
79
81
 
80
- globals = {}
81
- block_matched = false
82
- block_seen = false
83
- IO.foreach(file) do |line|
84
- next if line =~ /^\s*(?:#.*)?$/
82
+ globals = {}
83
+ block_matched = false
84
+ block_seen = false
85
+ IO.foreach(file) do |line|
86
+ next if line =~ /^\s*(?:#.*)?$/
85
87
 
86
- if line =~ /^\s*(\S+)\s*=(.*)$/
87
- key, value = $1, $2
88
- else
89
- key, value = line.strip.split(/\s+/, 2)
90
- end
88
+ if line =~ /^\s*(\S+)\s*=(.*)$/
89
+ key, value = $1, $2
90
+ else
91
+ key, value = line.strip.split(/\s+/, 2)
92
+ end
91
93
 
92
- # silently ignore malformed entries
93
- next if value.nil?
94
+ # silently ignore malformed entries
95
+ next if value.nil?
94
96
 
95
- key.downcase!
96
- value = $1 if value =~ /^"(.*)"$/
97
+ key.downcase!
98
+ value = $1 if value =~ /^"(.*)"$/
97
99
 
98
- value = case value.strip
99
- when /^\d+$/ then value.to_i
100
- when /^no$/i then false
101
- when /^yes$/i then true
102
- else value
103
- end
100
+ value = case value.strip
101
+ when /^\d+$/ then value.to_i
102
+ when /^no$/i then false
103
+ when /^yes$/i then true
104
+ else value
105
+ end
104
106
 
105
- if key == 'host'
106
- # Support "Host host1 host2 hostN".
107
- # See http://github.com/net-ssh/net-ssh/issues#issue/6
108
- negative_hosts, positive_hosts = value.to_s.split(/\s+/).partition { |h| h.start_with?('!') }
107
+ if key == 'host'
108
+ # Support "Host host1 host2 hostN".
109
+ # See http://github.com/net-ssh/net-ssh/issues#issue/6
110
+ negative_hosts, positive_hosts = value.to_s.split(/\s+/).partition { |h| h.start_with?('!') }
109
111
 
110
- # Check for negative patterns first. If the host matches, that overrules any other positive match.
111
- # The host substring code is used to strip out the starting "!" so the regexp will be correct.
112
- negative_matched = negative_hosts.any? { |h| host =~ pattern2regex(h[1..-1]) }
112
+ # Check for negative patterns first. If the host matches, that overrules any other positive match.
113
+ # The host substring code is used to strip out the starting "!" so the regexp will be correct.
114
+ negative_matched = negative_hosts.any? { |h| host =~ pattern2regex(h[1..-1]) }
113
115
 
114
- if negative_matched
115
- block_matched = false
116
- else
117
- block_matched = positive_hosts.any? { |h| host =~ pattern2regex(h) }
118
- end
116
+ if negative_matched
117
+ block_matched = false
118
+ else
119
+ block_matched = positive_hosts.any? { |h| host =~ pattern2regex(h) }
120
+ end
119
121
 
120
- block_seen = true
121
- settings[key] = host
122
- elsif key == 'match'
123
- block_matched = eval_match_condition(value, host, settings)
124
- block_seen = true
125
- elsif !block_seen
126
- case key
127
- when 'identityfile'
128
- (globals[key] ||= []) << value
129
- when 'include'
130
- included_file_paths(base_dir, value).each do |file_path|
131
- globals = load(file_path, host, globals, base_dir)
122
+ block_seen = true
123
+ settings[key] = host
124
+ elsif key == 'match'
125
+ block_matched = eval_match_condition(value, host, settings)
126
+ block_seen = true
127
+ elsif !block_seen
128
+ case key
129
+ when 'identityfile'
130
+ (globals[key] ||= []) << value
131
+ when 'include'
132
+ included_file_paths(base_dir, value).each do |file_path|
133
+ globals = load(file_path, host, globals, base_dir)
134
+ end
135
+ else
136
+ globals[key] = value unless settings.key?(key)
137
+ end
138
+ elsif block_matched
139
+ case key
140
+ when 'identityfile'
141
+ (settings[key] ||= []) << value
142
+ when 'include'
143
+ included_file_paths(base_dir, value).each do |file_path|
144
+ settings = load(file_path, host, settings, base_dir)
145
+ end
146
+ else
147
+ settings[key] = value unless settings.key?(key)
132
148
  end
133
- else
134
- globals[key] = value unless settings.key?(key)
135
149
  end
136
- elsif block_matched
150
+ end
151
+
152
+ globals.merge(settings) do |key, oldval, newval|
137
153
  case key
138
154
  when 'identityfile'
139
- (settings[key] ||= []) << value
140
- when 'include'
141
- included_file_paths(base_dir, value).each do |file_path|
142
- settings = load(file_path, host, settings, base_dir)
143
- end
155
+ oldval + newval
144
156
  else
145
- settings[key] = value unless settings.key?(key)
157
+ newval
146
158
  end
147
159
  end
148
160
  end
149
161
 
150
- globals.merge(settings) do |key, oldval, newval|
151
- case key
152
- when 'identityfile'
153
- oldval + newval
154
- else
155
- newval
162
+ # Given a hash of OpenSSH configuration options, converts them into
163
+ # a hash of Net::SSH options. Unrecognized options are ignored. The
164
+ # +settings+ hash must have Strings for keys, all downcased, and
165
+ # the returned hash will have Symbols for keys.
166
+ def translate(settings)
167
+ auth_methods = default_auth_methods.clone
168
+ (auth_methods << 'challenge-response').uniq!
169
+ ret = settings.each_with_object({ auth_methods: auth_methods }) do |(key, value), hash|
170
+ translate_config_key(hash, key.to_sym, value, settings)
156
171
  end
172
+ merge_challenge_response_with_keyboard_interactive(ret)
157
173
  end
158
- end
159
-
160
- # Given a hash of OpenSSH configuration options, converts them into
161
- # a hash of Net::SSH options. Unrecognized options are ignored. The
162
- # +settings+ hash must have Strings for keys, all downcased, and
163
- # the returned hash will have Symbols for keys.
164
- def translate(settings)
165
- auth_methods = default_auth_methods.clone
166
- (auth_methods << 'challenge-response').uniq!
167
- ret = settings.inject({auth_methods: auth_methods}) do |hash, (key, value)|
168
- translate_config_key(hash, key.to_sym, value, settings)
169
- hash
170
- end
171
- merge_challenge_response_with_keyboard_interactive(ret)
172
- end
173
174
 
174
- # Filters default_files down to the files that are expandable.
175
- def expandable_default_files
176
- default_files.keep_if do |path|
177
- begin
178
- File.expand_path(path)
179
- true
180
- rescue ArgumentError
181
- false
175
+ # Filters default_files down to the files that are expandable.
176
+ def expandable_default_files
177
+ default_files.keep_if do |path|
178
+ begin
179
+ File.expand_path(path)
180
+ true
181
+ rescue ArgumentError
182
+ false
183
+ end
182
184
  end
183
185
  end
184
- end
185
186
 
186
- private
187
+ private
187
188
 
188
189
  def translate_config_key(hash, key, value, settings)
189
190
  rename = {
@@ -196,79 +197,80 @@ module Net; module SSH
196
197
  globalknownhostsfile: :global_known_hosts_file,
197
198
  hostkeyalias: :host_key_alias,
198
199
  identityfile: :keys,
200
+ fingerprinthash: :fingerprint_hash,
199
201
  port: :port,
200
202
  user: :user,
201
203
  userknownhostsfile: :user_known_hosts_file
202
204
  }
203
205
  case key
204
- when :ciphers
205
- hash[:encryption] = value.split(/,/)
206
- when :hostbasedauthentication
207
- if value
208
- (hash[:auth_methods] << "hostbased").uniq!
209
- else
210
- hash[:auth_methods].delete("hostbased")
211
- end
212
- when :hostkeyalgorithms
213
- hash[:host_key] = value.split(/,/)
214
- when :hostname
215
- hash[:host_name] = value.gsub(/%h/, settings['host'])
216
- when :macs
217
- hash[:hmac] = value.split(/,/)
218
- when :serveralivecountmax
219
- hash[:keepalive_maxcount] = value.to_i if value
220
- when :serveraliveinterval
221
- if value && value.to_i > 0
222
- hash[:keepalive] = true
223
- hash[:keepalive_interval] = value.to_i
224
- else
225
- hash[:keepalive] = false
226
- end
227
- when :passwordauthentication
228
- if value
229
- (hash[:auth_methods] << 'password').uniq!
230
- else
231
- hash[:auth_methods].delete('password')
232
- end
233
- when :challengeresponseauthentication
234
- if value
235
- (hash[:auth_methods] << 'challenge-response').uniq!
236
- else
237
- hash[:auth_methods].delete('challenge-response')
238
- end
239
- when :kbdinteractiveauthentication
240
- if value
241
- (hash[:auth_methods] << 'keyboard-interactive').uniq!
242
- else
243
- hash[:auth_methods].delete('keyboard-interactive')
244
- end
245
- when :preferredauthentications
246
- hash[:auth_methods] = value.split(/,/) # TODO we should place to preferred_auth_methods rather than auth_methods
247
- when :proxycommand
248
- if value and !(value =~ /^none$/)
249
- require 'net/ssh/proxy/command'
250
- hash[:proxy] = Net::SSH::Proxy::Command.new(value)
251
- end
252
- when :proxyjump
253
- if value
254
- require 'net/ssh/proxy/jump'
255
- hash[:proxy] = Net::SSH::Proxy::Jump.new(value)
256
- end
257
- when :pubkeyauthentication
258
- if value
259
- (hash[:auth_methods] << 'publickey').uniq!
260
- else
261
- hash[:auth_methods].delete('publickey')
262
- end
263
- when :rekeylimit
264
- hash[:rekey_limit] = interpret_size(value)
265
- when :sendenv
266
- multi_send_env = value.to_s.split(/\s+/)
267
- hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false }
268
- when :numberofpasswordprompts
269
- hash[:number_of_password_prompts] = value.to_i
270
- when *rename.keys
271
- hash[rename[key]] = value
206
+ when :ciphers
207
+ hash[:encryption] = value.split(/,/)
208
+ when :hostbasedauthentication
209
+ if value
210
+ (hash[:auth_methods] << "hostbased").uniq!
211
+ else
212
+ hash[:auth_methods].delete("hostbased")
213
+ end
214
+ when :hostkeyalgorithms
215
+ hash[:host_key] = value.split(/,/)
216
+ when :hostname
217
+ hash[:host_name] = value.gsub(/%h/, settings['host'])
218
+ when :macs
219
+ hash[:hmac] = value.split(/,/)
220
+ when :serveralivecountmax
221
+ hash[:keepalive_maxcount] = value.to_i if value
222
+ when :serveraliveinterval
223
+ if value && value.to_i > 0
224
+ hash[:keepalive] = true
225
+ hash[:keepalive_interval] = value.to_i
226
+ else
227
+ hash[:keepalive] = false
228
+ end
229
+ when :passwordauthentication
230
+ if value
231
+ (hash[:auth_methods] << 'password').uniq!
232
+ else
233
+ hash[:auth_methods].delete('password')
234
+ end
235
+ when :challengeresponseauthentication
236
+ if value
237
+ (hash[:auth_methods] << 'challenge-response').uniq!
238
+ else
239
+ hash[:auth_methods].delete('challenge-response')
240
+ end
241
+ when :kbdinteractiveauthentication
242
+ if value
243
+ (hash[:auth_methods] << 'keyboard-interactive').uniq!
244
+ else
245
+ hash[:auth_methods].delete('keyboard-interactive')
246
+ end
247
+ when :preferredauthentications
248
+ hash[:auth_methods] = value.split(/,/) # TODO we should place to preferred_auth_methods rather than auth_methods
249
+ when :proxycommand
250
+ if value and value !~ /^none$/
251
+ require 'net/ssh/proxy/command'
252
+ hash[:proxy] = Net::SSH::Proxy::Command.new(value)
253
+ end
254
+ when :proxyjump
255
+ if value
256
+ require 'net/ssh/proxy/jump'
257
+ hash[:proxy] = Net::SSH::Proxy::Jump.new(value)
258
+ end
259
+ when :pubkeyauthentication
260
+ if value
261
+ (hash[:auth_methods] << 'publickey').uniq!
262
+ else
263
+ hash[:auth_methods].delete('publickey')
264
+ end
265
+ when :rekeylimit
266
+ hash[:rekey_limit] = interpret_size(value)
267
+ when :sendenv
268
+ multi_send_env = value.to_s.split(/\s+/)
269
+ hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false }
270
+ when :numberofpasswordprompts
271
+ hash[:number_of_password_prompts] = value.to_i
272
+ when *rename.keys
273
+ hash[rename[key]] = value
272
274
  end
273
275
  end
274
276
 
@@ -340,7 +342,8 @@ module Net; module SSH
340
342
  # return false for unsupported conditions
341
343
  condition_met.nil? ? false : (negated ^ condition_met)
342
344
  end
345
+ end
343
346
  end
344
- end
345
347
 
346
- end; end
348
+ end
349
+ end