ddollar-net-ssh 2.0.1

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 (103) hide show
  1. data/CHANGELOG.rdoc +42 -0
  2. data/Manifest +101 -0
  3. data/README.rdoc +110 -0
  4. data/Rakefile +26 -0
  5. data/THANKS.rdoc +16 -0
  6. data/lib/net/ssh.rb +199 -0
  7. data/lib/net/ssh/authentication/agent.rb +175 -0
  8. data/lib/net/ssh/authentication/constants.rb +18 -0
  9. data/lib/net/ssh/authentication/key_manager.rb +169 -0
  10. data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  11. data/lib/net/ssh/authentication/methods/hostbased.rb +71 -0
  12. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +66 -0
  13. data/lib/net/ssh/authentication/methods/password.rb +39 -0
  14. data/lib/net/ssh/authentication/methods/publickey.rb +92 -0
  15. data/lib/net/ssh/authentication/pageant.rb +176 -0
  16. data/lib/net/ssh/authentication/session.rb +127 -0
  17. data/lib/net/ssh/buffer.rb +339 -0
  18. data/lib/net/ssh/buffered_io.rb +149 -0
  19. data/lib/net/ssh/config.rb +173 -0
  20. data/lib/net/ssh/connection/channel.rb +625 -0
  21. data/lib/net/ssh/connection/constants.rb +33 -0
  22. data/lib/net/ssh/connection/session.rb +569 -0
  23. data/lib/net/ssh/connection/term.rb +178 -0
  24. data/lib/net/ssh/errors.rb +85 -0
  25. data/lib/net/ssh/key_factory.rb +85 -0
  26. data/lib/net/ssh/known_hosts.rb +129 -0
  27. data/lib/net/ssh/loggable.rb +61 -0
  28. data/lib/net/ssh/packet.rb +102 -0
  29. data/lib/net/ssh/prompt.rb +93 -0
  30. data/lib/net/ssh/proxy/errors.rb +14 -0
  31. data/lib/net/ssh/proxy/http.rb +94 -0
  32. data/lib/net/ssh/proxy/socks4.rb +70 -0
  33. data/lib/net/ssh/proxy/socks5.rb +128 -0
  34. data/lib/net/ssh/service/forward.rb +267 -0
  35. data/lib/net/ssh/test.rb +89 -0
  36. data/lib/net/ssh/test/channel.rb +129 -0
  37. data/lib/net/ssh/test/extensions.rb +152 -0
  38. data/lib/net/ssh/test/kex.rb +44 -0
  39. data/lib/net/ssh/test/local_packet.rb +51 -0
  40. data/lib/net/ssh/test/packet.rb +81 -0
  41. data/lib/net/ssh/test/remote_packet.rb +38 -0
  42. data/lib/net/ssh/test/script.rb +157 -0
  43. data/lib/net/ssh/test/socket.rb +59 -0
  44. data/lib/net/ssh/transport/algorithms.rb +384 -0
  45. data/lib/net/ssh/transport/cipher_factory.rb +72 -0
  46. data/lib/net/ssh/transport/constants.rb +30 -0
  47. data/lib/net/ssh/transport/hmac.rb +31 -0
  48. data/lib/net/ssh/transport/hmac/abstract.rb +48 -0
  49. data/lib/net/ssh/transport/hmac/md5.rb +12 -0
  50. data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  51. data/lib/net/ssh/transport/hmac/none.rb +15 -0
  52. data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  53. data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  54. data/lib/net/ssh/transport/identity_cipher.rb +40 -0
  55. data/lib/net/ssh/transport/kex.rb +13 -0
  56. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +208 -0
  57. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +77 -0
  58. data/lib/net/ssh/transport/openssl.rb +128 -0
  59. data/lib/net/ssh/transport/packet_stream.rb +230 -0
  60. data/lib/net/ssh/transport/server_version.rb +61 -0
  61. data/lib/net/ssh/transport/session.rb +262 -0
  62. data/lib/net/ssh/transport/state.rb +170 -0
  63. data/lib/net/ssh/verifiers/lenient.rb +30 -0
  64. data/lib/net/ssh/verifiers/null.rb +12 -0
  65. data/lib/net/ssh/verifiers/strict.rb +53 -0
  66. data/lib/net/ssh/version.rb +60 -0
  67. data/net-ssh.gemspec +56 -0
  68. data/setup.rb +1585 -0
  69. data/test/authentication/methods/common.rb +28 -0
  70. data/test/authentication/methods/test_abstract.rb +51 -0
  71. data/test/authentication/methods/test_hostbased.rb +108 -0
  72. data/test/authentication/methods/test_keyboard_interactive.rb +98 -0
  73. data/test/authentication/methods/test_password.rb +50 -0
  74. data/test/authentication/methods/test_publickey.rb +123 -0
  75. data/test/authentication/test_agent.rb +205 -0
  76. data/test/authentication/test_key_manager.rb +100 -0
  77. data/test/authentication/test_session.rb +93 -0
  78. data/test/common.rb +106 -0
  79. data/test/configs/exact_match +8 -0
  80. data/test/configs/wild_cards +14 -0
  81. data/test/connection/test_channel.rb +452 -0
  82. data/test/connection/test_session.rb +483 -0
  83. data/test/test_all.rb +6 -0
  84. data/test/test_buffer.rb +336 -0
  85. data/test/test_buffered_io.rb +63 -0
  86. data/test/test_config.rb +78 -0
  87. data/test/test_key_factory.rb +67 -0
  88. data/test/transport/hmac/test_md5.rb +34 -0
  89. data/test/transport/hmac/test_md5_96.rb +25 -0
  90. data/test/transport/hmac/test_none.rb +34 -0
  91. data/test/transport/hmac/test_sha1.rb +34 -0
  92. data/test/transport/hmac/test_sha1_96.rb +25 -0
  93. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  94. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  95. data/test/transport/test_algorithms.rb +302 -0
  96. data/test/transport/test_cipher_factory.rb +163 -0
  97. data/test/transport/test_hmac.rb +34 -0
  98. data/test/transport/test_identity_cipher.rb +40 -0
  99. data/test/transport/test_packet_stream.rb +433 -0
  100. data/test/transport/test_server_version.rb +55 -0
  101. data/test/transport/test_session.rb +312 -0
  102. data/test/transport/test_state.rb +173 -0
  103. metadata +222 -0
@@ -0,0 +1,66 @@
1
+ require 'net/ssh/prompt'
2
+ require 'net/ssh/authentication/methods/abstract'
3
+
4
+ module Net
5
+ module SSH
6
+ module Authentication
7
+ module Methods
8
+
9
+ # Implements the "keyboard-interactive" SSH authentication method.
10
+ class KeyboardInteractive < Abstract
11
+ include Prompt
12
+
13
+ USERAUTH_INFO_REQUEST = 60
14
+ USERAUTH_INFO_RESPONSE = 61
15
+
16
+ # Attempt to authenticate the given user for the given service.
17
+ def authenticate(next_service, username, password=nil)
18
+ debug { "trying keyboard-interactive" }
19
+ send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
20
+
21
+ loop do
22
+ message = session.next_message
23
+
24
+ case message.type
25
+ when USERAUTH_SUCCESS
26
+ debug { "keyboard-interactive succeeded" }
27
+ return true
28
+ when USERAUTH_FAILURE
29
+ debug { "keyboard-interactive failed" }
30
+ return false
31
+ when USERAUTH_INFO_REQUEST
32
+ name = message.read_string
33
+ instruction = message.read_string
34
+ debug { "keyboard-interactive info request" }
35
+
36
+ unless password
37
+ puts(name) unless name.empty?
38
+ puts(instruction) unless instruction.empty?
39
+ end
40
+
41
+ lang_tag = message.read_string
42
+ responses =[]
43
+
44
+ message.read_long.times do
45
+ text = message.read_string
46
+ echo = message.read_bool
47
+ responses << (password || prompt(text, echo))
48
+ end
49
+
50
+ # if the password failed the first time around, don't try
51
+ # and use it on subsequent requests.
52
+ password = nil
53
+
54
+ msg = Buffer.from(:byte, USERAUTH_INFO_RESPONSE, :long, responses.length, :string, responses)
55
+ send_message(msg)
56
+ else
57
+ raise Net::SSH::Exception, "unexpected reply in keyboard interactive: #{message.type} (#{message.inspect})"
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,39 @@
1
+ require 'net/ssh/errors'
2
+ require 'net/ssh/authentication/methods/abstract'
3
+
4
+ module Net
5
+ module SSH
6
+ module Authentication
7
+ module Methods
8
+
9
+ # Implements the "password" SSH authentication method.
10
+ class Password < Abstract
11
+ # Attempt to authenticate the given user for the given service. If
12
+ # the password parameter is nil, this will never do anything except
13
+ # return false.
14
+ def authenticate(next_service, username, password=nil)
15
+ return false unless password
16
+
17
+ send_message(userauth_request(username, next_service, "password", false, password))
18
+ message = session.next_message
19
+
20
+ case message.type
21
+ when USERAUTH_SUCCESS
22
+ debug { "password succeeded" }
23
+ return true
24
+ when USERAUTH_FAILURE
25
+ debug { "password failed" }
26
+ return false
27
+ when USERAUTH_PASSWD_CHANGEREQ
28
+ debug { "password change request received, failing" }
29
+ return false
30
+ else
31
+ raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
32
+ end
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,92 @@
1
+ require 'net/ssh/buffer'
2
+ require 'net/ssh/errors'
3
+ require 'net/ssh/authentication/methods/abstract'
4
+
5
+ module Net
6
+ module SSH
7
+ module Authentication
8
+ module Methods
9
+
10
+ # Implements the "publickey" SSH authentication method.
11
+ class Publickey < Abstract
12
+ # Attempts to perform public-key authentication for the given
13
+ # username, trying each identity known to the key manager. If any of
14
+ # them succeed, returns +true+, otherwise returns +false+. This
15
+ # requires the presence of a key manager.
16
+ def authenticate(next_service, username, password=nil)
17
+ return false unless key_manager
18
+
19
+ key_manager.identities.each do |identity|
20
+ return true if authenticate_with(identity, next_service, username)
21
+ end
22
+
23
+ return false
24
+ end
25
+
26
+ private
27
+
28
+ # Builds a packet that contains the request formatted for sending
29
+ # a public-key request to the server.
30
+ def build_request(pub_key, username, next_service, has_sig)
31
+ blob = Net::SSH::Buffer.new
32
+ blob.write_key pub_key
33
+
34
+ userauth_request(username, next_service, "publickey", has_sig,
35
+ pub_key.ssh_type, blob.to_s)
36
+ end
37
+
38
+ # Builds and sends a request formatted for a public-key
39
+ # authentication request.
40
+ def send_request(pub_key, username, next_service, signature=nil)
41
+ msg = build_request(pub_key, username, next_service, !signature.nil?)
42
+ msg.write_string(signature) if signature
43
+ send_message(msg)
44
+ end
45
+
46
+ # Attempts to perform public-key authentication for the given
47
+ # username, with the given identity (public key). Returns +true+ if
48
+ # successful, or +false+ otherwise.
49
+ def authenticate_with(identity, next_service, username)
50
+ debug { "trying publickey (#{identity.fingerprint})" }
51
+ send_request(identity, username, next_service)
52
+
53
+ message = session.next_message
54
+
55
+ case message.type
56
+ when USERAUTH_PK_OK
57
+ buffer = build_request(identity, username, next_service, true)
58
+ sig_data = Net::SSH::Buffer.new
59
+ sig_data.write_string(session_id)
60
+ sig_data.append(buffer.to_s)
61
+
62
+ sig_blob = key_manager.sign(identity, sig_data)
63
+
64
+ send_request(identity, username, next_service, sig_blob.to_s)
65
+ message = session.next_message
66
+
67
+ case message.type
68
+ when USERAUTH_SUCCESS
69
+ debug { "publickey succeeded (#{identity.fingerprint})" }
70
+ return true
71
+ when USERAUTH_FAILURE
72
+ debug { "publickey failed (#{identity.fingerprint})" }
73
+ return false
74
+ else
75
+ raise Net::SSH::Exception,
76
+ "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
77
+ end
78
+
79
+ when USERAUTH_FAILURE
80
+ return false
81
+
82
+ else
83
+ raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,176 @@
1
+ require 'dl/import'
2
+ require 'dl/struct'
3
+
4
+ require 'net/ssh/errors'
5
+
6
+ module Net; module SSH; module Authentication
7
+
8
+ # This module encapsulates the implementation of a socket factory that
9
+ # uses the PuTTY "pageant" utility to obtain information about SSH
10
+ # identities.
11
+ #
12
+ # This code is a slightly modified version of the original implementation
13
+ # by Guillaume Mar�ais (guillaume.marcais@free.fr). It is used and
14
+ # relicensed by permission.
15
+ module Pageant
16
+
17
+ # From Putty pageant.c
18
+ AGENT_MAX_MSGLEN = 8192
19
+ AGENT_COPYDATA_ID = 0x804e50ba
20
+
21
+ # The definition of the Windows methods and data structures used in
22
+ # communicating with the pageant process.
23
+ module Win
24
+ extend DL::Importable
25
+
26
+ dlload 'user32'
27
+ dlload 'kernel32'
28
+
29
+ typealias("LPCTSTR", "char *") # From winnt.h
30
+ typealias("LPVOID", "void *") # From winnt.h
31
+ typealias("LPCVOID", "const void *") # From windef.h
32
+ typealias("LRESULT", "long") # From windef.h
33
+ typealias("WPARAM", "unsigned int *") # From windef.h
34
+ typealias("LPARAM", "long *") # From windef.h
35
+ typealias("PDWORD_PTR", "long *") # From basetsd.h
36
+
37
+ # From winbase.h, winnt.h
38
+ INVALID_HANDLE_VALUE = -1
39
+ NULL = nil
40
+ PAGE_READWRITE = 0x0004
41
+ FILE_MAP_WRITE = 2
42
+ WM_COPYDATA = 74
43
+
44
+ SMTO_NORMAL = 0 # From winuser.h
45
+
46
+ # args: lpClassName, lpWindowName
47
+ extern 'HWND FindWindow(LPCTSTR, LPCTSTR)'
48
+
49
+ # args: none
50
+ extern 'DWORD GetCurrentThreadId()'
51
+
52
+ # args: hFile, (ignored), flProtect, dwMaximumSizeHigh,
53
+ # dwMaximumSizeLow, lpName
54
+ extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD, DWORD, ' +
55
+ 'DWORD, LPCTSTR)'
56
+
57
+ # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
58
+ # dwfileOffsetLow, dwNumberOfBytesToMap
59
+ extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)'
60
+
61
+ # args: lpBaseAddress
62
+ extern 'BOOL UnmapViewOfFile(LPCVOID)'
63
+
64
+ # args: hObject
65
+ extern 'BOOL CloseHandle(HANDLE)'
66
+
67
+ # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
68
+ extern 'LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, ' +
69
+ 'UINT, UINT, PDWORD_PTR)'
70
+ end
71
+
72
+ # This is the pseudo-socket implementation that mimics the interface of
73
+ # a socket, translating each request into a Windows messaging call to
74
+ # the pageant daemon. This allows pageant support to be implemented
75
+ # simply by replacing the socket factory used by the Agent class.
76
+ class Socket
77
+
78
+ private_class_method :new
79
+
80
+ # The factory method for creating a new Socket instance. The location
81
+ # parameter is ignored, and is only needed for compatibility with
82
+ # the general Socket interface.
83
+ def self.open(location=nil)
84
+ new
85
+ end
86
+
87
+ # Create a new instance that communicates with the running pageant
88
+ # instance. If no such instance is running, this will cause an error.
89
+ def initialize
90
+ @win = Win.findWindow("Pageant", "Pageant")
91
+
92
+ if @win == 0
93
+ raise Net::SSH::Exception,
94
+ "pageant process not running"
95
+ end
96
+
97
+ @res = nil
98
+ @pos = 0
99
+ end
100
+
101
+ # Forwards the data to #send_query, ignoring any arguments after
102
+ # the first. Returns 0.
103
+ def send(data, *args)
104
+ @res = send_query(data)
105
+ @pos = 0
106
+ end
107
+
108
+ # Packages the given query string and sends it to the pageant
109
+ # process via the Windows messaging subsystem. The result is
110
+ # cached, to be returned piece-wise when #read is called.
111
+ def send_query(query)
112
+ res = nil
113
+ filemap = 0
114
+ ptr = nil
115
+ id = DL::PtrData.malloc(DL.sizeof("L"))
116
+
117
+ mapname = "PageantRequest%08x\000" % Win.getCurrentThreadId()
118
+ filemap = Win.createFileMapping(Win::INVALID_HANDLE_VALUE,
119
+ Win::NULL,
120
+ Win::PAGE_READWRITE, 0,
121
+ AGENT_MAX_MSGLEN, mapname)
122
+ if filemap == 0
123
+ raise Net::SSH::Exception,
124
+ "Creation of file mapping failed"
125
+ end
126
+
127
+ ptr = Win.mapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
128
+ AGENT_MAX_MSGLEN)
129
+
130
+ if ptr.nil? || ptr.null?
131
+ raise Net::SSH::Exception, "Mapping of file failed"
132
+ end
133
+
134
+ ptr[0] = query
135
+
136
+ cds = [AGENT_COPYDATA_ID, mapname.size + 1, mapname].
137
+ pack("LLp").to_ptr
138
+ succ = Win.sendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
139
+ cds, Win::SMTO_NORMAL, 5000, id)
140
+
141
+ if succ > 0
142
+ retlen = 4 + ptr.to_s(4).unpack("N")[0]
143
+ res = ptr.to_s(retlen)
144
+ end
145
+
146
+ return res
147
+ ensure
148
+ Win.unmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
149
+ Win.closeHandle(filemap) if filemap != 0
150
+ end
151
+
152
+ # Conceptually close the socket. This doesn't really do anthing
153
+ # significant, but merely complies with the Socket interface.
154
+ def close
155
+ @res = nil
156
+ @pos = 0
157
+ end
158
+
159
+ # Reads +n+ bytes from the cached result of the last query. If +n+
160
+ # is +nil+, returns all remaining data from the last query.
161
+ def read(n = nil)
162
+ return nil unless @res
163
+ if n.nil?
164
+ start, @pos = @pos, @res.size
165
+ return @res[start..-1]
166
+ else
167
+ start, @pos = @pos, @pos + n
168
+ return @res[start, n]
169
+ end
170
+ end
171
+
172
+ end
173
+
174
+ end
175
+
176
+ end; end; end
@@ -0,0 +1,127 @@
1
+ require 'net/ssh/loggable'
2
+ require 'net/ssh/transport/constants'
3
+ require 'net/ssh/authentication/constants'
4
+ require 'net/ssh/authentication/key_manager'
5
+ require 'net/ssh/authentication/methods/publickey'
6
+ require 'net/ssh/authentication/methods/hostbased'
7
+ require 'net/ssh/authentication/methods/password'
8
+ require 'net/ssh/authentication/methods/keyboard_interactive'
9
+
10
+ module Net; module SSH; module Authentication
11
+
12
+ # Represents an authentication session. It manages the authentication of
13
+ # a user over an established connection (the "transport" object, see
14
+ # Net::SSH::Transport::Session).
15
+ #
16
+ # The use of an authentication session to manage user authentication is
17
+ # internal to Net::SSH (specifically Net::SSH.start). Consumers of the
18
+ # Net::SSH library will never need to access this class directly.
19
+ class Session
20
+ include Transport::Constants, Constants, Loggable
21
+
22
+ # transport layer abstraction
23
+ attr_reader :transport
24
+
25
+ # the list of authentication methods to try
26
+ attr_reader :auth_methods
27
+
28
+ # the list of authentication methods that are allowed
29
+ attr_reader :allowed_auth_methods
30
+
31
+ # a hash of options, given at construction time
32
+ attr_reader :options
33
+
34
+ # Instantiates a new Authentication::Session object over the given
35
+ # transport layer abstraction.
36
+ def initialize(transport, options={})
37
+ self.logger = transport.logger
38
+ @transport = transport
39
+
40
+ @auth_methods = options[:auth_methods] || %w(publickey hostbased password keyboard-interactive)
41
+ @options = options
42
+
43
+ @allowed_auth_methods = @auth_methods
44
+ end
45
+
46
+ # Attempts to authenticate the given user, in preparation for the next
47
+ # service request. Returns true if an authentication method succeeds in
48
+ # authenticating the user, and false otherwise.
49
+ def authenticate(next_service, username, password=nil)
50
+ debug { "beginning authentication of `#{username}'" }
51
+
52
+ transport.send_message(transport.service_request("ssh-userauth"))
53
+ message = expect_message(SERVICE_ACCEPT)
54
+
55
+ key_manager = KeyManager.new(logger, options)
56
+ keys.each { |key| key_manager.add(key) }
57
+
58
+ attempted = []
59
+
60
+ @auth_methods.each do |name|
61
+ next unless @allowed_auth_methods.include?(name)
62
+ attempted << name
63
+
64
+ debug { "trying #{name}" }
65
+ method = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join).new(self, :key_manager => key_manager)
66
+
67
+ return true if method.authenticate(next_service, username, password)
68
+ end
69
+
70
+ error { "all authorization methods failed (tried #{attempted.join(', ')})" }
71
+ return false
72
+ ensure
73
+ key_manager.finish if key_manager
74
+ end
75
+
76
+ # Blocks until a packet is received. It silently handles USERAUTH_BANNER
77
+ # packets, and will raise an error if any packet is received that is not
78
+ # valid during user authentication.
79
+ def next_message
80
+ loop do
81
+ packet = transport.next_message
82
+
83
+ case packet.type
84
+ when USERAUTH_BANNER
85
+ info { packet[:message] }
86
+ # TODO add a hook for people to retrieve the banner when it is sent
87
+
88
+ when USERAUTH_FAILURE
89
+ @allowed_auth_methods = packet[:authentications].split(/,/)
90
+ debug { "allowed methods: #{packet[:authentications]}" }
91
+ return packet
92
+
93
+ when USERAUTH_METHOD_RANGE, SERVICE_ACCEPT
94
+ return packet
95
+
96
+ when USERAUTH_SUCCESS
97
+ transport.hint :authenticated
98
+ return packet
99
+
100
+ else
101
+ raise Net::SSH::Exception, "unexpected message #{packet.type} (#{packet})"
102
+ end
103
+ end
104
+ end
105
+
106
+ # Blocks until a packet is received, and returns it if it is of the given
107
+ # type. If it is not, an exception is raised.
108
+ def expect_message(type)
109
+ message = next_message
110
+ unless message.type == type
111
+ raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})"
112
+ end
113
+ message
114
+ end
115
+
116
+ private
117
+
118
+ # Returns an array of paths to the key files that should be used when
119
+ # attempting any key-based authentication mechanism.
120
+ def keys
121
+ Array(
122
+ options[:keys] ||
123
+ %w(~/.ssh/id_dsa ~/.ssh/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_rsa)
124
+ )
125
+ end
126
+ end
127
+ end; end; end