sonixlabs-net-ssh 2.3.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.
Files changed (123) hide show
  1. data/CHANGELOG.rdoc +262 -0
  2. data/Manifest +121 -0
  3. data/README.rdoc +184 -0
  4. data/Rakefile +86 -0
  5. data/Rudyfile +96 -0
  6. data/THANKS.rdoc +19 -0
  7. data/lib/net/ssh.rb +223 -0
  8. data/lib/net/ssh/authentication/agent.rb +179 -0
  9. data/lib/net/ssh/authentication/constants.rb +18 -0
  10. data/lib/net/ssh/authentication/key_manager.rb +253 -0
  11. data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  12. data/lib/net/ssh/authentication/methods/hostbased.rb +75 -0
  13. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +70 -0
  14. data/lib/net/ssh/authentication/methods/password.rb +43 -0
  15. data/lib/net/ssh/authentication/methods/publickey.rb +96 -0
  16. data/lib/net/ssh/authentication/pageant.rb +264 -0
  17. data/lib/net/ssh/authentication/session.rb +146 -0
  18. data/lib/net/ssh/buffer.rb +340 -0
  19. data/lib/net/ssh/buffered_io.rb +198 -0
  20. data/lib/net/ssh/config.rb +207 -0
  21. data/lib/net/ssh/connection/channel.rb +630 -0
  22. data/lib/net/ssh/connection/constants.rb +33 -0
  23. data/lib/net/ssh/connection/session.rb +597 -0
  24. data/lib/net/ssh/connection/term.rb +178 -0
  25. data/lib/net/ssh/errors.rb +88 -0
  26. data/lib/net/ssh/key_factory.rb +102 -0
  27. data/lib/net/ssh/known_hosts.rb +129 -0
  28. data/lib/net/ssh/loggable.rb +61 -0
  29. data/lib/net/ssh/packet.rb +102 -0
  30. data/lib/net/ssh/prompt.rb +93 -0
  31. data/lib/net/ssh/proxy/command.rb +75 -0
  32. data/lib/net/ssh/proxy/errors.rb +14 -0
  33. data/lib/net/ssh/proxy/http.rb +94 -0
  34. data/lib/net/ssh/proxy/socks4.rb +70 -0
  35. data/lib/net/ssh/proxy/socks5.rb +142 -0
  36. data/lib/net/ssh/ruby_compat.rb +43 -0
  37. data/lib/net/ssh/service/forward.rb +298 -0
  38. data/lib/net/ssh/test.rb +89 -0
  39. data/lib/net/ssh/test/channel.rb +129 -0
  40. data/lib/net/ssh/test/extensions.rb +152 -0
  41. data/lib/net/ssh/test/kex.rb +44 -0
  42. data/lib/net/ssh/test/local_packet.rb +51 -0
  43. data/lib/net/ssh/test/packet.rb +81 -0
  44. data/lib/net/ssh/test/remote_packet.rb +38 -0
  45. data/lib/net/ssh/test/script.rb +157 -0
  46. data/lib/net/ssh/test/socket.rb +64 -0
  47. data/lib/net/ssh/transport/algorithms.rb +386 -0
  48. data/lib/net/ssh/transport/cipher_factory.rb +79 -0
  49. data/lib/net/ssh/transport/constants.rb +30 -0
  50. data/lib/net/ssh/transport/hmac.rb +42 -0
  51. data/lib/net/ssh/transport/hmac/abstract.rb +79 -0
  52. data/lib/net/ssh/transport/hmac/md5.rb +12 -0
  53. data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  54. data/lib/net/ssh/transport/hmac/none.rb +15 -0
  55. data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  56. data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  57. data/lib/net/ssh/transport/hmac/sha2_256.rb +15 -0
  58. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +13 -0
  59. data/lib/net/ssh/transport/hmac/sha2_512.rb +14 -0
  60. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +13 -0
  61. data/lib/net/ssh/transport/identity_cipher.rb +55 -0
  62. data/lib/net/ssh/transport/kex.rb +17 -0
  63. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +208 -0
  64. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +80 -0
  65. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +15 -0
  66. data/lib/net/ssh/transport/key_expander.rb +26 -0
  67. data/lib/net/ssh/transport/openssl.rb +127 -0
  68. data/lib/net/ssh/transport/packet_stream.rb +235 -0
  69. data/lib/net/ssh/transport/server_version.rb +71 -0
  70. data/lib/net/ssh/transport/session.rb +278 -0
  71. data/lib/net/ssh/transport/state.rb +206 -0
  72. data/lib/net/ssh/verifiers/lenient.rb +30 -0
  73. data/lib/net/ssh/verifiers/null.rb +12 -0
  74. data/lib/net/ssh/verifiers/strict.rb +53 -0
  75. data/lib/net/ssh/version.rb +62 -0
  76. data/lib/sonixlabs-net-ssh.rb +1 -0
  77. data/net-ssh.gemspec +145 -0
  78. data/setup.rb +1585 -0
  79. data/support/arcfour_check.rb +20 -0
  80. data/support/ssh_tunnel_bug.rb +65 -0
  81. data/test/authentication/methods/common.rb +28 -0
  82. data/test/authentication/methods/test_abstract.rb +51 -0
  83. data/test/authentication/methods/test_hostbased.rb +114 -0
  84. data/test/authentication/methods/test_keyboard_interactive.rb +100 -0
  85. data/test/authentication/methods/test_password.rb +52 -0
  86. data/test/authentication/methods/test_publickey.rb +148 -0
  87. data/test/authentication/test_agent.rb +205 -0
  88. data/test/authentication/test_key_manager.rb +171 -0
  89. data/test/authentication/test_session.rb +106 -0
  90. data/test/common.rb +107 -0
  91. data/test/configs/eqsign +3 -0
  92. data/test/configs/exact_match +8 -0
  93. data/test/configs/host_plus +10 -0
  94. data/test/configs/multihost +4 -0
  95. data/test/configs/wild_cards +14 -0
  96. data/test/connection/test_channel.rb +467 -0
  97. data/test/connection/test_session.rb +488 -0
  98. data/test/test_all.rb +9 -0
  99. data/test/test_buffer.rb +336 -0
  100. data/test/test_buffered_io.rb +63 -0
  101. data/test/test_config.rb +120 -0
  102. data/test/test_key_factory.rb +79 -0
  103. data/test/transport/hmac/test_md5.rb +39 -0
  104. data/test/transport/hmac/test_md5_96.rb +25 -0
  105. data/test/transport/hmac/test_none.rb +34 -0
  106. data/test/transport/hmac/test_sha1.rb +34 -0
  107. data/test/transport/hmac/test_sha1_96.rb +25 -0
  108. data/test/transport/hmac/test_sha2_256.rb +35 -0
  109. data/test/transport/hmac/test_sha2_256_96.rb +25 -0
  110. data/test/transport/hmac/test_sha2_512.rb +35 -0
  111. data/test/transport/hmac/test_sha2_512_96.rb +25 -0
  112. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  113. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  114. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +33 -0
  115. data/test/transport/test_algorithms.rb +308 -0
  116. data/test/transport/test_cipher_factory.rb +213 -0
  117. data/test/transport/test_hmac.rb +34 -0
  118. data/test/transport/test_identity_cipher.rb +40 -0
  119. data/test/transport/test_packet_stream.rb +736 -0
  120. data/test/transport/test_server_version.rb +78 -0
  121. data/test/transport/test_session.rb +315 -0
  122. data/test/transport/test_state.rb +179 -0
  123. metadata +178 -0
@@ -0,0 +1,43 @@
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
+
27
+ raise Net::SSH::Authentication::DisallowedMethod unless
28
+ message[:authentications].split(/,/).include? 'password'
29
+
30
+ return false
31
+ when USERAUTH_PASSWD_CHANGEREQ
32
+ debug { "password change request received, failing" }
33
+ return false
34
+ else
35
+ raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,96 @@
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.each_identity 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
+
74
+ raise Net::SSH::Authentication::DisallowedMethod unless
75
+ message[:authentications].split(/,/).include? 'publickey'
76
+
77
+ return false
78
+ else
79
+ raise Net::SSH::Exception,
80
+ "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
81
+ end
82
+
83
+ when USERAUTH_FAILURE
84
+ return false
85
+
86
+ else
87
+ raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,264 @@
1
+ require 'dl/import'
2
+
3
+ if RUBY_VERSION < "1.9"
4
+ require 'dl/struct'
5
+ end
6
+
7
+ if RUBY_VERSION =~ /^1.9/
8
+ require 'dl/types'
9
+ require 'dl'
10
+ end
11
+
12
+ require 'net/ssh/errors'
13
+
14
+ module Net; module SSH; module Authentication
15
+
16
+ # This module encapsulates the implementation of a socket factory that
17
+ # uses the PuTTY "pageant" utility to obtain information about SSH
18
+ # identities.
19
+ #
20
+ # This code is a slightly modified version of the original implementation
21
+ # by Guillaume Mar�ais (guillaume.marcais@free.fr). It is used and
22
+ # relicensed by permission.
23
+ module Pageant
24
+
25
+ # From Putty pageant.c
26
+ AGENT_MAX_MSGLEN = 8192
27
+ AGENT_COPYDATA_ID = 0x804e50ba
28
+
29
+ # The definition of the Windows methods and data structures used in
30
+ # communicating with the pageant process.
31
+ module Win
32
+ if RUBY_VERSION < "1.9"
33
+ extend DL::Importable
34
+
35
+ dlload 'user32'
36
+ dlload 'kernel32'
37
+ end
38
+
39
+ if RUBY_VERSION =~ /^1.9/
40
+ extend DL::Importer
41
+ dlload 'user32','kernel32'
42
+ include DL::Win32Types
43
+ end
44
+
45
+ typealias("LPCTSTR", "char *") # From winnt.h
46
+ typealias("LPVOID", "void *") # From winnt.h
47
+ typealias("LPCVOID", "const void *") # From windef.h
48
+ typealias("LRESULT", "long") # From windef.h
49
+ typealias("WPARAM", "unsigned int *") # From windef.h
50
+ typealias("LPARAM", "long *") # From windef.h
51
+ typealias("PDWORD_PTR", "long *") # From basetsd.h
52
+
53
+ # From winbase.h, winnt.h
54
+ INVALID_HANDLE_VALUE = -1
55
+ NULL = nil
56
+ PAGE_READWRITE = 0x0004
57
+ FILE_MAP_WRITE = 2
58
+ WM_COPYDATA = 74
59
+
60
+ SMTO_NORMAL = 0 # From winuser.h
61
+
62
+ # args: lpClassName, lpWindowName
63
+ extern 'HWND FindWindow(LPCTSTR, LPCTSTR)'
64
+
65
+ # args: none
66
+ extern 'DWORD GetCurrentThreadId()'
67
+
68
+ # args: hFile, (ignored), flProtect, dwMaximumSizeHigh,
69
+ # dwMaximumSizeLow, lpName
70
+ extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD, DWORD, ' +
71
+ 'DWORD, LPCTSTR)'
72
+
73
+ # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
74
+ # dwfileOffsetLow, dwNumberOfBytesToMap
75
+ extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)'
76
+
77
+ # args: lpBaseAddress
78
+ extern 'BOOL UnmapViewOfFile(LPCVOID)'
79
+
80
+ # args: hObject
81
+ extern 'BOOL CloseHandle(HANDLE)'
82
+
83
+ # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
84
+ extern 'LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, ' +
85
+ 'UINT, UINT, PDWORD_PTR)'
86
+ if RUBY_VERSION < "1.9"
87
+ alias_method :FindWindow,:findWindow
88
+ module_function :FindWindow
89
+ end
90
+ end
91
+
92
+ # This is the pseudo-socket implementation that mimics the interface of
93
+ # a socket, translating each request into a Windows messaging call to
94
+ # the pageant daemon. This allows pageant support to be implemented
95
+ # simply by replacing the socket factory used by the Agent class.
96
+ class Socket
97
+
98
+ private_class_method :new
99
+
100
+ # The factory method for creating a new Socket instance. The location
101
+ # parameter is ignored, and is only needed for compatibility with
102
+ # the general Socket interface.
103
+ def self.open(location=nil)
104
+ new
105
+ end
106
+
107
+ # Create a new instance that communicates with the running pageant
108
+ # instance. If no such instance is running, this will cause an error.
109
+ def initialize
110
+ @win = Win.FindWindow("Pageant", "Pageant")
111
+
112
+ if @win == 0
113
+ raise Net::SSH::Exception,
114
+ "pageant process not running"
115
+ end
116
+
117
+ @res = nil
118
+ @pos = 0
119
+ end
120
+
121
+ # Forwards the data to #send_query, ignoring any arguments after
122
+ # the first. Returns 0.
123
+ def send(data, *args)
124
+ @res = send_query(data)
125
+ @pos = 0
126
+ end
127
+
128
+ # Packages the given query string and sends it to the pageant
129
+ # process via the Windows messaging subsystem. The result is
130
+ # cached, to be returned piece-wise when #read is called.
131
+ def send_query(query)
132
+ res = nil
133
+ filemap = 0
134
+ ptr = nil
135
+ id = DL::PtrData.malloc(DL.sizeof("L"))
136
+
137
+ mapname = "PageantRequest%08x\000" % Win.getCurrentThreadId()
138
+ filemap = Win.createFileMapping(Win::INVALID_HANDLE_VALUE,
139
+ Win::NULL,
140
+ Win::PAGE_READWRITE, 0,
141
+ AGENT_MAX_MSGLEN, mapname)
142
+ if filemap == 0
143
+ raise Net::SSH::Exception,
144
+ "Creation of file mapping failed"
145
+ end
146
+
147
+ ptr = Win.mapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
148
+ AGENT_MAX_MSGLEN)
149
+
150
+ if ptr.nil? || ptr.null?
151
+ raise Net::SSH::Exception, "Mapping of file failed"
152
+ end
153
+
154
+ ptr[0] = query
155
+
156
+ cds = [AGENT_COPYDATA_ID, mapname.size + 1, mapname].
157
+ pack("LLp").to_ptr
158
+ succ = Win.sendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
159
+ cds, Win::SMTO_NORMAL, 5000, id)
160
+
161
+ if succ > 0
162
+ retlen = 4 + ptr.to_s(4).unpack("N")[0]
163
+ res = ptr.to_s(retlen)
164
+ end
165
+
166
+ return res
167
+ ensure
168
+ Win.unmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
169
+ Win.closeHandle(filemap) if filemap != 0
170
+ end
171
+
172
+ # Conceptually close the socket. This doesn't really do anthing
173
+ # significant, but merely complies with the Socket interface.
174
+ def close
175
+ @res = nil
176
+ @pos = 0
177
+ end
178
+
179
+ # Conceptually asks if the socket is closed. As with #close,
180
+ # this doesn't really do anything significant, but merely
181
+ # complies with the Socket interface.
182
+ def closed?
183
+ @res.nil? && @pos.zero?
184
+ end
185
+
186
+ # Reads +n+ bytes from the cached result of the last query. If +n+
187
+ # is +nil+, returns all remaining data from the last query.
188
+ def read(n = nil)
189
+ return nil unless @res
190
+ if n.nil?
191
+ start, @pos = @pos, @res.size
192
+ return @res[start..-1]
193
+ else
194
+ start, @pos = @pos, @pos + n
195
+ return @res[start, n]
196
+ end
197
+ end
198
+
199
+ end
200
+
201
+ # Socket changes for Ruby 1.9
202
+ # Functionality is the same as Ruby 1.8 but it includes the new calls to
203
+ # the DL module as well as other pointer transformations
204
+ class Socket19 < Socket
205
+ # Packages the given query string and sends it to the pageant
206
+ # process via the Windows messaging subsystem. The result is
207
+ # cached, to be returned piece-wise when #read is called.
208
+ def send_query(query)
209
+ res = nil
210
+ filemap = 0
211
+ ptr = nil
212
+ id = DL.malloc(DL::SIZEOF_LONG)
213
+
214
+ mapname = "PageantRequest%08x\000" % Win.GetCurrentThreadId()
215
+
216
+ filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE,
217
+ Win::NULL,
218
+ Win::PAGE_READWRITE, 0,
219
+ AGENT_MAX_MSGLEN, mapname)
220
+
221
+ if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE
222
+ raise Net::SSH::Exception,
223
+ "Creation of file mapping failed"
224
+ end
225
+
226
+ ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
227
+ 0)
228
+
229
+ if ptr.nil? || ptr.null?
230
+ raise Net::SSH::Exception, "Mapping of file failed"
231
+ end
232
+
233
+ DL::CPtr.new(ptr)[0,query.size]=query
234
+
235
+ cds = DL::CPtr.to_ptr [AGENT_COPYDATA_ID, mapname.size + 1, mapname].
236
+ pack("LLp")
237
+ succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
238
+ cds, Win::SMTO_NORMAL, 5000, id)
239
+
240
+ if succ > 0
241
+ retlen = 4 + ptr.to_s(4).unpack("N")[0]
242
+ res = ptr.to_s(retlen)
243
+ end
244
+
245
+ return res
246
+ ensure
247
+ Win.UnmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
248
+ Win.CloseHandle(filemap) if filemap != 0
249
+ end
250
+ end
251
+
252
+ # Selects which socket to use depending on the ruby version
253
+ # This is needed due changes in the DL module.
254
+ def self.socket_factory
255
+ if RUBY_VERSION < "1.9"
256
+ Socket
257
+ else
258
+ Socket19
259
+ end
260
+ end
261
+
262
+ end
263
+
264
+ end; end; end
@@ -0,0 +1,146 @@
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
+ # Raised if the current authentication method is not allowed
13
+ class DisallowedMethod < Net::SSH::Exception
14
+ end
15
+
16
+ # Represents an authentication session. It manages the authentication of
17
+ # a user over an established connection (the "transport" object, see
18
+ # Net::SSH::Transport::Session).
19
+ #
20
+ # The use of an authentication session to manage user authentication is
21
+ # internal to Net::SSH (specifically Net::SSH.start). Consumers of the
22
+ # Net::SSH library will never need to access this class directly.
23
+ class Session
24
+ include Transport::Constants, Constants, Loggable
25
+
26
+ # transport layer abstraction
27
+ attr_reader :transport
28
+
29
+ # the list of authentication methods to try
30
+ attr_reader :auth_methods
31
+
32
+ # the list of authentication methods that are allowed
33
+ attr_reader :allowed_auth_methods
34
+
35
+ # a hash of options, given at construction time
36
+ attr_reader :options
37
+
38
+ # Instantiates a new Authentication::Session object over the given
39
+ # transport layer abstraction.
40
+ def initialize(transport, options={})
41
+ self.logger = transport.logger
42
+ @transport = transport
43
+
44
+ @auth_methods = options[:auth_methods] || %w(publickey hostbased password keyboard-interactive)
45
+ @options = options
46
+
47
+ @allowed_auth_methods = @auth_methods
48
+ end
49
+
50
+ # Attempts to authenticate the given user, in preparation for the next
51
+ # service request. Returns true if an authentication method succeeds in
52
+ # authenticating the user, and false otherwise.
53
+ def authenticate(next_service, username, password=nil)
54
+ debug { "beginning authentication of `#{username}'" }
55
+
56
+ transport.send_message(transport.service_request("ssh-userauth"))
57
+ message = expect_message(SERVICE_ACCEPT)
58
+
59
+ key_manager = KeyManager.new(logger, options)
60
+ keys.each { |key| key_manager.add(key) } unless keys.empty?
61
+ key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty?
62
+
63
+ attempted = []
64
+
65
+ @auth_methods.each do |name|
66
+ begin
67
+ next unless @allowed_auth_methods.include?(name)
68
+ attempted << name
69
+
70
+ debug { "trying #{name}" }
71
+ begin
72
+ method = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join).new(self, :key_manager => key_manager)
73
+ rescue NameError => ne
74
+ debug{"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
75
+ next
76
+ end
77
+
78
+ return true if method.authenticate(next_service, username, password)
79
+ rescue Net::SSH::Authentication::DisallowedMethod
80
+ end
81
+ end
82
+
83
+ error { "all authorization methods failed (tried #{attempted.join(', ')})" }
84
+ return false
85
+ ensure
86
+ key_manager.finish if key_manager
87
+ end
88
+
89
+ # Blocks until a packet is received. It silently handles USERAUTH_BANNER
90
+ # packets, and will raise an error if any packet is received that is not
91
+ # valid during user authentication.
92
+ def next_message
93
+ loop do
94
+ packet = transport.next_message
95
+
96
+ case packet.type
97
+ when USERAUTH_BANNER
98
+ info { packet[:message] }
99
+ # TODO add a hook for people to retrieve the banner when it is sent
100
+
101
+ when USERAUTH_FAILURE
102
+ @allowed_auth_methods = packet[:authentications].split(/,/)
103
+ debug { "allowed methods: #{packet[:authentications]}" }
104
+ return packet
105
+
106
+ when USERAUTH_METHOD_RANGE, SERVICE_ACCEPT
107
+ return packet
108
+
109
+ when USERAUTH_SUCCESS
110
+ transport.hint :authenticated
111
+ return packet
112
+
113
+ else
114
+ raise Net::SSH::Exception, "unexpected message #{packet.type} (#{packet})"
115
+ end
116
+ end
117
+ end
118
+
119
+ # Blocks until a packet is received, and returns it if it is of the given
120
+ # type. If it is not, an exception is raised.
121
+ def expect_message(type)
122
+ message = next_message
123
+ unless message.type == type
124
+ raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})"
125
+ end
126
+ message
127
+ end
128
+
129
+ private
130
+
131
+ # Returns an array of paths to the key files that should be used when
132
+ # attempting any key-based authentication mechanism.
133
+ def keys
134
+ Array(
135
+ options[:keys] ||
136
+ %w(~/.ssh/id_dsa ~/.ssh/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_rsa)
137
+ )
138
+ end
139
+
140
+ # Returns an array of the key data that should be used when
141
+ # attempting any key-based authentication mechanism.
142
+ def key_data
143
+ Array(options[:key_data])
144
+ end
145
+ end
146
+ end; end; end