ruby_smb 1.1.0 → 2.0.4

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 (163) 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 -5
  5. data/Gemfile +6 -2
  6. data/examples/anonymous_auth.rb +3 -3
  7. data/examples/append_file.rb +10 -8
  8. data/examples/authenticate.rb +9 -5
  9. data/examples/delete_file.rb +8 -6
  10. data/examples/enum_registry_key.rb +5 -4
  11. data/examples/enum_registry_values.rb +5 -4
  12. data/examples/list_directory.rb +8 -6
  13. data/examples/negotiate.rb +51 -8
  14. data/examples/negotiate_with_netbios_service.rb +9 -5
  15. data/examples/net_share_enum_all.rb +6 -4
  16. data/examples/pipes.rb +11 -12
  17. data/examples/query_service_status.rb +64 -0
  18. data/examples/read_file.rb +8 -6
  19. data/examples/read_file_encryption.rb +56 -0
  20. data/examples/read_registry_key_value.rb +6 -5
  21. data/examples/rename_file.rb +9 -7
  22. data/examples/tree_connect.rb +7 -5
  23. data/examples/write_file.rb +9 -7
  24. data/lib/ruby_smb.rb +4 -0
  25. data/lib/ruby_smb/client.rb +246 -26
  26. data/lib/ruby_smb/client/authentication.rb +32 -18
  27. data/lib/ruby_smb/client/echo.rb +2 -4
  28. data/lib/ruby_smb/client/encryption.rb +62 -0
  29. data/lib/ruby_smb/client/negotiation.rb +156 -16
  30. data/lib/ruby_smb/client/signing.rb +19 -0
  31. data/lib/ruby_smb/client/tree_connect.rb +6 -8
  32. data/lib/ruby_smb/client/utils.rb +24 -17
  33. data/lib/ruby_smb/client/winreg.rb +1 -1
  34. data/lib/ruby_smb/crypto.rb +30 -0
  35. data/lib/ruby_smb/dcerpc.rb +2 -0
  36. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  37. data/lib/ruby_smb/dcerpc/ndr.rb +209 -44
  38. data/lib/ruby_smb/dcerpc/request.rb +13 -0
  39. data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
  40. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +9 -6
  41. data/lib/ruby_smb/dcerpc/svcctl.rb +479 -0
  42. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
  43. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
  44. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
  45. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
  46. data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
  47. data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
  48. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
  49. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
  50. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
  51. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
  52. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
  53. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
  54. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
  55. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
  56. data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
  57. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
  58. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
  59. data/lib/ruby_smb/dcerpc/winreg.rb +98 -17
  60. data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +73 -0
  61. data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
  62. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +1 -1
  63. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +1 -1
  64. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +1 -1
  65. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +4 -4
  66. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +1 -1
  67. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +7 -6
  68. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +10 -10
  69. data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +37 -0
  70. data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
  71. data/lib/ruby_smb/dispatcher/base.rb +1 -1
  72. data/lib/ruby_smb/dispatcher/socket.rb +5 -4
  73. data/lib/ruby_smb/error.rb +49 -6
  74. data/lib/ruby_smb/field/stringz16.rb +17 -1
  75. data/lib/ruby_smb/generic_packet.rb +11 -1
  76. data/lib/ruby_smb/nbss/session_header.rb +4 -4
  77. data/lib/ruby_smb/smb1/commands.rb +1 -1
  78. data/lib/ruby_smb/smb1/file.rb +13 -28
  79. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
  80. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
  81. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  82. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
  83. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
  84. data/lib/ruby_smb/smb1/pipe.rb +8 -8
  85. data/lib/ruby_smb/smb1/tree.rb +25 -12
  86. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  87. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  88. data/lib/ruby_smb/smb2/file.rb +59 -77
  89. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  90. data/lib/ruby_smb/smb2/packet.rb +2 -0
  91. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  92. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
  93. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
  94. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  95. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
  96. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
  97. data/lib/ruby_smb/smb2/pipe.rb +8 -20
  98. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  99. data/lib/ruby_smb/smb2/tree.rb +44 -28
  100. data/lib/ruby_smb/version.rb +1 -1
  101. data/ruby_smb.gemspec +3 -1
  102. data/spec/lib/ruby_smb/client_spec.rb +1408 -70
  103. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  104. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1396 -77
  105. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
  106. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +49 -12
  107. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
  108. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
  109. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
  110. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
  111. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
  112. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
  113. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
  114. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
  115. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
  116. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
  117. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
  118. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
  119. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
  120. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
  121. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
  122. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
  123. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
  124. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
  125. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
  126. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
  127. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +0 -4
  128. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +2 -2
  129. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +2 -2
  130. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +9 -4
  131. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +0 -4
  132. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +17 -17
  133. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +11 -23
  134. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
  135. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
  136. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +227 -41
  137. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
  138. data/spec/lib/ruby_smb/error_spec.rb +88 -0
  139. data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
  140. data/spec/lib/ruby_smb/generic_packet_spec.rb +7 -0
  141. data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
  142. data/spec/lib/ruby_smb/smb1/file_spec.rb +1 -3
  143. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  144. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  145. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  146. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  147. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +30 -5
  148. data/spec/lib/ruby_smb/smb1/tree_spec.rb +22 -0
  149. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  150. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  151. data/spec/lib/ruby_smb/smb2/file_spec.rb +147 -71
  152. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  153. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  154. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  155. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  156. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  157. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  158. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
  159. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +9 -45
  160. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  161. data/spec/lib/ruby_smb/smb2/tree_spec.rb +111 -9
  162. metadata +194 -75
  163. metadata.gz.sig +2 -1
@@ -4,7 +4,7 @@ module RubySMB
4
4
  # A SMB1 SMB_COM_SESSION_SETUP_ANDX Request Packet, without NTLMSSP as defined in
5
5
  # [2.2.4.53.1 Request](https://msdn.microsoft.com/en-us/library/ee441849.aspx)
6
6
  class SessionSetupLegacyRequest < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
8
8
 
9
9
  # A SMB1 Parameter Block as defined by the {SessionSetupRequest}
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
@@ -1,10 +1,10 @@
1
1
  module RubySMB
2
2
  module SMB1
3
3
  module Packet
4
- # A SMB1 SMB_COM_SESSION_SETUP Legacy Response Packet as defined in
4
+ # A SMB1 SMB_COM_SESSION_SETUP_ANDX Legacy Response Packet as defined in
5
5
  # [2.2.4.53.2 Response](https://msdn.microsoft.com/en-us/library/ee442143.aspx)
6
6
  class SessionSetupLegacyResponse < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
8
8
 
9
9
  # A SMB1 Parameter Block as defined by the {SessionSetupResponse}
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
@@ -4,7 +4,7 @@ module RubySMB
4
4
  # A SMB1 SMB_COM_SESSION_SETUP_ANDX Request Packet as defined in
5
5
  # [2.2.4.6.1](https://msdn.microsoft.com/en-us/library/cc246328.aspx)
6
6
  class SessionSetupRequest < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
8
8
 
9
9
  # A SMB1 Parameter Block as defined by the {SessionSetupRequest}
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
@@ -1,10 +1,10 @@
1
1
  module RubySMB
2
2
  module SMB1
3
3
  module Packet
4
- # A SMB1 SMB_COM_SESSION_SETUP Response Packet as defined in
4
+ # A SMB1 SMB_COM_SESSION_SETUP_ANDX Response Packet as defined in
5
5
  # [2.2.4.6.2](https://msdn.microsoft.com/en-us/library/cc246329.aspx)
6
6
  class SessionSetupResponse < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
8
8
 
9
9
  # A SMB1 Parameter Block as defined by the {SessionSetupResponse}
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
@@ -43,7 +43,7 @@ module RubySMB
43
43
 
44
44
  # Represents the specific layout of the DataBlock for a {WriteAndxRequest} Packet.
45
45
  class DataBlock < RubySMB::SMB1::DataBlock
46
- uint8 :pad, label: 'Pad'
46
+ string :pad, label: 'Pad', length: 1
47
47
  string :data, label: 'Data'
48
48
  end
49
49
 
@@ -16,10 +16,12 @@ module RubySMB
16
16
  def initialize(tree:, response:, name:)
17
17
  raise ArgumentError, 'No Name Provided' if name.nil?
18
18
  case name
19
- when 'srvsvc'
19
+ when 'srvsvc', '\\srvsvc'
20
20
  extend RubySMB::Dcerpc::Srvsvc
21
- when 'winreg'
21
+ when 'winreg', '\\winreg'
22
22
  extend RubySMB::Dcerpc::Winreg
23
+ when 'svcctl', '\\svcctl'
24
+ extend RubySMB::Dcerpc::Svcctl
23
25
  end
24
26
  super(tree: tree, response: response, name: name)
25
27
  end
@@ -41,13 +43,12 @@ module RubySMB
41
43
  raise RubySMB::Error::InvalidPacket.new(
42
44
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
43
45
  expected_cmd: RubySMB::SMB1::Packet::Trans::PeekNmpipeRequest::COMMAND,
44
- received_proto: response.smb_header.protocol,
45
- received_cmd: response.smb_header.command
46
+ packet: response
46
47
  )
47
48
  end
48
49
 
49
50
  unless response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW or response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
50
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
51
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
51
52
  end
52
53
 
53
54
  response
@@ -100,13 +101,12 @@ module RubySMB
100
101
  raise RubySMB::Error::InvalidPacket.new(
101
102
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
102
103
  expected_cmd: RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse::COMMAND,
103
- received_proto: trans_nmpipe_response.smb_header.protocol,
104
- received_cmd: trans_nmpipe_response.smb_header.command
104
+ packet: trans_nmpipe_response
105
105
  )
106
106
  end
107
107
  unless [WindowsError::NTStatus::STATUS_SUCCESS,
108
108
  WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW].include?(trans_nmpipe_response.status_code)
109
- raise RubySMB::Error::UnexpectedStatusCode, trans_nmpipe_response.status_code.name
109
+ raise RubySMB::Error::UnexpectedStatusCode, trans_nmpipe_response.status_code
110
110
  end
111
111
 
112
112
  raw_data = trans_nmpipe_response.data_block.trans_data.read_data.to_binary_s
@@ -49,13 +49,20 @@ module RubySMB
49
49
  raise RubySMB::Error::InvalidPacket.new(
50
50
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
51
51
  expected_cmd: RubySMB::SMB1::Packet::TreeDisconnectResponse::COMMAND,
52
- received_proto: response.smb_header.protocol,
53
- received_cmd: response.smb_header.command
52
+ packet: response
54
53
  )
55
54
  end
56
55
  response.status_code
57
56
  end
58
57
 
58
+ def open_pipe(opts)
59
+ # Make sure we don't modify the caller's hash options
60
+ opts = opts.dup
61
+ opts[:filename] = opts[:filename].dup
62
+ opts[:filename].prepend('\\') unless opts[:filename].start_with?('\\')
63
+ open_file(opts)
64
+ end
65
+
59
66
  # Open a file on the remote share.
60
67
  #
61
68
  # @example
@@ -123,18 +130,26 @@ module RubySMB
123
130
  raw_response = @client.send_recv(nt_create_andx_request)
124
131
  response = RubySMB::SMB1::Packet::NtCreateAndxResponse.read(raw_response)
125
132
  unless response.valid?
133
+ if response.is_a?(RubySMB::SMB1::Packet::EmptyPacket) &&
134
+ response.smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
135
+ response.smb_header.command == response.original_command
136
+ raise RubySMB::Error::InvalidPacket.new(
137
+ 'The response seems to be an SMB1 NtCreateAndxResponse but an '\
138
+ 'error occurs while parsing it. It is probably missing the '\
139
+ 'required extended information.'
140
+ )
141
+ end
126
142
  raise RubySMB::Error::InvalidPacket.new(
127
143
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
128
144
  expected_cmd: RubySMB::SMB1::Packet::NtCreateAndxResponse::COMMAND,
129
- received_proto: response.smb_header.protocol,
130
- received_cmd: response.smb_header.command
145
+ packet: response
131
146
  )
132
147
  end
133
148
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
134
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
149
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
135
150
  end
136
151
 
137
- case response.parameter_block.resource_type
152
+ case response.parameter_block.resource_type
138
153
  when RubySMB::SMB1::ResourceType::BYTE_MODE_PIPE, RubySMB::SMB1::ResourceType::MESSAGE_MODE_PIPE
139
154
  RubySMB::SMB1::Pipe.new(name: filename, tree: self, response: response)
140
155
  when RubySMB::SMB1::ResourceType::DISK
@@ -186,12 +201,11 @@ module RubySMB
186
201
  raise RubySMB::Error::InvalidPacket.new(
187
202
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
188
203
  expected_cmd: RubySMB::SMB1::Packet::Trans2::FindFirst2Response::COMMAND,
189
- received_proto: response.smb_header.protocol,
190
- received_cmd: response.smb_header.command
204
+ packet: response
191
205
  )
192
206
  end
193
207
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
194
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
208
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
195
209
  end
196
210
 
197
211
  results = response.results(type, unicode: unicode)
@@ -221,12 +235,11 @@ module RubySMB
221
235
  raise RubySMB::Error::InvalidPacket.new(
222
236
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
223
237
  expected_cmd: RubySMB::SMB1::Packet::Trans2::FindNext2Response::COMMAND,
224
- received_proto: response.smb_header.protocol,
225
- received_cmd: response.smb_header.command
238
+ packet: response
226
239
  )
227
240
  end
228
241
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
229
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
242
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
230
243
  end
231
244
 
232
245
  results += response.results(type, unicode: unicode)
@@ -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.
@@ -83,18 +89,17 @@ module RubySMB
83
89
  # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
84
90
  def close
85
91
  close_request = set_header_fields(RubySMB::SMB2::Packet::CloseRequest.new)
86
- raw_response = tree.client.send_recv(close_request)
92
+ raw_response = tree.client.send_recv(close_request, encrypt: @tree_connect_encrypt_data)
87
93
  response = RubySMB::SMB2::Packet::CloseResponse.read(raw_response)
88
94
  unless response.valid?
89
95
  raise RubySMB::Error::InvalidPacket.new(
90
96
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
91
97
  expected_cmd: RubySMB::SMB2::Packet::CloseResponse::COMMAND,
92
- received_proto: response.smb2_header.protocol,
93
- received_cmd: response.smb2_header.command
98
+ packet: response
94
99
  )
95
100
  end
96
101
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
97
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
102
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
98
103
  end
99
104
  response.status_code
100
105
  end
@@ -109,25 +114,26 @@ module RubySMB
109
114
  # @raise [RubySMB::Error::InvalidPacket] if the response is not a ReadResponse packet
110
115
  # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
111
116
  def read(bytes: size, offset: 0)
112
- atomic_read_size = if bytes > tree.client.server_max_read_size
113
- tree.client.server_max_read_size
114
- else
115
- bytes
116
- end
117
-
118
- read_request = read_packet(read_length: atomic_read_size, offset: offset)
119
- raw_response = tree.client.send_recv(read_request)
117
+ max_read = tree.client.server_max_read_size
118
+ max_read = 65536 unless tree.client.server_supports_multi_credit
119
+ atomic_read_size = [bytes, max_read].min
120
+ credit_charge = 0
121
+ if tree.client.server_supports_multi_credit
122
+ credit_charge = (atomic_read_size - 1) / 65536 + 1
123
+ end
124
+
125
+ read_request = read_packet(read_length: atomic_read_size, offset: offset, credit_charge: credit_charge)
126
+ raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
120
127
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
121
128
  unless response.valid?
122
129
  raise RubySMB::Error::InvalidPacket.new(
123
130
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
124
131
  expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
125
- received_proto: response.smb2_header.protocol,
126
- received_cmd: response.smb2_header.command
132
+ packet: response
127
133
  )
128
134
  end
129
135
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
130
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
136
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
131
137
  end
132
138
 
133
139
  data = response.buffer.to_binary_s
@@ -136,21 +142,20 @@ module RubySMB
136
142
 
137
143
  while remaining_bytes > 0
138
144
  offset += atomic_read_size
139
- atomic_read_size = remaining_bytes if remaining_bytes < tree.client.server_max_read_size
145
+ atomic_read_size = remaining_bytes if remaining_bytes < max_read
140
146
 
141
- read_request = read_packet(read_length: atomic_read_size, offset: offset)
142
- raw_response = tree.client.send_recv(read_request)
147
+ read_request = read_packet(read_length: atomic_read_size, offset: offset, credit_charge: credit_charge)
148
+ raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
143
149
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
144
150
  unless response.valid?
145
151
  raise RubySMB::Error::InvalidPacket.new(
146
152
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
147
153
  expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
148
- received_proto: response.smb2_header.protocol,
149
- received_cmd: response.smb2_header.command
154
+ packet: response
150
155
  )
151
156
  end
152
157
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
153
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
158
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
154
159
  end
155
160
 
156
161
  data << response.buffer.to_binary_s
@@ -163,40 +168,29 @@ module RubySMB
163
168
  #
164
169
  # @param bytes [Integer] the number of bytes to read
165
170
  # @param offset [Integer] the byte offset in the file to start reading from
171
+ # @param credit_charge [Integer] the number of credits that this request consumes
166
172
  # @return [RubySMB::SMB2::Packet::ReadRequest] the data read from the file
167
- def read_packet(read_length: 0, offset: 0)
173
+ def read_packet(read_length: 0, offset: 0, credit_charge: 1)
168
174
  read_request = set_header_fields(RubySMB::SMB2::Packet::ReadRequest.new)
169
175
  read_request.read_length = read_length
170
176
  read_request.offset = offset
177
+ read_request.smb2_header.credit_charge = credit_charge
171
178
  read_request
172
179
  end
173
-
180
+
174
181
  def send_recv_read(read_length: 0, offset: 0)
175
182
  read_request = read_packet(read_length: read_length, offset: offset)
176
- raw_response = tree.client.send_recv(read_request)
183
+ raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
177
184
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
178
185
  unless response.valid?
179
186
  raise RubySMB::Error::InvalidPacket.new(
180
187
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
181
188
  expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
182
- received_proto: response.smb2_header.protocol,
183
- received_cmd: response.smb2_header.command
189
+ packet: response
184
190
  )
185
191
  end
186
- if response.status_code == WindowsError::NTStatus::STATUS_PENDING
187
- sleep 1
188
- raw_response = tree.client.dispatcher.recv_packet
189
- response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
190
- unless response.valid?
191
- raise RubySMB::Error::InvalidPacket.new(
192
- expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
193
- expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
194
- received_proto: response.smb2_header.protocol,
195
- received_cmd: response.smb2_header.command
196
- )
197
- end
198
- elsif response.status_code != WindowsError::NTStatus::STATUS_SUCCESS
199
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
192
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
193
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
200
194
  end
201
195
  response.buffer.to_binary_s
202
196
  end
@@ -206,14 +200,13 @@ module RubySMB
206
200
  # @return [WindowsError::ErrorCode] the NTStatus Response code
207
201
  # @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
208
202
  def delete
209
- raw_response = tree.client.send_recv(delete_packet)
203
+ raw_response = tree.client.send_recv(delete_packet, encrypt: @tree_connect_encrypt_data)
210
204
  response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
211
205
  unless response.valid?
212
206
  raise RubySMB::Error::InvalidPacket.new(
213
207
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
214
208
  expected_cmd: RubySMB::SMB2::Packet::SetInfoResponse::COMMAND,
215
- received_proto: response.smb2_header.protocol,
216
- received_cmd: response.smb2_header.command
209
+ packet: response
217
210
  )
218
211
  end
219
212
  response.smb2_header.nt_status.to_nt_status
@@ -246,29 +239,30 @@ module RubySMB
246
239
  # @return [WindowsError::ErrorCode] the NTStatus code returned from the operation
247
240
  # @raise [RubySMB::Error::InvalidPacket] if the response is not a WriteResponse packet
248
241
  def write(data:'', offset: 0)
242
+ max_write = tree.client.server_max_write_size
243
+ max_write = 65536 unless tree.client.server_supports_multi_credit
249
244
  buffer = data.dup
250
245
  bytes = data.length
251
- atomic_write_size = if bytes > tree.client.server_max_write_size
252
- tree.client.server_max_write_size
253
- else
254
- bytes
255
- end
246
+ atomic_write_size = [bytes, max_write].min
247
+ credit_charge = 0
248
+ if tree.client.server_supports_multi_credit
249
+ credit_charge = (atomic_write_size - 1) / 65536 + 1
250
+ end
256
251
 
257
252
  while buffer.length > 0 do
258
- write_request = write_packet(data: buffer.slice!(0,atomic_write_size), offset: offset)
259
- raw_response = tree.client.send_recv(write_request)
253
+ write_request = write_packet(data: buffer.slice!(0, atomic_write_size), offset: offset, credit_charge: credit_charge)
254
+ raw_response = tree.client.send_recv(write_request, encrypt: @tree_connect_encrypt_data)
260
255
  response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
261
256
  unless response.valid?
262
257
  raise RubySMB::Error::InvalidPacket.new(
263
258
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
264
259
  expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
265
- received_proto: response.smb2_header.protocol,
266
- received_cmd: response.smb2_header.command
260
+ packet: response
267
261
  )
268
262
  end
269
263
  status = response.smb2_header.nt_status.to_nt_status
270
264
 
271
- offset+= atomic_write_size
265
+ offset += atomic_write_size
272
266
  return status unless status == WindowsError::NTStatus::STATUS_SUCCESS
273
267
  end
274
268
 
@@ -279,58 +273,46 @@ module RubySMB
279
273
  #
280
274
  # @param data [String] the data to write to the file
281
275
  # @param offset [Integer] the offset in the file to start writing from
276
+ # @param credit_charge [Integer] the number of credits that this request consumes
282
277
  # @return []RubySMB::SMB2::Packet::WriteRequest] the request packet
283
- def write_packet(data:'', offset: 0)
278
+ def write_packet(data:'', offset: 0, credit_charge: 1)
284
279
  write_request = set_header_fields(RubySMB::SMB2::Packet::WriteRequest.new)
285
280
  write_request.write_offset = offset
286
281
  write_request.buffer = data
282
+ write_request.smb2_header.credit_charge = credit_charge
287
283
  write_request
288
284
  end
289
-
285
+
290
286
  def send_recv_write(data:'', offset: 0)
291
287
  pkt = write_packet(data: data, offset: offset)
292
- raw_response = tree.client.send_recv(pkt)
288
+ raw_response = tree.client.send_recv(pkt, encrypt: @tree_connect_encrypt_data)
293
289
  response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
294
290
  unless response.valid?
295
291
  raise RubySMB::Error::InvalidPacket.new(
296
292
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
297
293
  expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
298
- received_proto: response.smb2_header.protocol,
299
- received_cmd: response.smb2_header.command
294
+ packet: response
300
295
  )
301
296
  end
302
- if response.status_code == WindowsError::NTStatus::STATUS_PENDING
303
- sleep 1
304
- raw_response = tree.client.dispatcher.recv_packet
305
- response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
306
- unless response.valid?
307
- raise RubySMB::Error::InvalidPacket.new(
308
- expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
309
- expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
310
- received_proto: response.smb2_header.protocol,
311
- received_cmd: response.smb2_header.command
312
- )
313
- end
314
- elsif response.status_code != WindowsError::NTStatus::STATUS_SUCCESS
315
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
297
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
298
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
316
299
  end
317
300
  response.write_count
318
301
  end
319
-
302
+
320
303
  # Rename a file
321
304
  #
322
305
  # @param new_file_name [String] the new name
323
306
  # @return [WindowsError::ErrorCode] the NTStatus Response code
324
307
  # @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
325
308
  def rename(new_file_name)
326
- raw_response = tree.client.send_recv(rename_packet(new_file_name))
309
+ raw_response = tree.client.send_recv(rename_packet(new_file_name), encrypt: @tree_connect_encrypt_data)
327
310
  response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
328
311
  unless response.valid?
329
312
  raise RubySMB::Error::InvalidPacket.new(
330
313
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
331
314
  expected_cmd: RubySMB::SMB2::Packet::SetInfoResponse::COMMAND,
332
- received_proto: response.smb2_header.protocol,
333
- received_cmd: response.smb2_header.command
315
+ packet: response
334
316
  )
335
317
  end
336
318
  response.smb2_header.nt_status.to_nt_status