ruby_smb 1.0.3 → 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 (200) 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/enum_registry_key.rb +28 -0
  8. data/examples/enum_registry_values.rb +30 -0
  9. data/examples/negotiate.rb +51 -8
  10. data/examples/pipes.rb +2 -1
  11. data/examples/read_file_encryption.rb +56 -0
  12. data/examples/read_registry_key_value.rb +32 -0
  13. data/lib/ruby_smb.rb +4 -1
  14. data/lib/ruby_smb/client.rb +233 -22
  15. data/lib/ruby_smb/client/authentication.rb +70 -33
  16. data/lib/ruby_smb/client/echo.rb +20 -2
  17. data/lib/ruby_smb/client/encryption.rb +62 -0
  18. data/lib/ruby_smb/client/negotiation.rb +172 -24
  19. data/lib/ruby_smb/client/signing.rb +19 -0
  20. data/lib/ruby_smb/client/tree_connect.rb +24 -18
  21. data/lib/ruby_smb/client/utils.rb +8 -7
  22. data/lib/ruby_smb/client/winreg.rb +46 -0
  23. data/lib/ruby_smb/crypto.rb +30 -0
  24. data/lib/ruby_smb/dcerpc.rb +38 -0
  25. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  26. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  27. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  28. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  29. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  30. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  31. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  32. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  33. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  34. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  35. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  36. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  37. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  38. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  39. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  40. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  41. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  42. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  43. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  44. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  45. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  46. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  47. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  48. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  49. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  50. data/lib/ruby_smb/dispatcher/socket.rb +4 -3
  51. data/lib/ruby_smb/error.rb +68 -2
  52. data/lib/ruby_smb/generic_packet.rb +33 -4
  53. data/lib/ruby_smb/smb1/commands.rb +1 -1
  54. data/lib/ruby_smb/smb1/file.rb +66 -15
  55. data/lib/ruby_smb/smb1/packet/close_request.rb +2 -5
  56. data/lib/ruby_smb/smb1/packet/close_response.rb +2 -1
  57. data/lib/ruby_smb/smb1/packet/echo_request.rb +2 -4
  58. data/lib/ruby_smb/smb1/packet/echo_response.rb +2 -1
  59. data/lib/ruby_smb/smb1/packet/empty_packet.rb +10 -1
  60. data/lib/ruby_smb/smb1/packet/logoff_request.rb +2 -4
  61. data/lib/ruby_smb/smb1/packet/logoff_response.rb +2 -1
  62. data/lib/ruby_smb/smb1/packet/negotiate_request.rb +2 -5
  63. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +3 -7
  64. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +4 -4
  65. data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +2 -4
  66. data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +2 -1
  67. data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +2 -1
  68. data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +2 -1
  69. data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +2 -4
  70. data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +2 -1
  71. data/lib/ruby_smb/smb1/packet/read_andx_request.rb +2 -5
  72. data/lib/ruby_smb/smb1/packet/read_andx_response.rb +2 -1
  73. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -1
  74. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +3 -2
  75. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
  76. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +3 -2
  77. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_request.rb +0 -1
  78. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_response.rb +3 -2
  79. data/lib/ruby_smb/smb1/packet/trans/request.rb +2 -5
  80. data/lib/ruby_smb/smb1/packet/trans/response.rb +2 -1
  81. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +1 -1
  82. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +1 -1
  83. data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +2 -1
  84. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +8 -2
  85. data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +2 -1
  86. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +8 -2
  87. data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +2 -1
  88. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +2 -1
  89. data/lib/ruby_smb/smb1/packet/trans2/request.rb +2 -4
  90. data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +2 -4
  91. data/lib/ruby_smb/smb1/packet/trans2/response.rb +2 -1
  92. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +2 -1
  93. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +2 -1
  94. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +2 -4
  95. data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +13 -3
  96. data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
  97. data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -1
  98. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +3 -6
  99. data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
  100. data/lib/ruby_smb/smb1/pipe.rb +87 -6
  101. data/lib/ruby_smb/smb1/tree.rb +50 -3
  102. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  103. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  104. data/lib/ruby_smb/smb2/file.rb +103 -25
  105. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  106. data/lib/ruby_smb/smb2/packet.rb +2 -0
  107. data/lib/ruby_smb/smb2/packet/close_request.rb +2 -4
  108. data/lib/ruby_smb/smb2/packet/close_response.rb +2 -1
  109. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  110. data/lib/ruby_smb/smb2/packet/create_request.rb +2 -4
  111. data/lib/ruby_smb/smb2/packet/create_response.rb +2 -1
  112. data/lib/ruby_smb/smb2/packet/echo_request.rb +2 -4
  113. data/lib/ruby_smb/smb2/packet/echo_response.rb +2 -1
  114. data/lib/ruby_smb/smb2/packet/error_packet.rb +15 -3
  115. data/lib/ruby_smb/smb2/packet/ioctl_request.rb +2 -5
  116. data/lib/ruby_smb/smb2/packet/ioctl_response.rb +2 -1
  117. data/lib/ruby_smb/smb2/packet/logoff_request.rb +2 -4
  118. data/lib/ruby_smb/smb2/packet/logoff_response.rb +2 -1
  119. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -17
  120. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +52 -5
  121. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +2 -4
  122. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +8 -2
  123. data/lib/ruby_smb/smb2/packet/read_request.rb +2 -4
  124. data/lib/ruby_smb/smb2/packet/read_response.rb +2 -1
  125. data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
  126. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -1
  127. data/lib/ruby_smb/smb2/packet/set_info_request.rb +2 -4
  128. data/lib/ruby_smb/smb2/packet/set_info_response.rb +2 -1
  129. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  130. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +93 -10
  131. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +10 -22
  132. data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +2 -4
  133. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +2 -1
  134. data/lib/ruby_smb/smb2/packet/write_request.rb +2 -4
  135. data/lib/ruby_smb/smb2/packet/write_response.rb +2 -1
  136. data/lib/ruby_smb/smb2/pipe.rb +86 -12
  137. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  138. data/lib/ruby_smb/smb2/tree.rb +65 -21
  139. data/lib/ruby_smb/version.rb +1 -1
  140. data/ruby_smb.gemspec +5 -3
  141. data/spec/lib/ruby_smb/client_spec.rb +1612 -108
  142. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  143. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  144. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  145. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  146. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  147. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  148. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  149. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  150. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  151. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  152. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  153. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  154. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  155. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  156. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  157. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  158. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  159. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  160. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  161. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  162. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  163. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  164. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  165. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  166. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  167. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +2 -2
  168. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  169. data/spec/lib/ruby_smb/generic_packet_spec.rb +52 -4
  170. data/spec/lib/ruby_smb/smb1/file_spec.rb +191 -2
  171. data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +68 -0
  172. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  173. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  174. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  175. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  176. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +11 -2
  177. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +11 -2
  178. data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +40 -0
  179. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +272 -149
  180. data/spec/lib/ruby_smb/smb1/tree_spec.rb +44 -7
  181. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  182. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  183. data/spec/lib/ruby_smb/smb2/file_spec.rb +323 -6
  184. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  185. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  186. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +78 -0
  187. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  188. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  189. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +8 -0
  190. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  191. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  192. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -22
  193. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +286 -149
  194. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  195. data/spec/lib/ruby_smb/smb2/tree_spec.rb +261 -2
  196. metadata +191 -83
  197. metadata.gz.sig +0 -0
  198. data/lib/ruby_smb/smb1/dcerpc.rb +0 -67
  199. data/lib/ruby_smb/smb2/dcerpc.rb +0 -70
  200. data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +0 -37
@@ -39,11 +39,20 @@ module RubySMB
39
39
  # Disconnects this Tree from the current session
40
40
  #
41
41
  # @return [WindowsError::ErrorCode] the NTStatus sent back by the server.
42
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a TreeDisconnectResponse packet
42
43
  def disconnect!
43
44
  request = RubySMB::SMB1::Packet::TreeDisconnectRequest.new
44
45
  request = set_header_fields(request)
45
46
  raw_response = client.send_recv(request)
46
47
  response = RubySMB::SMB1::Packet::TreeDisconnectResponse.read(raw_response)
48
+ unless response.valid?
49
+ raise RubySMB::Error::InvalidPacket.new(
50
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
51
+ expected_cmd: RubySMB::SMB1::Packet::TreeDisconnectResponse::COMMAND,
52
+ received_proto: response.smb_header.protocol,
53
+ received_cmd: response.smb_header.command
54
+ )
55
+ end
47
56
  response.status_code
48
57
  end
49
58
 
@@ -113,11 +122,25 @@ module RubySMB
113
122
 
114
123
  raw_response = @client.send_recv(nt_create_andx_request)
115
124
  response = RubySMB::SMB1::Packet::NtCreateAndxResponse.read(raw_response)
116
- unless response.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_NT_CREATE_ANDX
117
- raise RubySMB::Error::InvalidPacket, 'Not a NtCreateAndxResponse packet'
125
+ unless response.valid?
126
+ if response.is_a?(RubySMB::SMB1::Packet::EmptyPacket) &&
127
+ response.smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
128
+ response.smb_header.command == response.original_command
129
+ raise RubySMB::Error::InvalidPacket.new(
130
+ 'The response seems to be an SMB1 NtCreateAndxResponse but an '\
131
+ 'error occurs while parsing it. It is probably missing the '\
132
+ 'required extended information.'
133
+ )
134
+ end
135
+ raise RubySMB::Error::InvalidPacket.new(
136
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
137
+ expected_cmd: RubySMB::SMB1::Packet::NtCreateAndxResponse::COMMAND,
138
+ received_proto: response.smb_header.protocol,
139
+ received_cmd: response.smb_header.command
140
+ )
118
141
  end
119
142
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
120
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
143
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
121
144
  end
122
145
 
123
146
  case response.parameter_block.resource_type
@@ -140,6 +163,8 @@ module RubySMB
140
163
  # @param pattern [String] search pattern
141
164
  # @param type [Class] file information class
142
165
  # @return [Array] array of directory structures
166
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a Trans2 packet
167
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
143
168
  def list(directory: '\\', pattern: '*', unicode: true,
144
169
  type: RubySMB::SMB1::Packet::Trans2::FindInformationLevel::FindFileFullDirectoryInfo)
145
170
  find_first_request = RubySMB::SMB1::Packet::Trans2::FindFirst2Request.new
@@ -166,6 +191,17 @@ module RubySMB
166
191
 
167
192
  raw_response = client.send_recv(find_first_request)
168
193
  response = RubySMB::SMB1::Packet::Trans2::FindFirst2Response.read(raw_response)
194
+ unless response.valid?
195
+ raise RubySMB::Error::InvalidPacket.new(
196
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
197
+ expected_cmd: RubySMB::SMB1::Packet::Trans2::FindFirst2Response::COMMAND,
198
+ received_proto: response.smb_header.protocol,
199
+ received_cmd: response.smb_header.command
200
+ )
201
+ end
202
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
203
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
204
+ end
169
205
 
170
206
  results = response.results(type, unicode: unicode)
171
207
 
@@ -190,6 +226,17 @@ module RubySMB
190
226
 
191
227
  raw_response = client.send_recv(find_next_request)
192
228
  response = RubySMB::SMB1::Packet::Trans2::FindNext2Response.read(raw_response)
229
+ unless response.valid?
230
+ raise RubySMB::Error::InvalidPacket.new(
231
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
232
+ expected_cmd: RubySMB::SMB1::Packet::Trans2::FindNext2Response::COMMAND,
233
+ received_proto: response.smb_header.protocol,
234
+ received_cmd: response.smb_header.command
235
+ )
236
+ end
237
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
238
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
239
+ end
193
240
 
194
241
  results += response.results(type, unicode: unicode)
195
242
 
@@ -4,7 +4,8 @@ module RubySMB
4
4
  # The SessionsFlags bit-field for a {RubySMB::SMB2::Packet::SessionSetupResponse}
5
5
  class SessionFlags < BinData::Record
6
6
  endian :little
7
- bit6 :reserved3, label: 'Reserved', initial_value: 0
7
+ bit5 :reserved3, label: 'Reserved', initial_value: 0
8
+ bit1 :encrypt_data, label: 'Encrypt Data', initial_value: 0
8
9
  bit1 :null, label: 'ASYNC Command', initial_value: 0
9
10
  bit1 :guest, label: 'Is Guest?', initial_value: 0
10
11
  resume_byte_alignment
@@ -8,7 +8,7 @@ module RubySMB
8
8
  bit2 :reserved1, label: 'Reserved Space'
9
9
  bit1 :vdo_caching, label: 'VDO Caching'
10
10
  bit1 :auto_caching, label: 'Auto Caching'
11
- bit2 :reserved2
11
+ bit2 :reserved2, label: 'Reserved Space'
12
12
  bit1 :dfs_root, label: 'DFS Root'
13
13
  bit1 :dfs, label: 'DFS'
14
14
  # byte boundary
@@ -20,9 +20,11 @@ module RubySMB
20
20
  bit1 :namespace_caching, label: 'Namespace Caching'
21
21
  bit1 :shared_delete, label: 'Force Shared Delete'
22
22
  bit1 :restrict_exclusive_opens, label: 'Restrict Exclusive Opens'
23
-
24
- bit8 :reserved3, label: 'Reserved Space'
25
- bit8 :reserved4, label: 'Reserved Space'
23
+ # byte boundary
24
+ bit5 :reserved3, label: 'Reserved Space'
25
+ bit1 :identity_remoting, label: 'Identity Remoting'
26
+ bit2 :reserved4, label: 'Reserved Space'
27
+ bit8 :reserved5, label: 'Reserved Space'
26
28
 
27
29
  def caching_type
28
30
  if vdo_caching == 1 && auto_caching.zero?
@@ -52,7 +52,12 @@ module RubySMB
52
52
  # @return [RubySMB::SMB2::Tree]
53
53
  attr_accessor :tree
54
54
 
55
- def initialize(tree:, response:, name:)
55
+ # Whether or not the share associated with this tree connect needs to be encrypted (SMB 3.x)
56
+ # @!attribute [rw] tree_connect_encrypt_data
57
+ # @return [Boolean]
58
+ attr_accessor :tree_connect_encrypt_data
59
+
60
+ def initialize(tree:, response:, name:, encrypt: false)
56
61
  raise ArgumentError, 'No Tree Provided' if tree.nil?
57
62
  raise ArgumentError, 'No Response Provided' if response.nil?
58
63
 
@@ -66,6 +71,7 @@ module RubySMB
66
71
  @last_write = response.last_write.to_datetime
67
72
  @size = response.end_of_file
68
73
  @size_on_disk = response.allocation_size
74
+ @tree_connect_encrypt_data = encrypt
69
75
  end
70
76
 
71
77
  # Appends the supplied data to the end of the file.
@@ -79,11 +85,24 @@ module RubySMB
79
85
  # Closes the handle to the remote file.
80
86
  #
81
87
  # @return [WindowsError::ErrorCode] the NTStatus code returned by the operation
88
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a CloseResponse packet
89
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
82
90
  def close
83
91
  close_request = set_header_fields(RubySMB::SMB2::Packet::CloseRequest.new)
84
- raw_response = tree.client.send_recv(close_request)
92
+ raw_response = tree.client.send_recv(close_request, encrypt: @tree_connect_encrypt_data)
85
93
  response = RubySMB::SMB2::Packet::CloseResponse.read(raw_response)
86
- response.smb2_header.nt_status.to_nt_status
94
+ unless response.valid?
95
+ raise RubySMB::Error::InvalidPacket.new(
96
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
97
+ expected_cmd: RubySMB::SMB2::Packet::CloseResponse::COMMAND,
98
+ received_proto: response.smb2_header.protocol,
99
+ received_cmd: response.smb2_header.command
100
+ )
101
+ end
102
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
103
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
104
+ end
105
+ response.status_code
87
106
  end
88
107
 
89
108
  # Read from the file, a specific number of bytes
@@ -93,6 +112,8 @@ module RubySMB
93
112
  # @param bytes [Integer] the number of bytes to read
94
113
  # @param offset [Integer] the byte offset in the file to start reading from
95
114
  # @return [String] the data read from the file
115
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a ReadResponse packet
116
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
96
117
  def read(bytes: size, offset: 0)
97
118
  atomic_read_size = if bytes > tree.client.server_max_read_size
98
119
  tree.client.server_max_read_size
@@ -101,8 +122,19 @@ module RubySMB
101
122
  end
102
123
 
103
124
  read_request = read_packet(read_length: atomic_read_size, offset: offset)
104
- raw_response = tree.client.send_recv(read_request)
125
+ raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
105
126
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
127
+ unless response.valid?
128
+ raise RubySMB::Error::InvalidPacket.new(
129
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
130
+ expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
131
+ received_proto: response.smb2_header.protocol,
132
+ received_cmd: response.smb2_header.command
133
+ )
134
+ end
135
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
136
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
137
+ end
106
138
 
107
139
  data = response.buffer.to_binary_s
108
140
 
@@ -113,8 +145,19 @@ module RubySMB
113
145
  atomic_read_size = remaining_bytes if remaining_bytes < tree.client.server_max_read_size
114
146
 
115
147
  read_request = read_packet(read_length: atomic_read_size, offset: offset)
116
- raw_response = tree.client.send_recv(read_request)
148
+ raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
117
149
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
150
+ unless response.valid?
151
+ raise RubySMB::Error::InvalidPacket.new(
152
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
153
+ expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
154
+ received_proto: response.smb2_header.protocol,
155
+ received_cmd: response.smb2_header.command
156
+ )
157
+ end
158
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
159
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
160
+ end
118
161
 
119
162
  data << response.buffer.to_binary_s
120
163
  remaining_bytes -= atomic_read_size
@@ -133,17 +176,21 @@ module RubySMB
133
176
  read_request.offset = offset
134
177
  read_request
135
178
  end
136
-
179
+
137
180
  def send_recv_read(read_length: 0, offset: 0)
138
181
  read_request = read_packet(read_length: read_length, offset: offset)
139
- raw_response = tree.client.send_recv(read_request)
182
+ raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
140
183
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
141
- if response.status_code == WindowsError::NTStatus::STATUS_PENDING
142
- sleep 1
143
- raw_response = tree.client.dispatcher.recv_packet
144
- response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
145
- elsif response.status_code != WindowsError::NTStatus::STATUS_SUCCESS
146
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
184
+ unless response.valid?
185
+ raise RubySMB::Error::InvalidPacket.new(
186
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
187
+ expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
188
+ received_proto: response.smb2_header.protocol,
189
+ received_cmd: response.smb2_header.command
190
+ )
191
+ end
192
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
193
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
147
194
  end
148
195
  response.buffer.to_binary_s
149
196
  end
@@ -151,9 +198,18 @@ module RubySMB
151
198
  # Delete a file on close
152
199
  #
153
200
  # @return [WindowsError::ErrorCode] the NTStatus Response code
201
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
154
202
  def delete
155
- raw_response = tree.client.send_recv(delete_packet)
203
+ raw_response = tree.client.send_recv(delete_packet, encrypt: @tree_connect_encrypt_data)
156
204
  response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
205
+ unless response.valid?
206
+ raise RubySMB::Error::InvalidPacket.new(
207
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
208
+ expected_cmd: RubySMB::SMB2::Packet::SetInfoResponse::COMMAND,
209
+ received_proto: response.smb2_header.protocol,
210
+ received_cmd: response.smb2_header.command
211
+ )
212
+ end
157
213
  response.smb2_header.nt_status.to_nt_status
158
214
  end
159
215
 
@@ -182,6 +238,7 @@ module RubySMB
182
238
  # @param data [String] the data to write to the file
183
239
  # @param offset [Integer] the offset in the file to start writing from
184
240
  # @return [WindowsError::ErrorCode] the NTStatus code returned from the operation
241
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a WriteResponse packet
185
242
  def write(data:'', offset: 0)
186
243
  buffer = data.dup
187
244
  bytes = data.length
@@ -193,8 +250,16 @@ module RubySMB
193
250
 
194
251
  while buffer.length > 0 do
195
252
  write_request = write_packet(data: buffer.slice!(0,atomic_write_size), offset: offset)
196
- raw_response = tree.client.send_recv(write_request)
253
+ raw_response = tree.client.send_recv(write_request, encrypt: @tree_connect_encrypt_data)
197
254
  response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
255
+ unless response.valid?
256
+ raise RubySMB::Error::InvalidPacket.new(
257
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
258
+ expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
259
+ received_proto: response.smb2_header.protocol,
260
+ received_cmd: response.smb2_header.command
261
+ )
262
+ end
198
263
  status = response.smb2_header.nt_status.to_nt_status
199
264
 
200
265
  offset+= atomic_write_size
@@ -215,28 +280,41 @@ module RubySMB
215
280
  write_request.buffer = data
216
281
  write_request
217
282
  end
218
-
283
+
219
284
  def send_recv_write(data:'', offset: 0)
220
285
  pkt = write_packet(data: data, offset: offset)
221
- raw_response = tree.client.send_recv(pkt)
286
+ raw_response = tree.client.send_recv(pkt, encrypt: @tree_connect_encrypt_data)
222
287
  response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
223
- if response.status_code == WindowsError::NTStatus::STATUS_PENDING
224
- sleep 1
225
- raw_response = tree.client.dispatcher.recv_packet
226
- response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
227
- elsif response.status_code != WindowsError::NTStatus::STATUS_SUCCESS
228
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
288
+ unless response.valid?
289
+ raise RubySMB::Error::InvalidPacket.new(
290
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
291
+ expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
292
+ received_proto: response.smb2_header.protocol,
293
+ received_cmd: response.smb2_header.command
294
+ )
295
+ end
296
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
297
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
229
298
  end
230
299
  response.write_count
231
300
  end
232
-
301
+
233
302
  # Rename a file
234
303
  #
235
304
  # @param new_file_name [String] the new name
236
305
  # @return [WindowsError::ErrorCode] the NTStatus Response code
306
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
237
307
  def rename(new_file_name)
238
- raw_response = tree.client.send_recv(rename_packet(new_file_name))
308
+ raw_response = tree.client.send_recv(rename_packet(new_file_name), encrypt: @tree_connect_encrypt_data)
239
309
  response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
310
+ unless response.valid?
311
+ raise RubySMB::Error::InvalidPacket.new(
312
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
313
+ expected_cmd: RubySMB::SMB2::Packet::SetInfoResponse::COMMAND,
314
+ received_proto: response.smb2_header.protocol,
315
+ received_cmd: response.smb2_header.command
316
+ )
317
+ end
240
318
  response.smb2_header.nt_status.to_nt_status
241
319
  end
242
320
 
@@ -0,0 +1,108 @@
1
+ module RubySMB
2
+ module SMB2
3
+
4
+ # An SMB2 PREAUTH_INTEGRITY_CAPABILITIES context struct as defined in
5
+ # [2.2.3.1.1 SMB2_PREAUTH_INTEGRITY_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5a07bd66-4734-4af8-abcf-5a44ff7ee0e5)
6
+ class PreauthIntegrityCapabilities < BinData::Record
7
+ SHA_512 = 0x0001
8
+ HASH_ALGORITM_MAP = {
9
+ SHA_512 => 'SHA512'
10
+ }
11
+
12
+ endian :little
13
+
14
+ uint16 :hash_algorithm_count, label: 'Hash Algorithm Count', initial_value: -> { hash_algorithms.size }
15
+ uint16 :salt_length, label: 'Salt Length', initial_value: -> { salt.num_bytes }
16
+ array :hash_algorithms, label: 'Hash Algorithms', type: :uint16, initial_length: -> { hash_algorithm_count }
17
+ string :salt, label: 'Salt', read_length: -> { salt_length }
18
+ end
19
+
20
+ # An SMB2 ENCRYPTION_CAPABILITIES context struct as defined in
21
+ # [2.2.3.1.2 SMB2_ENCRYPTION_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/16693be7-2b27-4d3b-804b-f605bde5bcdd)
22
+ class EncryptionCapabilities < BinData::Record
23
+ AES_128_CCM = 0x0001
24
+ AES_128_GCM = 0x0002
25
+ ENCRYPTION_ALGORITHM_MAP = {
26
+ AES_128_CCM => 'AES-128-CCM',
27
+ AES_128_GCM => 'AES-128-GCM'
28
+ }
29
+
30
+ endian :little
31
+
32
+ uint16 :cipher_count, label: 'Cipher Count', initial_value: -> { ciphers.size }
33
+ array :ciphers, label: 'Ciphers', type: :uint16, initial_length: -> { cipher_count }
34
+ end
35
+
36
+ # An SMB2 COMPRESSION_CAPABILITIES context struct as defined in
37
+ # [2.2.3.1.3 SMB2_COMPRESSION_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/78e0c942-ab41-472b-b117-4a95ebe88271)
38
+ class CompressionCapabilities < BinData::Record
39
+ # Flags
40
+ # Chained compression is not supported.
41
+ SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE = 0x00000000
42
+ # Chained compression is supported on this connection.
43
+ SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED = 0x00000001
44
+
45
+ # Compression Algorithms
46
+ NONE = 0x0000
47
+ LZNT1 = 0x0001
48
+ LZ77 = 0x0002
49
+ LZ77_Huffman = 0x0003
50
+ Pattern_V1 = 0x0004
51
+ COMPRESSION_ALGORITHM_MAP = {
52
+ NONE => 'NONE',
53
+ LZNT1 => 'LZNT1',
54
+ LZ77 => 'LZ77',
55
+ LZ77_Huffman => 'LZ77_Huffman',
56
+ Pattern_V1 => 'Pattern_V1'
57
+ }
58
+
59
+ endian :little
60
+
61
+ uint16 :compression_algorithm_count, label: 'Compression Algorithm Count', initial_value: -> { compression_algorithms.size }
62
+ uint16 :padding, label: 'Padding', initial_value: 0
63
+ uint32 :flags, label: 'Flags'
64
+ array :compression_algorithms, label: 'Compression Algorithms', type: :uint16, initial_length: -> { compression_algorithm_count }
65
+ end
66
+
67
+ # An SMB2 NETNAME_NEGOTIATE_CONTEXT_ID context struct as defined in
68
+ # [2.2.3.1.4 SMB2_NETNAME_NEGOTIATE_CONTEXT_ID](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/ca6726bd-b9cf-43d9-b0bc-d127d3c993b3)
69
+ class NetnameNegotiateContextId < BinData::Record
70
+ endian :little
71
+
72
+ stringz16 :net_name, label: 'Net Name'
73
+ end
74
+
75
+
76
+ # An SMB2 NEGOTIATE_CONTEXT struct as defined in
77
+ # [2.2.3.1 SMB2 NEGOTIATE_CONTEXT Request Values](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/15332256-522e-4a53-8cd7-0bd17678a2f7)
78
+ class NegotiateContext < BinData::Record
79
+ # The NegotiateContext Data field contains a list of preauthentication integrity hash functions as well as an optional salt value, as specified in section 2.2.3.1.1.
80
+ SMB2_PREAUTH_INTEGRITY_CAPABILITIES = 0x0001
81
+ # The NegotiateContext Data field contains a list of encryption algorithms, as specified in section 2.2.3.1.2.
82
+ SMB2_ENCRYPTION_CAPABILITIES = 0x0002
83
+ # The NegotiateContext Data field contains a list of compression algorithms, as specified in section 2.2.3.1.3.
84
+ SMB2_COMPRESSION_CAPABILITIES = 0x0003
85
+ # The NegotiateContext Data field contains the server name to which the client connects.
86
+ SMB2_NETNAME_NEGOTIATE_CONTEXT_ID = 0x0005
87
+
88
+ endian :little
89
+
90
+ string :pad, label: 'Padding', length: -> { pad_length }
91
+ uint16 :context_type, label: 'Context Type'
92
+ uint16 :data_length, label: 'Data Length', initial_value: -> { data.num_bytes }
93
+ uint32 :reserved, label: 'Reserved', initial_value: 0
94
+ choice :data, label: 'Data', selection: -> { context_type } do
95
+ preauth_integrity_capabilities SMB2_PREAUTH_INTEGRITY_CAPABILITIES, label: 'Preauthentication Integrity Capabilities'
96
+ encryption_capabilities SMB2_ENCRYPTION_CAPABILITIES, label: 'Encryption Capabilities'
97
+ compression_capabilities SMB2_COMPRESSION_CAPABILITIES, label: 'Compression Capabilities'
98
+ netname_negotiate_context_id SMB2_NETNAME_NEGOTIATE_CONTEXT_ID, label: 'Netname Negotiate Context ID'
99
+ end
100
+
101
+ def pad_length
102
+ offset = pad.abs_offset % 8
103
+ (8 - offset) % 8
104
+ end
105
+
106
+ end
107
+ end
108
+ end