ruby_smb 1.1.0 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
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