ruby_smb 1.0.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +3 -2
  5. data/Gemfile +6 -2
  6. data/README.md +35 -47
  7. data/examples/enum_registry_key.rb +28 -0
  8. data/examples/enum_registry_values.rb +30 -0
  9. data/examples/negotiate.rb +51 -8
  10. data/examples/pipes.rb +2 -1
  11. data/examples/read_file_encryption.rb +56 -0
  12. data/examples/read_registry_key_value.rb +32 -0
  13. data/lib/ruby_smb.rb +4 -1
  14. data/lib/ruby_smb/client.rb +200 -20
  15. data/lib/ruby_smb/client/authentication.rb +70 -33
  16. data/lib/ruby_smb/client/echo.rb +20 -2
  17. data/lib/ruby_smb/client/encryption.rb +62 -0
  18. data/lib/ruby_smb/client/negotiation.rb +160 -24
  19. data/lib/ruby_smb/client/signing.rb +19 -0
  20. data/lib/ruby_smb/client/tree_connect.rb +24 -18
  21. data/lib/ruby_smb/client/utils.rb +8 -7
  22. data/lib/ruby_smb/client/winreg.rb +46 -0
  23. data/lib/ruby_smb/crypto.rb +30 -0
  24. data/lib/ruby_smb/dcerpc.rb +38 -0
  25. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  26. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  27. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  28. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  29. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  30. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  31. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  32. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  33. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  34. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  35. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  36. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  37. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  38. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  39. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  40. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  41. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  42. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  43. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  44. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  45. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  46. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  47. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  48. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  49. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  50. data/lib/ruby_smb/dispatcher/socket.rb +5 -3
  51. data/lib/ruby_smb/error.rb +68 -2
  52. data/lib/ruby_smb/generic_packet.rb +33 -4
  53. data/lib/ruby_smb/smb1/commands.rb +1 -1
  54. data/lib/ruby_smb/smb1/file.rb +66 -15
  55. data/lib/ruby_smb/smb1/packet/close_request.rb +2 -5
  56. data/lib/ruby_smb/smb1/packet/close_response.rb +2 -1
  57. data/lib/ruby_smb/smb1/packet/echo_request.rb +2 -4
  58. data/lib/ruby_smb/smb1/packet/echo_response.rb +2 -1
  59. data/lib/ruby_smb/smb1/packet/empty_packet.rb +10 -1
  60. data/lib/ruby_smb/smb1/packet/logoff_request.rb +2 -4
  61. data/lib/ruby_smb/smb1/packet/logoff_response.rb +2 -1
  62. data/lib/ruby_smb/smb1/packet/negotiate_request.rb +2 -5
  63. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +3 -7
  64. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +4 -4
  65. data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +2 -4
  66. data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +2 -1
  67. data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +2 -1
  68. data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +2 -1
  69. data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +2 -4
  70. data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +2 -1
  71. data/lib/ruby_smb/smb1/packet/read_andx_request.rb +2 -5
  72. data/lib/ruby_smb/smb1/packet/read_andx_response.rb +2 -1
  73. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -1
  74. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +3 -2
  75. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
  76. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +3 -2
  77. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_request.rb +0 -1
  78. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_response.rb +3 -2
  79. data/lib/ruby_smb/smb1/packet/trans/request.rb +2 -5
  80. data/lib/ruby_smb/smb1/packet/trans/response.rb +2 -1
  81. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +1 -1
  82. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +1 -1
  83. data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +2 -1
  84. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +8 -2
  85. data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +2 -1
  86. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +8 -2
  87. data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +2 -1
  88. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +2 -1
  89. data/lib/ruby_smb/smb1/packet/trans2/request.rb +2 -4
  90. data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +2 -4
  91. data/lib/ruby_smb/smb1/packet/trans2/response.rb +2 -1
  92. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +2 -1
  93. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +2 -1
  94. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +2 -4
  95. data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +13 -3
  96. data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
  97. data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -1
  98. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +3 -6
  99. data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
  100. data/lib/ruby_smb/smb1/pipe.rb +87 -6
  101. data/lib/ruby_smb/smb1/tree.rb +41 -3
  102. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  103. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  104. data/lib/ruby_smb/smb2/file.rb +103 -25
  105. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  106. data/lib/ruby_smb/smb2/packet.rb +2 -0
  107. data/lib/ruby_smb/smb2/packet/close_request.rb +2 -4
  108. data/lib/ruby_smb/smb2/packet/close_response.rb +2 -1
  109. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  110. data/lib/ruby_smb/smb2/packet/create_request.rb +2 -4
  111. data/lib/ruby_smb/smb2/packet/create_response.rb +2 -1
  112. data/lib/ruby_smb/smb2/packet/echo_request.rb +2 -4
  113. data/lib/ruby_smb/smb2/packet/echo_response.rb +2 -1
  114. data/lib/ruby_smb/smb2/packet/error_packet.rb +15 -3
  115. data/lib/ruby_smb/smb2/packet/ioctl_request.rb +2 -5
  116. data/lib/ruby_smb/smb2/packet/ioctl_response.rb +2 -1
  117. data/lib/ruby_smb/smb2/packet/logoff_request.rb +2 -4
  118. data/lib/ruby_smb/smb2/packet/logoff_response.rb +2 -1
  119. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -17
  120. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +51 -4
  121. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +2 -4
  122. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +8 -2
  123. data/lib/ruby_smb/smb2/packet/read_request.rb +2 -4
  124. data/lib/ruby_smb/smb2/packet/read_response.rb +2 -1
  125. data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
  126. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -1
  127. data/lib/ruby_smb/smb2/packet/set_info_request.rb +2 -4
  128. data/lib/ruby_smb/smb2/packet/set_info_response.rb +2 -1
  129. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  130. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +93 -10
  131. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +10 -22
  132. data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +2 -4
  133. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +2 -1
  134. data/lib/ruby_smb/smb2/packet/write_request.rb +2 -4
  135. data/lib/ruby_smb/smb2/packet/write_response.rb +2 -1
  136. data/lib/ruby_smb/smb2/pipe.rb +86 -12
  137. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  138. data/lib/ruby_smb/smb2/tree.rb +65 -21
  139. data/lib/ruby_smb/version.rb +1 -1
  140. data/ruby_smb.gemspec +5 -3
  141. data/spec/lib/ruby_smb/client_spec.rb +1563 -104
  142. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  143. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  144. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  145. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  146. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  147. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  148. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  149. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  150. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  151. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  152. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  153. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  154. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  155. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  156. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  157. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  158. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  159. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  160. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  161. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  162. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  163. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  164. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  165. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  166. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  167. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +3 -1
  168. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  169. data/spec/lib/ruby_smb/generic_packet_spec.rb +52 -4
  170. data/spec/lib/ruby_smb/smb1/file_spec.rb +191 -2
  171. data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +68 -0
  172. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  173. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  174. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  175. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  176. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +11 -2
  177. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +11 -2
  178. data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +40 -0
  179. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +272 -149
  180. data/spec/lib/ruby_smb/smb1/tree_spec.rb +44 -7
  181. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  182. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  183. data/spec/lib/ruby_smb/smb2/file_spec.rb +323 -6
  184. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  185. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  186. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +78 -0
  187. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  188. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  189. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +8 -0
  190. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  191. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  192. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -22
  193. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +286 -149
  194. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  195. data/spec/lib/ruby_smb/smb2/tree_spec.rb +261 -2
  196. metadata +191 -83
  197. metadata.gz.sig +0 -0
  198. data/lib/ruby_smb/smb1/dcerpc.rb +0 -67
  199. data/lib/ruby_smb/smb2/dcerpc.rb +0 -70
  200. data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +0 -37
@@ -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,17 @@ 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
+ end
24
+ super(tree: tree, response: response, name: name)
25
+ end
26
+
16
27
  # Performs a peek operation on the named pipe
17
28
  #
18
29
  # @param peek_size [Integer] Amount of data to peek
@@ -26,14 +37,19 @@ module RubySMB
26
37
  packet = @tree.set_header_fields(packet)
27
38
  raw_response = @tree.client.send_recv(packet)
28
39
  response = RubySMB::SMB1::Packet::Trans::PeekNmpipeResponse.read(raw_response)
40
+ unless response.valid?
41
+ raise RubySMB::Error::InvalidPacket.new(
42
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
43
+ expected_cmd: RubySMB::SMB1::Packet::Trans::PeekNmpipeRequest::COMMAND,
44
+ received_proto: response.smb_header.protocol,
45
+ received_cmd: response.smb_header.command
46
+ )
47
+ end
29
48
 
30
49
  unless response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW or response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
31
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
50
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
32
51
  end
33
52
 
34
- unless response.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_TRANSACTION
35
- raise RubySMB::Error::InvalidPacket, 'Not a TransResponse packet'
36
- end
37
53
  response
38
54
  end
39
55
 
@@ -63,6 +79,71 @@ module RubySMB
63
79
  state == STATUS_OK
64
80
  end
65
81
 
82
+ # Send a DCERPC request with the provided stub packet.
83
+ #
84
+ # @params stub_packet [#opnum] the stub packet to add to the DCERPC request
85
+ # @return [String] the raw DCERPC response stub
86
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not valid
87
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if the response status code is different than STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW
88
+ def dcerpc_request(stub_packet, options={})
89
+ options.merge!(endpoint: stub_packet.class.name.split('::').at(-2))
90
+ dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: stub_packet.opnum }, options)
91
+ dcerpc_request.stub.read(stub_packet.to_binary_s)
92
+ trans_nmpipe_request = RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest.new(options)
93
+ @tree.set_header_fields(trans_nmpipe_request)
94
+ trans_nmpipe_request.set_fid(@fid)
95
+ trans_nmpipe_request.data_block.trans_data.write_data = dcerpc_request.to_binary_s
96
+
97
+ trans_nmpipe_raw_response = @tree.client.send_recv(trans_nmpipe_request)
98
+ trans_nmpipe_response = RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse.read(trans_nmpipe_raw_response)
99
+ unless trans_nmpipe_response.valid?
100
+ raise RubySMB::Error::InvalidPacket.new(
101
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
102
+ 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
105
+ )
106
+ end
107
+ unless [WindowsError::NTStatus::STATUS_SUCCESS,
108
+ WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW].include?(trans_nmpipe_response.status_code)
109
+ raise RubySMB::Error::UnexpectedStatusCode, trans_nmpipe_response.status_code
110
+ end
111
+
112
+ raw_data = trans_nmpipe_response.data_block.trans_data.read_data.to_binary_s
113
+ if trans_nmpipe_response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW
114
+ raw_data << read(bytes: @tree.client.max_buffer_size - trans_nmpipe_response.parameter_block.data_count)
115
+ dcerpc_response = dcerpc_response_from_raw_response(raw_data)
116
+ unless dcerpc_response.pdu_header.pfc_flags.first_frag == 1
117
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Not the first fragment"
118
+ end
119
+ stub_data = dcerpc_response.stub.to_s
120
+
121
+ loop do
122
+ break if dcerpc_response.pdu_header.pfc_flags.last_frag == 1
123
+ raw_data = read(bytes: @tree.client.max_buffer_size)
124
+ dcerpc_response = dcerpc_response_from_raw_response(raw_data)
125
+ stub_data << dcerpc_response.stub.to_s
126
+ end
127
+ stub_data
128
+ else
129
+ dcerpc_response = dcerpc_response_from_raw_response(raw_data)
130
+ dcerpc_response.stub.to_s
131
+ end
132
+ end
133
+
134
+
135
+ private
136
+
137
+ def dcerpc_response_from_raw_response(raw_data)
138
+ dcerpc_response = RubySMB::Dcerpc::Response.read(raw_data)
139
+ unless dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::RESPONSE
140
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Not a Response packet"
141
+ end
142
+ dcerpc_response
143
+ rescue IOError
144
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the DCERPC response"
145
+ end
146
+
66
147
  end
67
148
  end
68
149
  end
@@ -39,11 +39,20 @@ module RubySMB
39
39
  # Disconnects this Tree from the current session
40
40
  #
41
41
  # @return [WindowsError::ErrorCode] the NTStatus sent back by the server.
42
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a TreeDisconnectResponse packet
42
43
  def disconnect!
43
44
  request = RubySMB::SMB1::Packet::TreeDisconnectRequest.new
44
45
  request = set_header_fields(request)
45
46
  raw_response = client.send_recv(request)
46
47
  response = RubySMB::SMB1::Packet::TreeDisconnectResponse.read(raw_response)
48
+ unless response.valid?
49
+ raise RubySMB::Error::InvalidPacket.new(
50
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
51
+ expected_cmd: RubySMB::SMB1::Packet::TreeDisconnectResponse::COMMAND,
52
+ received_proto: response.smb_header.protocol,
53
+ received_cmd: response.smb_header.command
54
+ )
55
+ end
47
56
  response.status_code
48
57
  end
49
58
 
@@ -113,11 +122,16 @@ module RubySMB
113
122
 
114
123
  raw_response = @client.send_recv(nt_create_andx_request)
115
124
  response = RubySMB::SMB1::Packet::NtCreateAndxResponse.read(raw_response)
116
- unless response.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_NT_CREATE_ANDX
117
- raise RubySMB::Error::InvalidPacket, 'Not a NtCreateAndxResponse packet'
125
+ unless response.valid?
126
+ raise RubySMB::Error::InvalidPacket.new(
127
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
128
+ expected_cmd: RubySMB::SMB1::Packet::NtCreateAndxResponse::COMMAND,
129
+ received_proto: response.smb_header.protocol,
130
+ received_cmd: response.smb_header.command
131
+ )
118
132
  end
119
133
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
120
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
134
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
121
135
  end
122
136
 
123
137
  case response.parameter_block.resource_type
@@ -140,6 +154,8 @@ module RubySMB
140
154
  # @param pattern [String] search pattern
141
155
  # @param type [Class] file information class
142
156
  # @return [Array] array of directory structures
157
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a Trans2 packet
158
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
143
159
  def list(directory: '\\', pattern: '*', unicode: true,
144
160
  type: RubySMB::SMB1::Packet::Trans2::FindInformationLevel::FindFileFullDirectoryInfo)
145
161
  find_first_request = RubySMB::SMB1::Packet::Trans2::FindFirst2Request.new
@@ -166,6 +182,17 @@ module RubySMB
166
182
 
167
183
  raw_response = client.send_recv(find_first_request)
168
184
  response = RubySMB::SMB1::Packet::Trans2::FindFirst2Response.read(raw_response)
185
+ unless response.valid?
186
+ raise RubySMB::Error::InvalidPacket.new(
187
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
188
+ expected_cmd: RubySMB::SMB1::Packet::Trans2::FindFirst2Response::COMMAND,
189
+ received_proto: response.smb_header.protocol,
190
+ received_cmd: response.smb_header.command
191
+ )
192
+ end
193
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
194
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
195
+ end
169
196
 
170
197
  results = response.results(type, unicode: unicode)
171
198
 
@@ -190,6 +217,17 @@ module RubySMB
190
217
 
191
218
  raw_response = client.send_recv(find_next_request)
192
219
  response = RubySMB::SMB1::Packet::Trans2::FindNext2Response.read(raw_response)
220
+ unless response.valid?
221
+ raise RubySMB::Error::InvalidPacket.new(
222
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
223
+ expected_cmd: RubySMB::SMB1::Packet::Trans2::FindNext2Response::COMMAND,
224
+ received_proto: response.smb_header.protocol,
225
+ received_cmd: response.smb_header.command
226
+ )
227
+ end
228
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
229
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
230
+ end
193
231
 
194
232
  results += response.results(type, unicode: unicode)
195
233
 
@@ -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 encryption is required (SMB 3.x)
56
+ # @!attribute [rw] encryption_required
57
+ # @return [Boolean]
58
+ attr_accessor :encryption_required
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
+ @encryption_required = encrypt
69
75
  end
70
76
 
71
77
  # Appends the supplied data to the end of the file.
@@ -79,11 +85,24 @@ module RubySMB
79
85
  # Closes the handle to the remote file.
80
86
  #
81
87
  # @return [WindowsError::ErrorCode] the NTStatus code returned by the operation
88
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a CloseResponse packet
89
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
82
90
  def close
83
91
  close_request = set_header_fields(RubySMB::SMB2::Packet::CloseRequest.new)
84
- raw_response = tree.client.send_recv(close_request)
92
+ raw_response = tree.client.send_recv(close_request, encrypt: @encryption_required)
85
93
  response = RubySMB::SMB2::Packet::CloseResponse.read(raw_response)
86
- response.smb2_header.nt_status.to_nt_status
94
+ unless response.valid?
95
+ raise RubySMB::Error::InvalidPacket.new(
96
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
97
+ expected_cmd: RubySMB::SMB2::Packet::CloseResponse::COMMAND,
98
+ received_proto: response.smb2_header.protocol,
99
+ received_cmd: response.smb2_header.command
100
+ )
101
+ end
102
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
103
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
104
+ end
105
+ response.status_code
87
106
  end
88
107
 
89
108
  # Read from the file, a specific number of bytes
@@ -93,6 +112,8 @@ module RubySMB
93
112
  # @param bytes [Integer] the number of bytes to read
94
113
  # @param offset [Integer] the byte offset in the file to start reading from
95
114
  # @return [String] the data read from the file
115
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a ReadResponse packet
116
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
96
117
  def read(bytes: size, offset: 0)
97
118
  atomic_read_size = if bytes > tree.client.server_max_read_size
98
119
  tree.client.server_max_read_size
@@ -101,8 +122,19 @@ module RubySMB
101
122
  end
102
123
 
103
124
  read_request = read_packet(read_length: atomic_read_size, offset: offset)
104
- raw_response = tree.client.send_recv(read_request)
125
+ raw_response = tree.client.send_recv(read_request, encrypt: @encryption_required)
105
126
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
127
+ unless response.valid?
128
+ raise RubySMB::Error::InvalidPacket.new(
129
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
130
+ expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
131
+ received_proto: response.smb2_header.protocol,
132
+ received_cmd: response.smb2_header.command
133
+ )
134
+ end
135
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
136
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
137
+ end
106
138
 
107
139
  data = response.buffer.to_binary_s
108
140
 
@@ -113,8 +145,19 @@ module RubySMB
113
145
  atomic_read_size = remaining_bytes if remaining_bytes < tree.client.server_max_read_size
114
146
 
115
147
  read_request = read_packet(read_length: atomic_read_size, offset: offset)
116
- raw_response = tree.client.send_recv(read_request)
148
+ raw_response = tree.client.send_recv(read_request, encrypt: @encryption_required)
117
149
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
150
+ unless response.valid?
151
+ raise RubySMB::Error::InvalidPacket.new(
152
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
153
+ expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
154
+ received_proto: response.smb2_header.protocol,
155
+ received_cmd: response.smb2_header.command
156
+ )
157
+ end
158
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
159
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
160
+ end
118
161
 
119
162
  data << response.buffer.to_binary_s
120
163
  remaining_bytes -= atomic_read_size
@@ -133,17 +176,21 @@ module RubySMB
133
176
  read_request.offset = offset
134
177
  read_request
135
178
  end
136
-
179
+
137
180
  def send_recv_read(read_length: 0, offset: 0)
138
181
  read_request = read_packet(read_length: read_length, offset: offset)
139
- raw_response = tree.client.send_recv(read_request)
182
+ raw_response = tree.client.send_recv(read_request, encrypt: @encryption_required)
140
183
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
141
- if response.status_code == WindowsError::NTStatus::STATUS_PENDING
142
- sleep 1
143
- raw_response = tree.client.dispatcher.recv_packet
144
- response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
145
- elsif response.status_code != WindowsError::NTStatus::STATUS_SUCCESS
146
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
184
+ unless response.valid?
185
+ raise RubySMB::Error::InvalidPacket.new(
186
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
187
+ expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
188
+ received_proto: response.smb2_header.protocol,
189
+ received_cmd: response.smb2_header.command
190
+ )
191
+ end
192
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
193
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
147
194
  end
148
195
  response.buffer.to_binary_s
149
196
  end
@@ -151,9 +198,18 @@ module RubySMB
151
198
  # Delete a file on close
152
199
  #
153
200
  # @return [WindowsError::ErrorCode] the NTStatus Response code
201
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
154
202
  def delete
155
- raw_response = tree.client.send_recv(delete_packet)
203
+ raw_response = tree.client.send_recv(delete_packet, encrypt: @encryption_required)
156
204
  response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
205
+ unless response.valid?
206
+ raise RubySMB::Error::InvalidPacket.new(
207
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
208
+ expected_cmd: RubySMB::SMB2::Packet::SetInfoResponse::COMMAND,
209
+ received_proto: response.smb2_header.protocol,
210
+ received_cmd: response.smb2_header.command
211
+ )
212
+ end
157
213
  response.smb2_header.nt_status.to_nt_status
158
214
  end
159
215
 
@@ -182,6 +238,7 @@ module RubySMB
182
238
  # @param data [String] the data to write to the file
183
239
  # @param offset [Integer] the offset in the file to start writing from
184
240
  # @return [WindowsError::ErrorCode] the NTStatus code returned from the operation
241
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a WriteResponse packet
185
242
  def write(data:'', offset: 0)
186
243
  buffer = data.dup
187
244
  bytes = data.length
@@ -193,8 +250,16 @@ module RubySMB
193
250
 
194
251
  while buffer.length > 0 do
195
252
  write_request = write_packet(data: buffer.slice!(0,atomic_write_size), offset: offset)
196
- raw_response = tree.client.send_recv(write_request)
253
+ raw_response = tree.client.send_recv(write_request, encrypt: @encryption_required)
197
254
  response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
255
+ unless response.valid?
256
+ raise RubySMB::Error::InvalidPacket.new(
257
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
258
+ expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
259
+ received_proto: response.smb2_header.protocol,
260
+ received_cmd: response.smb2_header.command
261
+ )
262
+ end
198
263
  status = response.smb2_header.nt_status.to_nt_status
199
264
 
200
265
  offset+= atomic_write_size
@@ -215,28 +280,41 @@ module RubySMB
215
280
  write_request.buffer = data
216
281
  write_request
217
282
  end
218
-
283
+
219
284
  def send_recv_write(data:'', offset: 0)
220
285
  pkt = write_packet(data: data, offset: offset)
221
- raw_response = tree.client.send_recv(pkt)
286
+ raw_response = tree.client.send_recv(pkt, encrypt: @encryption_required)
222
287
  response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
223
- if response.status_code == WindowsError::NTStatus::STATUS_PENDING
224
- sleep 1
225
- raw_response = tree.client.dispatcher.recv_packet
226
- response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
227
- elsif response.status_code != WindowsError::NTStatus::STATUS_SUCCESS
228
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
288
+ unless response.valid?
289
+ raise RubySMB::Error::InvalidPacket.new(
290
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
291
+ expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
292
+ received_proto: response.smb2_header.protocol,
293
+ received_cmd: response.smb2_header.command
294
+ )
295
+ end
296
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
297
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
229
298
  end
230
299
  response.write_count
231
300
  end
232
-
301
+
233
302
  # Rename a file
234
303
  #
235
304
  # @param new_file_name [String] the new name
236
305
  # @return [WindowsError::ErrorCode] the NTStatus Response code
306
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
237
307
  def rename(new_file_name)
238
- raw_response = tree.client.send_recv(rename_packet(new_file_name))
308
+ raw_response = tree.client.send_recv(rename_packet(new_file_name), encrypt: @encryption_required)
239
309
  response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
310
+ unless response.valid?
311
+ raise RubySMB::Error::InvalidPacket.new(
312
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
313
+ expected_cmd: RubySMB::SMB2::Packet::SetInfoResponse::COMMAND,
314
+ received_proto: response.smb2_header.protocol,
315
+ received_cmd: response.smb2_header.command
316
+ )
317
+ end
240
318
  response.smb2_header.nt_status.to_nt_status
241
319
  end
242
320