ruby_smb 1.0.5 → 2.0.3

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 (191) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +3 -2
  5. data/Gemfile +6 -2
  6. data/README.md +35 -47
  7. data/examples/anonymous_auth.rb +3 -3
  8. data/examples/append_file.rb +10 -8
  9. data/examples/authenticate.rb +9 -5
  10. data/examples/delete_file.rb +8 -6
  11. data/examples/enum_registry_key.rb +29 -0
  12. data/examples/enum_registry_values.rb +31 -0
  13. data/examples/list_directory.rb +8 -6
  14. data/examples/negotiate.rb +51 -8
  15. data/examples/negotiate_with_netbios_service.rb +9 -5
  16. data/examples/net_share_enum_all.rb +6 -4
  17. data/examples/pipes.rb +13 -13
  18. data/examples/query_service_status.rb +64 -0
  19. data/examples/read_file.rb +8 -6
  20. data/examples/read_file_encryption.rb +56 -0
  21. data/examples/read_registry_key_value.rb +33 -0
  22. data/examples/rename_file.rb +9 -7
  23. data/examples/tree_connect.rb +7 -5
  24. data/examples/write_file.rb +9 -7
  25. data/lib/ruby_smb.rb +4 -1
  26. data/lib/ruby_smb/client.rb +239 -21
  27. data/lib/ruby_smb/client/authentication.rb +27 -8
  28. data/lib/ruby_smb/client/encryption.rb +62 -0
  29. data/lib/ruby_smb/client/negotiation.rb +154 -12
  30. data/lib/ruby_smb/client/signing.rb +19 -0
  31. data/lib/ruby_smb/client/tree_connect.rb +4 -4
  32. data/lib/ruby_smb/client/utils.rb +8 -7
  33. data/lib/ruby_smb/client/winreg.rb +46 -0
  34. data/lib/ruby_smb/crypto.rb +30 -0
  35. data/lib/ruby_smb/dcerpc.rb +40 -0
  36. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  37. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  38. data/lib/ruby_smb/dcerpc/error.rb +6 -0
  39. data/lib/ruby_smb/dcerpc/ndr.rb +260 -16
  40. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  41. data/lib/ruby_smb/dcerpc/request.rb +41 -9
  42. data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
  43. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +38 -0
  44. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  45. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  46. data/lib/ruby_smb/dcerpc/svcctl.rb +479 -0
  47. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
  48. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
  49. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
  50. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
  51. data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
  52. data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
  53. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
  54. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
  55. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
  56. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
  57. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
  58. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
  59. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
  60. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
  61. data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
  62. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
  63. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
  64. data/lib/ruby_smb/dcerpc/winreg.rb +421 -0
  65. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  66. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  67. data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +73 -0
  68. data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
  69. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  70. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  71. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  72. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  73. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  74. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  75. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  76. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  77. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  78. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  79. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +40 -0
  80. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  81. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  82. data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +37 -0
  83. data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
  84. data/lib/ruby_smb/dispatcher/base.rb +1 -1
  85. data/lib/ruby_smb/dispatcher/socket.rb +5 -4
  86. data/lib/ruby_smb/error.rb +28 -1
  87. data/lib/ruby_smb/field/stringz16.rb +17 -1
  88. data/lib/ruby_smb/nbss/session_header.rb +4 -4
  89. data/lib/ruby_smb/smb1/commands.rb +1 -1
  90. data/lib/ruby_smb/smb1/file.rb +8 -14
  91. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
  92. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
  93. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  94. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
  95. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
  96. data/lib/ruby_smb/smb1/pipe.rb +81 -3
  97. data/lib/ruby_smb/smb1/tree.rb +12 -3
  98. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  99. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  100. data/lib/ruby_smb/smb2/file.rb +51 -61
  101. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  102. data/lib/ruby_smb/smb2/packet.rb +2 -0
  103. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  104. data/lib/ruby_smb/smb2/packet/error_packet.rb +2 -4
  105. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
  106. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
  107. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  108. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
  109. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
  110. data/lib/ruby_smb/smb2/pipe.rb +80 -3
  111. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  112. data/lib/ruby_smb/smb2/tree.rb +32 -20
  113. data/lib/ruby_smb/version.rb +1 -1
  114. data/ruby_smb.gemspec +5 -3
  115. data/spec/lib/ruby_smb/client_spec.rb +1583 -102
  116. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  117. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  118. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  119. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1729 -0
  120. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  121. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
  122. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +135 -0
  123. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  124. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  125. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
  126. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
  127. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
  128. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
  129. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
  130. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
  131. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
  132. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
  133. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
  134. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
  135. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
  136. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
  137. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
  138. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
  139. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
  140. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
  141. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
  142. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
  143. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  144. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  145. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
  146. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
  147. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +104 -0
  148. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  149. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  150. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  151. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  152. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  153. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +95 -0
  154. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  155. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +35 -0
  156. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  157. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  158. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +138 -0
  159. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  160. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
  161. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
  162. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +884 -0
  163. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  164. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
  165. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  166. data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
  167. data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
  168. data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
  169. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  170. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  171. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  172. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  173. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +216 -147
  174. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  175. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  176. data/spec/lib/ruby_smb/smb2/file_spec.rb +146 -68
  177. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  178. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  179. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +3 -24
  180. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  181. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  182. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  183. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  184. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
  185. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +226 -148
  186. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  187. data/spec/lib/ruby_smb/smb2/tree_spec.rb +88 -9
  188. metadata +257 -81
  189. metadata.gz.sig +0 -0
  190. data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
  191. data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -8,6 +8,8 @@ module RubySMB
8
8
  require 'ruby_smb/client/tree_connect'
9
9
  require 'ruby_smb/client/echo'
10
10
  require 'ruby_smb/client/utils'
11
+ require 'ruby_smb/client/winreg'
12
+ require 'ruby_smb/client/encryption'
11
13
 
12
14
  include RubySMB::Client::Negotiation
13
15
  include RubySMB::Client::Authentication
@@ -15,13 +17,21 @@ module RubySMB
15
17
  include RubySMB::Client::TreeConnect
16
18
  include RubySMB::Client::Echo
17
19
  include RubySMB::Client::Utils
20
+ include RubySMB::Client::Winreg
21
+ include RubySMB::Client::Encryption
18
22
 
19
23
  # The Default SMB1 Dialect string used in an SMB1 Negotiate Request
20
24
  SMB1_DIALECT_SMB1_DEFAULT = 'NT LM 0.12'.freeze
21
25
  # The Default SMB2 Dialect string used in an SMB1 Negotiate Request
22
26
  SMB1_DIALECT_SMB2_DEFAULT = 'SMB 2.002'.freeze
23
- # Dialect value for SMB2 Default (Version 2.02)
24
- SMB2_DIALECT_DEFAULT = 0x0202
27
+ # The SMB2 wildcard revision number Dialect string used in an SMB1 Negotiate Request
28
+ # It indicates that the server implements SMB 2.1 or future dialect revisions
29
+ # Note that this must be used for SMB3
30
+ SMB1_DIALECT_SMB2_WILDCARD = 'SMB 2.???'.freeze
31
+ # Dialect values for SMB2
32
+ SMB2_DIALECT_DEFAULT = ['0x0202', '0x0210']
33
+ # Dialect values for SMB3
34
+ SMB3_DIALECT_DEFAULT = ['0x0300', '0x0302', '0x0311']
25
35
  # The default maximum size of a SMB message that the Client accepts (in bytes)
26
36
  MAX_BUFFER_SIZE = 64512
27
37
  # The default maximum size of a SMB message that the Server accepts (in bytes)
@@ -133,6 +143,11 @@ module RubySMB
133
143
  # @return [Boolean]
134
144
  attr_accessor :smb2
135
145
 
146
+ # Whether or not the Client should support SMB3
147
+ # @!attribute [rw] smb3
148
+ # @return [Boolean]
149
+ attr_accessor :smb3
150
+
136
151
  # Tracks the current SMB2 Message ID that keeps communication in sync
137
152
  # @!attribute [rw] smb2_message_id
138
153
  # @return [Integer]
@@ -145,9 +160,16 @@ module RubySMB
145
160
 
146
161
  # The UID set in SMB1
147
162
  # @!attribute [rw] user_id
148
- # @return [String]
163
+ # @return [Integer]
149
164
  attr_accessor :user_id
150
165
 
166
+ # The Process ID set in SMB1
167
+ # It is randomly generated during the client initialization, but can
168
+ # be modified using the accessor.
169
+ # @!attribute [rw] pid
170
+ # @return [Integer]
171
+ attr_accessor :pid
172
+
151
173
  # The maximum size SMB message that the Client accepts (in bytes)
152
174
  # The default value is equal to {MAX_BUFFER_SIZE}.
153
175
  # @!attribute [rw] max_buffer_size
@@ -176,15 +198,92 @@ module RubySMB
176
198
  # @return [Integer]
177
199
  attr_accessor :server_max_transact_size
178
200
 
201
+ # The algorithm to compute the preauthentication integrity hash (SMB 3.1.1).
202
+ # @!attribute [rw] preauth_integrity_hash_algorithm
203
+ # @return [String]
204
+ attr_accessor :preauth_integrity_hash_algorithm
205
+
206
+ # The preauthentication integrity hash value (SMB 3.1.1).
207
+ # @!attribute [rw] preauth_integrity_hash_value
208
+ # @return [String]
209
+ attr_accessor :preauth_integrity_hash_value
210
+
211
+ # The algorithm for encryption (SMB 3.x).
212
+ # @!attribute [rw] encryption_algorithm
213
+ # @return [String]
214
+ attr_accessor :encryption_algorithm
215
+
216
+ # The client encryption key (SMB 3.x).
217
+ # @!attribute [rw] client_encryption_key
218
+ # @return [String]
219
+ attr_accessor :client_encryption_key
220
+
221
+ # The server encryption key (SMB 3.x).
222
+ # @!attribute [rw] server_encryption_key
223
+ # @return [String]
224
+ attr_accessor :server_encryption_key
225
+
226
+ # Whether or not the whole session needs to be encrypted (SMB 3.x)
227
+ # @!attribute [rw] session_encrypt_data
228
+ # @return [Boolean]
229
+ attr_accessor :session_encrypt_data
230
+
231
+ # The encryption algorithms supported by the server (SMB 3.x).
232
+ # @!attribute [rw] server_encryption_algorithms
233
+ # @return [Array<Integer>] list of supported encryption algorithms
234
+ # (constants defined in RubySMB::SMB2::EncryptionCapabilities)
235
+ attr_accessor :server_encryption_algorithms
236
+
237
+ # The compression algorithms supported by the server (SMB 3.x).
238
+ # @!attribute [rw] server_compression_algorithms
239
+ # @return [Array<Integer>] list of supported compression algorithms
240
+ # (constants defined in RubySMB::SMB2::CompressionCapabilities)
241
+ attr_accessor :server_compression_algorithms
242
+
243
+ # The GUID of the server (SMB 2.x and 3.x).
244
+ # @!attribute [rw] server_guid
245
+ # @return [String]
246
+ attr_accessor :server_guid
247
+
248
+ # The server's start time if it is reported as part of the negotiation
249
+ # process (SMB 2.x and 3.x). This value is nil if the server does not report
250
+ # it (reports a value of 0).
251
+ # @!attribute [rw] server_start_time
252
+ # @return [Time] the time that the server reports that it was started at
253
+ attr_accessor :server_start_time
254
+
255
+ # The server's current time if it is reported as part of the negotiation
256
+ # process (SMB 2.x and 3.x). This value is nil if the server does not report
257
+ # it (reports a value of 0).
258
+ # @!attribute [rw] server_system_time
259
+ # @return [Time] the time that the server reports as current
260
+ attr_accessor :server_system_time
261
+
262
+ # The SMB version that has been successfully negotiated. This value is only
263
+ # set after the NEGOTIATE handshake has been performed.
264
+ # @!attribute [rw] negotiated_smb_version
265
+ # @return [Integer] the negotiated SMB version
266
+ attr_accessor :negotiated_smb_version
267
+
268
+ # Whether or not the server supports multi-credit operations. It is
269
+ # reported by the LARGE_MTU capabiliy as part of the negotiation process
270
+ # (SMB 2.x and 3.x).
271
+ # @!attribute [rw] server_supports_multi_credit
272
+ # @return [Boolean] true if the server supports multi-credit operations,
273
+ # false otherwise
274
+ attr_accessor :server_supports_multi_credit
275
+
179
276
  # @param dispatcher [RubySMB::Dispatcher::Socket] the packet dispatcher to use
180
277
  # @param smb1 [Boolean] whether or not to enable SMB1 support
181
278
  # @param smb2 [Boolean] whether or not to enable SMB2 support
182
- def initialize(dispatcher, smb1: true, smb2: true, username:, password:, domain: '.', local_workstation: 'WORKSTATION')
279
+ # @param smb3 [Boolean] whether or not to enable SMB3 support
280
+ def initialize(dispatcher, smb1: true, smb2: true, smb3: true, username:, password:, domain: '.', local_workstation: 'WORKSTATION', always_encrypt: true)
183
281
  raise ArgumentError, 'No Dispatcher provided' unless dispatcher.is_a? RubySMB::Dispatcher::Base
184
- if smb1 == false && smb2 == false
282
+ if smb1 == false && smb2 == false && smb3 == false
185
283
  raise ArgumentError, 'You must enable at least one Protocol'
186
284
  end
187
285
  @dispatcher = dispatcher
286
+ @pid = rand(0xFFFF)
188
287
  @domain = domain
189
288
  @local_workstation = local_workstation
190
289
  @password = password.encode('utf-8') || ''.encode('utf-8')
@@ -194,6 +293,7 @@ module RubySMB
194
293
  @signing_required = false
195
294
  @smb1 = smb1
196
295
  @smb2 = smb2
296
+ @smb3 = smb3
197
297
  @username = username.encode('utf-8') || ''.encode('utf-8')
198
298
  @max_buffer_size = MAX_BUFFER_SIZE
199
299
  # These sizes will be modifed during negotiation
@@ -201,11 +301,16 @@ module RubySMB
201
301
  @server_max_read_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
202
302
  @server_max_write_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
203
303
  @server_max_transact_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
304
+ @server_supports_multi_credit = false
305
+
306
+ # SMB 3.x options
307
+ @session_encrypt_data = always_encrypt
204
308
 
205
309
  negotiate_version_flag = 0x02000000
206
310
  flags = Net::NTLM::Client::DEFAULT_FLAGS |
207
311
  Net::NTLM::FLAGS[:TARGET_INFO] |
208
- negotiate_version_flag
312
+ negotiate_version_flag ^
313
+ Net::NTLM::FLAGS[:OEM]
209
314
 
210
315
  @ntlm_client = Net::NTLM::Client.new(
211
316
  @username,
@@ -241,7 +346,7 @@ module RubySMB
241
346
  # @param data [String] the data the server should echo back (ignored in SMB2)
242
347
  # @return [WindowsError::ErrorCode] the NTStatus of the last response received
243
348
  def echo(count: 1, data: '')
244
- response = if smb2
349
+ response = if smb2 || smb3
245
350
  smb2_echo
246
351
  else
247
352
  smb1_echo(count: count, data: data)
@@ -256,10 +361,8 @@ module RubySMB
256
361
  # @param packet [RubySMB::GenericPacket] the packet to set the message id for
257
362
  # @return [RubySMB::GenericPacket] the modified packet
258
363
  def increment_smb_message_id(packet)
259
- if packet.smb2_header.message_id.zero? && smb2_message_id != 0
260
- packet.smb2_header.message_id = smb2_message_id
261
- self.smb2_message_id += 1
262
- end
364
+ packet.smb2_header.message_id = self.smb2_message_id
365
+ self.smb2_message_id += 1
263
366
  packet
264
367
  end
265
368
 
@@ -281,7 +384,8 @@ module RubySMB
281
384
  negotiate_version_flag = 0x02000000
282
385
  flags = Net::NTLM::Client::DEFAULT_FLAGS |
283
386
  Net::NTLM::FLAGS[:TARGET_INFO] |
284
- negotiate_version_flag
387
+ negotiate_version_flag ^
388
+ Net::NTLM::FLAGS[:OEM]
285
389
 
286
390
  @ntlm_client = Net::NTLM::Client.new(
287
391
  @username,
@@ -299,7 +403,7 @@ module RubySMB
299
403
  # @return [WindowsError::ErrorCode] the NTStatus of the response
300
404
  # @raise [RubySMB::Error::InvalidPacket] if the response packet is not a LogoffResponse packet
301
405
  def logoff!
302
- if smb2
406
+ if smb2 || smb3
303
407
  request = RubySMB::SMB2::Packet::LogoffRequest.new
304
408
  raw_response = send_recv(request)
305
409
  response = RubySMB::SMB2::Packet::LogoffResponse.read(raw_response)
@@ -332,25 +436,125 @@ module RubySMB
332
436
  # It will also sign the packet if neccessary.
333
437
  #
334
438
  # @param packet [RubySMB::GenericPacket] the request to be sent
439
+ # @param encrypt [Boolean] true if encryption has to be enabled for this transaction
440
+ # (note that if @session_encrypt_data is set, encryption will be enabled
441
+ # regardless of this parameter value)
335
442
  # @return [String] the raw response data received
336
- def send_recv(packet)
337
- case packet.packet_smb_version
443
+ def send_recv(packet, encrypt: false)
444
+ version = packet.packet_smb_version
445
+ case version
338
446
  when 'SMB1'
339
- packet.smb_header.uid = user_id if user_id
447
+ packet.smb_header.uid = self.user_id if self.user_id
448
+ packet.smb_header.pid_low = self.pid if self.pid
340
449
  packet = smb1_sign(packet)
341
450
  when 'SMB2'
342
451
  packet = increment_smb_message_id(packet)
343
452
  packet.smb2_header.session_id = session_id
344
453
  unless packet.is_a?(RubySMB::SMB2::Packet::SessionSetupRequest)
345
- packet = smb2_sign(packet)
454
+ if self.smb2
455
+ packet = smb2_sign(packet)
456
+ elsif self.smb3
457
+ packet = smb3_sign(packet)
458
+ end
346
459
  end
347
460
  else
348
461
  packet = packet
349
462
  end
350
- dispatcher.send_packet(packet)
351
- raw_response = dispatcher.recv_packet
463
+
464
+ encrypt_data = false
465
+ if can_be_encrypted?(packet) && encryption_supported? && (@session_encrypt_data || encrypt)
466
+ encrypt_data = true
467
+ end
468
+ send_packet(packet, encrypt: encrypt_data)
469
+ raw_response = recv_packet(encrypt: encrypt_data)
470
+ smb2_header = nil
471
+ loop do
472
+ smb2_header = RubySMB::SMB2::SMB2Header.read(raw_response)
473
+ break unless is_status_pending?(smb2_header)
474
+ sleep 1
475
+ raw_response = recv_packet(encrypt: encrypt_data)
476
+ end unless version == 'SMB1'
352
477
 
353
478
  self.sequence_counter += 1 if signing_required && !session_key.empty?
479
+ # update the SMB2 message ID according to the received Credit Charged
480
+ self.smb2_message_id += smb2_header.credit_charge - 1 if smb2_header && self.dialect != '0x0202'
481
+ raw_response
482
+ end
483
+
484
+ # Check if the response is an asynchronous operation with STATUS_PENDING
485
+ # status code.
486
+ #
487
+ # @param smb2_header [String] the response packet SMB2 header
488
+ # @return [Boolean] true if it is a status pending operation, false otherwise
489
+ def is_status_pending?(smb2_header)
490
+ value = smb2_header.nt_status.value
491
+ status_code = WindowsError::NTStatus.find_by_retval(value).first
492
+ status_code == WindowsError::NTStatus::STATUS_PENDING &&
493
+ smb2_header.flags.async_command == 1
494
+ end
495
+
496
+ # Check if the request packet can be encrypted. Per the SMB2 spec,
497
+ # SessionSetupRequest and NegotiateRequest must not be encrypted.
498
+ #
499
+ # @param packet [RubySMB::GenericPacket] the request packet
500
+ # @return [Boolean] true if the packet can be encrypted
501
+ def can_be_encrypted?(packet)
502
+ return false if packet.packet_smb_version == 'SMB1'
503
+ [RubySMB::SMB2::Packet::SessionSetupRequest, RubySMB::SMB2::Packet::NegotiateRequest].none? do |klass|
504
+ packet.is_a?(klass)
505
+ end
506
+ end
507
+
508
+ # Check if the current dialect supports encryption.
509
+ #
510
+ # @return [Boolean] true if encryption is supported
511
+ def encryption_supported?
512
+ ['0x0300', '0x0302', '0x0311'].include?(@dialect)
513
+ end
514
+
515
+ # Encrypt (if required) and send a packet.
516
+ #
517
+ # @param encrypt [Boolean] true if the packet should be encrypted, false
518
+ # otherwise
519
+ def send_packet(packet, encrypt: false)
520
+ if encrypt
521
+ begin
522
+ packet = smb3_encrypt(packet.to_binary_s)
523
+ rescue RubySMB::Error::RubySMBError => e
524
+ raise RubySMB::Error::EncryptionError, "Error while encrypting #{packet.class.name} packet (SMB #{@dialect}): #{e}"
525
+ end
526
+ end
527
+ dispatcher.send_packet(packet)
528
+ end
529
+
530
+ # Receives the raw response through the Dispatcher and decrypt the packet (if required).
531
+ #
532
+ # @param encrypt [Boolean] true if the packet is encrypted, false otherwise
533
+ # @return [String] the raw unencrypted packet
534
+ def recv_packet(encrypt: false)
535
+ begin
536
+ raw_response = dispatcher.recv_packet
537
+ rescue RubySMB::Error::CommunicationError => e
538
+ if encrypt
539
+ raise RubySMB::Error::EncryptionError, "Communication error with the "\
540
+ "remote host: #{e.message}. The server supports encryption but was "\
541
+ "not able to handle the encrypted request."
542
+ else
543
+ raise e
544
+ end
545
+ end
546
+ if encrypt
547
+ begin
548
+ transform_response = RubySMB::SMB2::Packet::TransformHeader.read(raw_response)
549
+ rescue IOError
550
+ raise RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet'
551
+ end
552
+ begin
553
+ raw_response = smb3_decrypt(transform_response)
554
+ rescue RubySMB::Error::RubySMBError => e
555
+ raise RubySMB::Error::EncryptionError, "Error while decrypting #{transform_response.class.name} packet (SMB #@dialect}): #{e}"
556
+ end
557
+ end
354
558
  raw_response
355
559
  end
356
560
 
@@ -360,7 +564,7 @@ module RubySMB
360
564
  # @return [RubySMB::SMB1::Tree] if talking over SMB1
361
565
  # @return [RubySMB::SMB2::Tree] if talking over SMB2
362
566
  def tree_connect(share)
363
- connected_tree = if smb2
567
+ connected_tree = if smb2 || smb3
364
568
  smb2_tree_connect(share)
365
569
  else
366
570
  smb1_tree_connect(share)
@@ -390,6 +594,9 @@ module RubySMB
390
594
  self.session_key = ''
391
595
  self.sequence_counter = 0
392
596
  self.smb2_message_id = 0
597
+ self.client_encryption_key = nil
598
+ self.server_encryption_key = nil
599
+ self.server_supports_multi_credit = false
393
600
  end
394
601
 
395
602
  # Requests a NetBIOS Session Service using the provided name.
@@ -427,10 +634,21 @@ module RubySMB
427
634
  session_request.session_header.session_packet_type = RubySMB::Nbss::SESSION_REQUEST
428
635
  session_request.called_name = called_name
429
636
  session_request.calling_name = calling_name
430
- session_request.session_header.packet_length =
637
+ session_request.session_header.stream_protocol_length =
431
638
  session_request.num_bytes - session_request.session_header.num_bytes
432
639
  session_request
433
640
  end
434
641
 
642
+ def update_preauth_hash(data)
643
+ unless @preauth_integrity_hash_algorithm
644
+ raise RubySMB::Error::EncryptionError.new(
645
+ 'Cannot compute the Preauth Integrity Hash value: Preauth Integrity Hash Algorithm is nil'
646
+ )
647
+ end
648
+ @preauth_integrity_hash_value = OpenSSL::Digest.digest(
649
+ @preauth_integrity_hash_algorithm,
650
+ @preauth_integrity_hash_value + data.to_binary_s
651
+ )
652
+ end
435
653
  end
436
654
  end
@@ -175,7 +175,7 @@ module RubySMB
175
175
 
176
176
  status_code = packet.status_code
177
177
  unless status_code.name == 'STATUS_MORE_PROCESSING_REQUIRED'
178
- raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
178
+ raise RubySMB::Error::UnexpectedStatusCode, status_code
179
179
  end
180
180
 
181
181
  packet
@@ -201,6 +201,9 @@ module RubySMB
201
201
  def smb2_authenticate
202
202
  response = smb2_ntlmssp_negotiate
203
203
  challenge_packet = smb2_ntlmssp_challenge_packet(response)
204
+ if @dialect == '0x0311'
205
+ update_preauth_hash(challenge_packet)
206
+ end
204
207
  @session_id = challenge_packet.smb2_header.session_id
205
208
  type2_b64_message = smb2_type2_message(challenge_packet)
206
209
  type3_message = @ntlm_client.init_context(type2_b64_message)
@@ -213,6 +216,16 @@ module RubySMB
213
216
  raw = smb2_ntlmssp_authenticate(type3_message, @session_id)
214
217
  response = smb2_ntlmssp_final_packet(raw)
215
218
 
219
+ if @smb3 && !@session_encrypt_data && response.session_flags.encrypt_data == 1
220
+ @session_encrypt_data = true
221
+ end
222
+ ######
223
+ # DEBUG
224
+ #puts "Session ID = #{@session_id.to_binary_s.each_byte.map {|e| '%02x' % e}.join}"
225
+ #puts "Session key = #{@session_key.each_byte.map {|e| '%02x' % e}.join}"
226
+ #puts "PreAuthHash = #{@preauth_integrity_hash_value.each_byte.map {|e| '%02x' % e}.join}" if @preauth_integrity_hash_value
227
+ ######
228
+
216
229
  response.status_code
217
230
  end
218
231
 
@@ -245,7 +258,7 @@ module RubySMB
245
258
 
246
259
  status_code = packet.status_code
247
260
  unless status_code.name == 'STATUS_MORE_PROCESSING_REQUIRED'
248
- raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
261
+ raise RubySMB::Error::UnexpectedStatusCode, status_code
249
262
  end
250
263
  packet
251
264
  end
@@ -256,7 +269,11 @@ module RubySMB
256
269
  # @return [String] the binary string response from the server
257
270
  def smb2_ntlmssp_negotiate
258
271
  packet = smb2_ntlmssp_negotiate_packet
259
- send_recv(packet)
272
+ response = send_recv(packet)
273
+ if @dialect == '0x0311'
274
+ update_preauth_hash(packet)
275
+ end
276
+ response
260
277
  end
261
278
 
262
279
  # Creates the {RubySMB::SMB2::Packet::SessionSetupRequest} packet
@@ -268,10 +285,7 @@ module RubySMB
268
285
  type1_message = ntlm_client.init_context
269
286
  packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
270
287
  packet.set_type1_blob(type1_message.serialize)
271
- # This Message ID should always be 1, but thanks to Multi-Protocol Negotiation
272
- # the Message ID can be out of sync at this point so we re-synch it here.
273
- packet.smb2_header.message_id = 1
274
- self.smb2_message_id = 2
288
+ packet.security_mode.signing_enabled = 1
275
289
  packet
276
290
  end
277
291
 
@@ -294,7 +308,11 @@ module RubySMB
294
308
  # @return [String] the raw binary response from the server
295
309
  def smb2_ntlmssp_authenticate(type3_message, user_id)
296
310
  packet = smb2_ntlmssp_auth_packet(type3_message, user_id)
297
- send_recv(packet)
311
+ response = send_recv(packet)
312
+ if @dialect == '0x0311'
313
+ update_preauth_hash(packet)
314
+ end
315
+ response
298
316
  end
299
317
 
300
318
  # Generates the {RubySMB::SMB2::Packet::SessionSetupRequest} packet
@@ -307,6 +325,7 @@ module RubySMB
307
325
  packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
308
326
  packet.smb2_header.session_id = session_id
309
327
  packet.set_type3_blob(type3_message.serialize)
328
+ packet.security_mode.signing_enabled = 1
310
329
  packet
311
330
  end
312
331