ruby_smb 1.0.5 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +3 -2
  5. data/Gemfile +6 -2
  6. data/README.md +35 -47
  7. data/examples/anonymous_auth.rb +3 -3
  8. data/examples/append_file.rb +10 -8
  9. data/examples/authenticate.rb +9 -5
  10. data/examples/delete_file.rb +8 -6
  11. data/examples/enum_registry_key.rb +29 -0
  12. data/examples/enum_registry_values.rb +31 -0
  13. data/examples/list_directory.rb +8 -6
  14. data/examples/negotiate.rb +51 -8
  15. data/examples/negotiate_with_netbios_service.rb +9 -5
  16. data/examples/net_share_enum_all.rb +6 -4
  17. data/examples/pipes.rb +13 -13
  18. data/examples/query_service_status.rb +64 -0
  19. data/examples/read_file.rb +8 -6
  20. data/examples/read_file_encryption.rb +56 -0
  21. data/examples/read_registry_key_value.rb +33 -0
  22. data/examples/rename_file.rb +9 -7
  23. data/examples/tree_connect.rb +7 -5
  24. data/examples/write_file.rb +9 -7
  25. data/lib/ruby_smb.rb +4 -1
  26. data/lib/ruby_smb/client.rb +239 -21
  27. data/lib/ruby_smb/client/authentication.rb +27 -8
  28. data/lib/ruby_smb/client/encryption.rb +62 -0
  29. data/lib/ruby_smb/client/negotiation.rb +154 -12
  30. data/lib/ruby_smb/client/signing.rb +19 -0
  31. data/lib/ruby_smb/client/tree_connect.rb +4 -4
  32. data/lib/ruby_smb/client/utils.rb +8 -7
  33. data/lib/ruby_smb/client/winreg.rb +46 -0
  34. data/lib/ruby_smb/crypto.rb +30 -0
  35. data/lib/ruby_smb/dcerpc.rb +40 -0
  36. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  37. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  38. data/lib/ruby_smb/dcerpc/error.rb +6 -0
  39. data/lib/ruby_smb/dcerpc/ndr.rb +260 -16
  40. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  41. data/lib/ruby_smb/dcerpc/request.rb +41 -9
  42. data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
  43. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +38 -0
  44. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  45. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  46. data/lib/ruby_smb/dcerpc/svcctl.rb +479 -0
  47. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
  48. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
  49. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
  50. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
  51. data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
  52. data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
  53. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
  54. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
  55. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
  56. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
  57. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
  58. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
  59. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
  60. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
  61. data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
  62. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
  63. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
  64. data/lib/ruby_smb/dcerpc/winreg.rb +421 -0
  65. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  66. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  67. data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +73 -0
  68. data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
  69. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  70. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  71. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  72. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  73. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  74. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  75. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  76. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  77. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  78. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  79. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +40 -0
  80. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  81. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  82. data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +37 -0
  83. data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
  84. data/lib/ruby_smb/dispatcher/base.rb +1 -1
  85. data/lib/ruby_smb/dispatcher/socket.rb +5 -4
  86. data/lib/ruby_smb/error.rb +28 -1
  87. data/lib/ruby_smb/field/stringz16.rb +17 -1
  88. data/lib/ruby_smb/nbss/session_header.rb +4 -4
  89. data/lib/ruby_smb/smb1/commands.rb +1 -1
  90. data/lib/ruby_smb/smb1/file.rb +8 -14
  91. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
  92. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
  93. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  94. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
  95. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
  96. data/lib/ruby_smb/smb1/pipe.rb +81 -3
  97. data/lib/ruby_smb/smb1/tree.rb +12 -3
  98. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  99. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  100. data/lib/ruby_smb/smb2/file.rb +51 -61
  101. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  102. data/lib/ruby_smb/smb2/packet.rb +2 -0
  103. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  104. data/lib/ruby_smb/smb2/packet/error_packet.rb +2 -4
  105. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
  106. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
  107. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  108. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
  109. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
  110. data/lib/ruby_smb/smb2/pipe.rb +80 -3
  111. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  112. data/lib/ruby_smb/smb2/tree.rb +32 -20
  113. data/lib/ruby_smb/version.rb +1 -1
  114. data/ruby_smb.gemspec +5 -3
  115. data/spec/lib/ruby_smb/client_spec.rb +1583 -102
  116. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  117. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  118. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  119. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1729 -0
  120. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  121. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
  122. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +135 -0
  123. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  124. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  125. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
  126. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
  127. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
  128. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
  129. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
  130. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
  131. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
  132. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
  133. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
  134. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
  135. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
  136. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
  137. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
  138. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
  139. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
  140. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
  141. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
  142. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
  143. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  144. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  145. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
  146. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
  147. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +104 -0
  148. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  149. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  150. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  151. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  152. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  153. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +95 -0
  154. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  155. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +35 -0
  156. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  157. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  158. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +138 -0
  159. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  160. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
  161. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
  162. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +884 -0
  163. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  164. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
  165. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  166. data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
  167. data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
  168. data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
  169. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  170. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  171. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  172. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  173. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +216 -147
  174. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  175. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  176. data/spec/lib/ruby_smb/smb2/file_spec.rb +146 -68
  177. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  178. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  179. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +3 -24
  180. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  181. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  182. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  183. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  184. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
  185. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +226 -148
  186. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  187. data/spec/lib/ruby_smb/smb2/tree_spec.rb +88 -9
  188. metadata +257 -81
  189. metadata.gz.sig +0 -0
  190. data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
  191. data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -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
 
@@ -3,9 +3,9 @@ module RubySMB
3
3
  # Represents a pipe on the Remote server that we can perform
4
4
  # various I/O operations on.
5
5
  class Pipe < File
6
- require 'ruby_smb/smb1/dcerpc'
6
+ require 'ruby_smb/dcerpc'
7
7
 
8
- include RubySMB::SMB1::Dcerpc
8
+ include RubySMB::Dcerpc
9
9
 
10
10
  # Reference: https://msdn.microsoft.com/en-us/library/ee441883.aspx
11
11
  STATUS_DISCONNECTED = 0x0001
@@ -13,6 +13,19 @@ module RubySMB
13
13
  STATUS_OK = 0x0003
14
14
  STATUS_CLOSED = 0x0004
15
15
 
16
+ def initialize(tree:, response:, name:)
17
+ raise ArgumentError, 'No Name Provided' if name.nil?
18
+ case name
19
+ when 'srvsvc'
20
+ extend RubySMB::Dcerpc::Srvsvc
21
+ when 'winreg'
22
+ extend RubySMB::Dcerpc::Winreg
23
+ when 'svcctl'
24
+ extend RubySMB::Dcerpc::Svcctl
25
+ end
26
+ super(tree: tree, response: response, name: name)
27
+ end
28
+
16
29
  # Performs a peek operation on the named pipe
17
30
  #
18
31
  # @param peek_size [Integer] Amount of data to peek
@@ -36,7 +49,7 @@ module RubySMB
36
49
  end
37
50
 
38
51
  unless response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW or response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
39
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
52
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
40
53
  end
41
54
 
42
55
  response
@@ -68,6 +81,71 @@ module RubySMB
68
81
  state == STATUS_OK
69
82
  end
70
83
 
84
+ # Send a DCERPC request with the provided stub packet.
85
+ #
86
+ # @params stub_packet [#opnum] the stub packet to add to the DCERPC request
87
+ # @return [String] the raw DCERPC response stub
88
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not valid
89
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if the response status code is different than STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW
90
+ def dcerpc_request(stub_packet, options={})
91
+ options.merge!(endpoint: stub_packet.class.name.split('::').at(-2))
92
+ dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: stub_packet.opnum }, options)
93
+ dcerpc_request.stub.read(stub_packet.to_binary_s)
94
+ trans_nmpipe_request = RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest.new(options)
95
+ @tree.set_header_fields(trans_nmpipe_request)
96
+ trans_nmpipe_request.set_fid(@fid)
97
+ trans_nmpipe_request.data_block.trans_data.write_data = dcerpc_request.to_binary_s
98
+
99
+ trans_nmpipe_raw_response = @tree.client.send_recv(trans_nmpipe_request)
100
+ trans_nmpipe_response = RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse.read(trans_nmpipe_raw_response)
101
+ unless trans_nmpipe_response.valid?
102
+ raise RubySMB::Error::InvalidPacket.new(
103
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
104
+ expected_cmd: RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse::COMMAND,
105
+ received_proto: trans_nmpipe_response.smb_header.protocol,
106
+ received_cmd: trans_nmpipe_response.smb_header.command
107
+ )
108
+ end
109
+ unless [WindowsError::NTStatus::STATUS_SUCCESS,
110
+ WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW].include?(trans_nmpipe_response.status_code)
111
+ raise RubySMB::Error::UnexpectedStatusCode, trans_nmpipe_response.status_code
112
+ end
113
+
114
+ raw_data = trans_nmpipe_response.data_block.trans_data.read_data.to_binary_s
115
+ if trans_nmpipe_response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW
116
+ raw_data << read(bytes: @tree.client.max_buffer_size - trans_nmpipe_response.parameter_block.data_count)
117
+ dcerpc_response = dcerpc_response_from_raw_response(raw_data)
118
+ unless dcerpc_response.pdu_header.pfc_flags.first_frag == 1
119
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Not the first fragment"
120
+ end
121
+ stub_data = dcerpc_response.stub.to_s
122
+
123
+ loop do
124
+ break if dcerpc_response.pdu_header.pfc_flags.last_frag == 1
125
+ raw_data = read(bytes: @tree.client.max_buffer_size)
126
+ dcerpc_response = dcerpc_response_from_raw_response(raw_data)
127
+ stub_data << dcerpc_response.stub.to_s
128
+ end
129
+ stub_data
130
+ else
131
+ dcerpc_response = dcerpc_response_from_raw_response(raw_data)
132
+ dcerpc_response.stub.to_s
133
+ end
134
+ end
135
+
136
+
137
+ private
138
+
139
+ def dcerpc_response_from_raw_response(raw_data)
140
+ dcerpc_response = RubySMB::Dcerpc::Response.read(raw_data)
141
+ unless dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::RESPONSE
142
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Not a Response packet"
143
+ end
144
+ dcerpc_response
145
+ rescue IOError
146
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the DCERPC response"
147
+ end
148
+
71
149
  end
72
150
  end
73
151
  end
@@ -123,6 +123,15 @@ module RubySMB
123
123
  raw_response = @client.send_recv(nt_create_andx_request)
124
124
  response = RubySMB::SMB1::Packet::NtCreateAndxResponse.read(raw_response)
125
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
126
135
  raise RubySMB::Error::InvalidPacket.new(
127
136
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
128
137
  expected_cmd: RubySMB::SMB1::Packet::NtCreateAndxResponse::COMMAND,
@@ -131,7 +140,7 @@ module RubySMB
131
140
  )
132
141
  end
133
142
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
134
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
143
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
135
144
  end
136
145
 
137
146
  case response.parameter_block.resource_type
@@ -191,7 +200,7 @@ module RubySMB
191
200
  )
192
201
  end
193
202
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
194
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
203
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
195
204
  end
196
205
 
197
206
  results = response.results(type, unicode: unicode)
@@ -226,7 +235,7 @@ module RubySMB
226
235
  )
227
236
  end
228
237
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
229
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
238
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
230
239
  end
231
240
 
232
241
  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,7 +89,7 @@ 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(
@@ -94,7 +100,7 @@ module RubySMB
94
100
  )
95
101
  end
96
102
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
97
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
103
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
98
104
  end
99
105
  response.status_code
100
106
  end
@@ -109,14 +115,16 @@ module RubySMB
109
115
  # @raise [RubySMB::Error::InvalidPacket] if the response is not a ReadResponse packet
110
116
  # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
111
117
  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)
118
+ max_read = tree.client.server_max_read_size
119
+ max_read = 65536 unless tree.client.server_supports_multi_credit
120
+ atomic_read_size = [bytes, max_read].min
121
+ credit_charge = 0
122
+ if tree.client.server_supports_multi_credit
123
+ credit_charge = (atomic_read_size - 1) / 65536 + 1
124
+ end
125
+
126
+ read_request = read_packet(read_length: atomic_read_size, offset: offset, credit_charge: credit_charge)
127
+ raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
120
128
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
121
129
  unless response.valid?
122
130
  raise RubySMB::Error::InvalidPacket.new(
@@ -127,7 +135,7 @@ module RubySMB
127
135
  )
128
136
  end
129
137
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
130
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
138
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
131
139
  end
132
140
 
133
141
  data = response.buffer.to_binary_s
@@ -136,10 +144,10 @@ module RubySMB
136
144
 
137
145
  while remaining_bytes > 0
138
146
  offset += atomic_read_size
139
- atomic_read_size = remaining_bytes if remaining_bytes < tree.client.server_max_read_size
147
+ atomic_read_size = remaining_bytes if remaining_bytes < max_read
140
148
 
141
- read_request = read_packet(read_length: atomic_read_size, offset: offset)
142
- raw_response = tree.client.send_recv(read_request)
149
+ read_request = read_packet(read_length: atomic_read_size, offset: offset, credit_charge: credit_charge)
150
+ raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
143
151
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
144
152
  unless response.valid?
145
153
  raise RubySMB::Error::InvalidPacket.new(
@@ -150,7 +158,7 @@ module RubySMB
150
158
  )
151
159
  end
152
160
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
153
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
161
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
154
162
  end
155
163
 
156
164
  data << response.buffer.to_binary_s
@@ -163,17 +171,19 @@ module RubySMB
163
171
  #
164
172
  # @param bytes [Integer] the number of bytes to read
165
173
  # @param offset [Integer] the byte offset in the file to start reading from
174
+ # @param credit_charge [Integer] the number of credits that this request consumes
166
175
  # @return [RubySMB::SMB2::Packet::ReadRequest] the data read from the file
167
- def read_packet(read_length: 0, offset: 0)
176
+ def read_packet(read_length: 0, offset: 0, credit_charge: 1)
168
177
  read_request = set_header_fields(RubySMB::SMB2::Packet::ReadRequest.new)
169
178
  read_request.read_length = read_length
170
179
  read_request.offset = offset
180
+ read_request.smb2_header.credit_charge = credit_charge
171
181
  read_request
172
182
  end
173
-
183
+
174
184
  def send_recv_read(read_length: 0, offset: 0)
175
185
  read_request = read_packet(read_length: read_length, offset: offset)
176
- raw_response = tree.client.send_recv(read_request)
186
+ raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
177
187
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
178
188
  unless response.valid?
179
189
  raise RubySMB::Error::InvalidPacket.new(
@@ -183,20 +193,8 @@ module RubySMB
183
193
  received_cmd: response.smb2_header.command
184
194
  )
185
195
  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
196
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
197
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
200
198
  end
201
199
  response.buffer.to_binary_s
202
200
  end
@@ -206,7 +204,7 @@ module RubySMB
206
204
  # @return [WindowsError::ErrorCode] the NTStatus Response code
207
205
  # @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
208
206
  def delete
209
- raw_response = tree.client.send_recv(delete_packet)
207
+ raw_response = tree.client.send_recv(delete_packet, encrypt: @tree_connect_encrypt_data)
210
208
  response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
211
209
  unless response.valid?
212
210
  raise RubySMB::Error::InvalidPacket.new(
@@ -246,17 +244,19 @@ module RubySMB
246
244
  # @return [WindowsError::ErrorCode] the NTStatus code returned from the operation
247
245
  # @raise [RubySMB::Error::InvalidPacket] if the response is not a WriteResponse packet
248
246
  def write(data:'', offset: 0)
247
+ max_write = tree.client.server_max_write_size
248
+ max_write = 65536 unless tree.client.server_supports_multi_credit
249
249
  buffer = data.dup
250
250
  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
251
+ atomic_write_size = [bytes, max_write].min
252
+ credit_charge = 0
253
+ if tree.client.server_supports_multi_credit
254
+ credit_charge = (atomic_write_size - 1) / 65536 + 1
255
+ end
256
256
 
257
257
  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)
258
+ write_request = write_packet(data: buffer.slice!(0, atomic_write_size), offset: offset, credit_charge: credit_charge)
259
+ raw_response = tree.client.send_recv(write_request, encrypt: @tree_connect_encrypt_data)
260
260
  response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
261
261
  unless response.valid?
262
262
  raise RubySMB::Error::InvalidPacket.new(
@@ -268,7 +268,7 @@ module RubySMB
268
268
  end
269
269
  status = response.smb2_header.nt_status.to_nt_status
270
270
 
271
- offset+= atomic_write_size
271
+ offset += atomic_write_size
272
272
  return status unless status == WindowsError::NTStatus::STATUS_SUCCESS
273
273
  end
274
274
 
@@ -279,17 +279,19 @@ module RubySMB
279
279
  #
280
280
  # @param data [String] the data to write to the file
281
281
  # @param offset [Integer] the offset in the file to start writing from
282
+ # @param credit_charge [Integer] the number of credits that this request consumes
282
283
  # @return []RubySMB::SMB2::Packet::WriteRequest] the request packet
283
- def write_packet(data:'', offset: 0)
284
+ def write_packet(data:'', offset: 0, credit_charge: 1)
284
285
  write_request = set_header_fields(RubySMB::SMB2::Packet::WriteRequest.new)
285
286
  write_request.write_offset = offset
286
287
  write_request.buffer = data
288
+ write_request.smb2_header.credit_charge = credit_charge
287
289
  write_request
288
290
  end
289
-
291
+
290
292
  def send_recv_write(data:'', offset: 0)
291
293
  pkt = write_packet(data: data, offset: offset)
292
- raw_response = tree.client.send_recv(pkt)
294
+ raw_response = tree.client.send_recv(pkt, encrypt: @tree_connect_encrypt_data)
293
295
  response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
294
296
  unless response.valid?
295
297
  raise RubySMB::Error::InvalidPacket.new(
@@ -299,31 +301,19 @@ module RubySMB
299
301
  received_cmd: response.smb2_header.command
300
302
  )
301
303
  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
304
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
305
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
316
306
  end
317
307
  response.write_count
318
308
  end
319
-
309
+
320
310
  # Rename a file
321
311
  #
322
312
  # @param new_file_name [String] the new name
323
313
  # @return [WindowsError::ErrorCode] the NTStatus Response code
324
314
  # @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
325
315
  def rename(new_file_name)
326
- raw_response = tree.client.send_recv(rename_packet(new_file_name))
316
+ raw_response = tree.client.send_recv(rename_packet(new_file_name), encrypt: @tree_connect_encrypt_data)
327
317
  response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
328
318
  unless response.valid?
329
319
  raise RubySMB::Error::InvalidPacket.new(