ruby_smb 1.0.5 → 2.0.3

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