ruby_smb 1.0.3 → 2.0.1

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 +233 -22
  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 +172 -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 +4 -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 +50 -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 +52 -5
  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 +1612 -108
  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 +2 -2
  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
@@ -48,7 +48,7 @@ module RubySMB
48
48
  end
49
49
 
50
50
  rescue IOError, Errno::ECONNABORTED, Errno::ECONNRESET => e
51
- raise RubySMB::Error::CommunicationError, "An error occured writing to the Socket: #{e.message}"
51
+ raise RubySMB::Error::CommunicationError, "An error occurred writing to the Socket: #{e.message}"
52
52
  end
53
53
  nil
54
54
  end
@@ -61,13 +61,14 @@ module RubySMB
61
61
  # which are assumed to be the NetBiosSessionService header.
62
62
  # @raise [RubySMB::Error::CommunicationError] if the read timeout expires or an error occurs when reading the socket
63
63
  def recv_packet(full_response: false)
64
+ raise RubySMB::Error::CommunicationError, 'Connection has already been closed' if @tcp_socket.closed?
64
65
  if IO.select([@tcp_socket], nil, nil, @read_timeout).nil?
65
66
  raise RubySMB::Error::CommunicationError, "Read timeout expired when reading from the Socket (timeout=#{@read_timeout})"
66
67
  end
67
68
 
68
69
  begin
69
70
  nbss_data = @tcp_socket.read(4)
70
- raise IOError if nbss_data.nil?
71
+ raise RubySMB::Error::CommunicationError, 'Socket read returned nil' if nbss_data.nil?
71
72
  nbss_header = RubySMB::Nbss::SessionHeader.read(nbss_data)
72
73
  rescue IOError
73
74
  raise ::RubySMB::Error::NetBiosSessionService, 'NBSS Header is missing'
@@ -84,7 +85,7 @@ module RubySMB
84
85
  end
85
86
  data
86
87
  rescue Errno::EINVAL, Errno::ECONNABORTED, Errno::ECONNRESET, TypeError, NoMethodError => e
87
- raise RubySMB::Error::CommunicationError, "An error occured reading from the Socket #{e.message}"
88
+ raise RubySMB::Error::CommunicationError, "An error occurred reading from the Socket #{e.message}"
88
89
  end
89
90
  end
90
91
  end
@@ -15,10 +15,66 @@ module RubySMB
15
15
 
16
16
  # Raised when trying to parse raw binary into a Packet and the data
17
17
  # is invalid.
18
- class InvalidPacket < RubySMBError; end
18
+ class InvalidPacket < RubySMBError
19
+ def initialize(args = nil)
20
+ if args.nil?
21
+ super
22
+ elsif args.is_a? String
23
+ super(args)
24
+ elsif args.is_a? Hash
25
+ expected_proto = args[:expected_proto] ? translate_protocol(args[:expected_proto]) : "???"
26
+ expected_cmd = args[:expected_cmd] || "???"
27
+ received_proto = args[:received_proto] ? translate_protocol(args[:received_proto]) : "???"
28
+ received_cmd = args[:received_cmd] || "???"
29
+ super(
30
+ "Expecting #{expected_proto} protocol "\
31
+ "with command=#{expected_cmd}"\
32
+ "#{(" (" + args[:expected_custom] + ")") if args[:expected_custom]}, "\
33
+ "got #{received_proto} protocol "\
34
+ "with command=#{received_cmd}"\
35
+ "#{(" (" + args[:received_custom] + ")") if args[:received_custom]}"
36
+ )
37
+ else
38
+ raise ArgumentError, "InvalidPacket expects a String or a Hash, got a #{args.class}"
39
+ end
40
+ end
41
+
42
+ def translate_protocol(proto)
43
+ case proto
44
+ when RubySMB::SMB1::SMB_PROTOCOL_ID
45
+ 'SMB1'
46
+ when RubySMB::SMB2::SMB2_PROTOCOL_ID
47
+ 'SMB2'
48
+ else
49
+ raise ArgumentError, 'Unknown SMB protocol'
50
+ end
51
+ end
52
+ private :translate_protocol
53
+ end
19
54
 
20
55
  # Raised when a response packet has a NTStatus code that was unexpected.
21
- class UnexpectedStatusCode < RubySMBError; end
56
+ class UnexpectedStatusCode < RubySMBError
57
+ attr_reader :status_code
58
+
59
+ def initialize(status_code)
60
+ case status_code
61
+ when WindowsError::ErrorCode
62
+ @status_code = status_code
63
+ when Integer
64
+ @status_code = WindowsError::NTStatus.find_by_retval(status_code).first
65
+ if @status_code.nil?
66
+ @status_code = WindowsError::ErrorCode.new("0x#{status_code.to_s(16)}", status_code, "Unknown 0x#{status_code.to_s(16)}")
67
+ end
68
+ else
69
+ raise ArgumentError, "Status code must be a WindowsError::ErrorCode or an Integer, got #{status_code.class}"
70
+ end
71
+ super
72
+ end
73
+
74
+ def to_s
75
+ "The server responded with an unexpected status code: #{status_code.name}"
76
+ end
77
+ end
22
78
 
23
79
  # Raised when an error occurs with the underlying socket.
24
80
  class CommunicationError < RubySMBError; end
@@ -26,5 +82,15 @@ module RubySMB
26
82
  # Raised when Protocol Negotiation fails, possibly due to an
27
83
  # unsupported protocol.
28
84
  class NegotiationFailure < RubySMBError; end
85
+
86
+ # Raised when trying to parse raw binary into a BitField and the data
87
+ # is invalid.
88
+ class InvalidBitField < RubySMBError; end
89
+
90
+ # Raised when an encryption operation fails
91
+ class EncryptionError < RubySMBError; end
92
+
93
+ # Raised when an signing operation fails
94
+ class SigningError < RubySMBError; end
29
95
  end
30
96
  end
@@ -45,14 +45,16 @@ module RubySMB
45
45
  rescue IOError => e
46
46
  case self.to_s
47
47
  when /EmptyPacket|ErrorPacket/
48
- raise e
48
+ raise RubySMB::Error::InvalidPacket, 'Not a valid SMB packet'
49
49
  when /SMB1/
50
- RubySMB::SMB1::Packet::EmptyPacket.read(val)
50
+ packet = RubySMB::SMB1::Packet::EmptyPacket.read(val)
51
51
  when /SMB2/
52
- RubySMB::SMB2::Packet::ErrorPacket.read(val)
52
+ packet = RubySMB::SMB2::Packet::ErrorPacket.read(val)
53
53
  else
54
- raise e
54
+ raise RubySMB::Error::InvalidPacket, 'Not a valid SMB packet'
55
55
  end
56
+ packet.original_command = self::COMMAND
57
+ packet
56
58
  end
57
59
  end
58
60
 
@@ -72,6 +74,33 @@ module RubySMB
72
74
  status_code
73
75
  end
74
76
 
77
+ # Validates the packet protocol ID and the SMB command
78
+ #
79
+ # @return [TrueClass, FalseClass] true if the packet is valid, false otherwise
80
+ def valid?
81
+ case packet_smb_version
82
+ when 'SMB1'
83
+ return smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
84
+ smb_header.command == self.class::COMMAND
85
+ when 'SMB2'
86
+ return smb2_header.protocol == RubySMB::SMB2::SMB2_PROTOCOL_ID &&
87
+ smb2_header.command == self.class::COMMAND
88
+ end
89
+ end
90
+
91
+ def initialize_instance
92
+ super
93
+
94
+ unless [RubySMB::SMB1::Packet::EmptyPacket, RubySMB::SMB2::Packet::ErrorPacket].any? {|klass| self.is_a? klass}
95
+ case packet_smb_version
96
+ when 'SMB1'
97
+ smb_header.command = self.class::COMMAND
98
+ when 'SMB2'
99
+ smb2_header.command = self.class::COMMAND
100
+ end
101
+ end
102
+ end
103
+
75
104
  # Returns an array of hashes representing the
76
105
  # fields for this record.
77
106
  #
@@ -10,7 +10,7 @@ module RubySMB
10
10
  SMB_COM_TRANSACTION2_SECONDARY = 0x33
11
11
  SMB_COM_TREE_DISCONNECT = 0x71
12
12
  SMB_COM_NEGOTIATE = 0x72
13
- SMB_COM_SESSION_SETUP = 0x73
13
+ SMB_COM_SESSION_SETUP_ANDX = 0x73
14
14
  SMB_COM_LOGOFF = 0x74
15
15
  SMB_COM_TREE_CONNECT = 0x75
16
16
  SMB_COM_NT_TRANSACT = 0xA0
@@ -76,17 +76,22 @@ module RubySMB
76
76
  # Closes the handle to the remote file.
77
77
  #
78
78
  # @return [WindowsError::ErrorCode] the NTStatus code returned by the operation
79
- # @raise [RubySMB::Error::InvalidPacket] if the response command is not SMB_COM_CLOSE
79
+ # @raise [RubySMB::Error::InvalidPacket] if the response packet is not valid
80
80
  # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
81
81
  def close
82
82
  close_request = set_header_fields(RubySMB::SMB1::Packet::CloseRequest.new)
83
83
  raw_response = @tree.client.send_recv(close_request)
84
84
  response = RubySMB::SMB1::Packet::CloseResponse.read(raw_response)
85
- unless response.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_CLOSE
86
- raise RubySMB::Error::InvalidPacket, 'Not a CloseResponse packet'
85
+ unless response.valid?
86
+ raise RubySMB::Error::InvalidPacket.new(
87
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
88
+ expected_cmd: RubySMB::SMB1::Packet::CloseResponse::COMMAND,
89
+ received_proto: response.smb_header.protocol,
90
+ received_cmd: response.smb_header.command
91
+ )
87
92
  end
88
93
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
89
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
94
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
90
95
  end
91
96
  response.status_code
92
97
  end
@@ -98,7 +103,7 @@ module RubySMB
98
103
  # @param bytes [Integer] the number of bytes to read
99
104
  # @param offset [Integer] the byte offset in the file to start reading from
100
105
  # @return [String] the data read from the file
101
- # @raise [RubySMB::Error::InvalidPacket] if the response command is not SMB_COM_READ_ANDX
106
+ # @raise [RubySMB::Error::InvalidPacket] if the response packet is not valid
102
107
  # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
103
108
  def read(bytes: @size, offset: 0)
104
109
  atomic_read_size = if bytes > @tree.client.max_buffer_size
@@ -113,11 +118,16 @@ module RubySMB
113
118
  read_request = read_packet(read_length: atomic_read_size, offset: offset)
114
119
  raw_response = @tree.client.send_recv(read_request)
115
120
  response = RubySMB::SMB1::Packet::ReadAndxResponse.read(raw_response)
116
- unless response.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_READ_ANDX
117
- raise RubySMB::Error::InvalidPacket, 'Not a ReadAndxResponse packet'
121
+ unless response.valid?
122
+ raise RubySMB::Error::InvalidPacket.new(
123
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
124
+ expected_cmd: RubySMB::SMB1::Packet::ReadAndxResponse::COMMAND,
125
+ received_proto: response.smb_header.protocol,
126
+ received_cmd: response.smb_header.command
127
+ )
118
128
  end
119
129
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
120
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
130
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
121
131
  end
122
132
 
123
133
  if response.is_a?(RubySMB::SMB1::Packet::ReadAndxResponse)
@@ -146,17 +156,27 @@ module RubySMB
146
156
  def read_packet(read_length: 0, offset: 0)
147
157
  read_request = set_header_fields(RubySMB::SMB1::Packet::ReadAndxRequest.new)
148
158
  read_request.parameter_block.max_count_of_bytes_to_return = read_length
159
+ read_request.parameter_block.min_count_of_bytes_to_return = read_length
160
+ read_request.parameter_block.remaining = read_length
149
161
  read_request.parameter_block.offset = offset
150
162
  read_request
151
163
  end
152
-
164
+
153
165
  def send_recv_read(read_length: 0, offset: 0)
154
166
  read_request = read_packet(read_length: read_length, offset: offset)
155
167
  raw_response = tree.client.send_recv(read_request)
156
168
 
157
169
  response = RubySMB::SMB1::Packet::ReadAndxResponse.read(raw_response)
170
+ unless response.valid?
171
+ raise RubySMB::Error::InvalidPacket.new(
172
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
173
+ expected_cmd: RubySMB::SMB1::Packet::ReadAndxResponse::COMMAND,
174
+ received_proto: response.smb_header.protocol,
175
+ received_cmd: response.smb_header.command
176
+ )
177
+ end
158
178
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
159
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
179
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
160
180
  end
161
181
 
162
182
  response.data_block.data.to_binary_s
@@ -165,9 +185,18 @@ module RubySMB
165
185
  # Delete a file on close
166
186
  #
167
187
  # @return [WindowsError::ErrorCode] the NTStatus Response code
188
+ # @raise [RubySMB::Error::InvalidPacket] if the response packet is not valid
168
189
  def delete
169
190
  raw_response = @tree.client.send_recv(delete_packet)
170
191
  response = RubySMB::SMB1::Packet::Trans2::SetFileInformationResponse.read(raw_response)
192
+ unless response.valid?
193
+ raise RubySMB::Error::InvalidPacket.new(
194
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
195
+ expected_cmd: RubySMB::SMB1::Packet::Trans2::SetFileInformationResponse::COMMAND,
196
+ received_proto: response.smb_header.protocol,
197
+ received_cmd: response.smb_header.command
198
+ )
199
+ end
171
200
  response.status_code
172
201
  end
173
202
 
@@ -190,7 +219,7 @@ module RubySMB
190
219
  # @param data [String] the data to write to the file
191
220
  # @param offset [Integer] the offset in the file to start writing from
192
221
  # @return [Integer] the count of bytes written
193
- # @raise [RubySMB::Error::InvalidPacket] if the response command is not SMB_COM_WRITE_ANDX
222
+ # @raise [RubySMB::Error::InvalidPacket] if the response packet is not valid
194
223
  # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
195
224
  def write(data:, offset: 0)
196
225
  buffer = data.dup
@@ -206,11 +235,16 @@ module RubySMB
206
235
  write_request = write_packet(data: buffer.slice!(0, atomic_write_size), offset: offset)
207
236
  raw_response = @tree.client.send_recv(write_request)
208
237
  response = RubySMB::SMB1::Packet::WriteAndxResponse.read(raw_response)
209
- unless response.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_WRITE_ANDX
210
- raise RubySMB::Error::InvalidPacket, 'Not a WriteAndxResponse packet'
238
+ unless response.valid?
239
+ raise RubySMB::Error::InvalidPacket.new(
240
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
241
+ expected_cmd: RubySMB::SMB1::Packet::WriteAndxResponse::COMMAND,
242
+ received_proto: response.smb_header.protocol,
243
+ received_cmd: response.smb_header.command
244
+ )
211
245
  end
212
246
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
213
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
247
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
214
248
  end
215
249
  bytes_written = response.parameter_block.count_low + (response.parameter_block.count_high << 16)
216
250
  total_bytes_written += bytes_written
@@ -235,12 +269,20 @@ module RubySMB
235
269
  write_request.parameter_block.remaining = write_request.parameter_block.data_length
236
270
  write_request
237
271
  end
238
-
272
+
239
273
  def send_recv_write(data:'', offset: 0)
240
274
  pkt = write_packet(data: data, offset: offset)
241
275
  pkt.set_64_bit_offset(true)
242
276
  raw_response = @tree.client.send_recv(pkt)
243
277
  response = RubySMB::SMB1::Packet::WriteAndxResponse.read(raw_response)
278
+ unless response.valid?
279
+ raise RubySMB::Error::InvalidPacket.new(
280
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
281
+ expected_cmd: RubySMB::SMB1::Packet::WriteAndxResponse::COMMAND,
282
+ received_proto: response.smb_header.protocol,
283
+ received_cmd: response.smb_header.command
284
+ )
285
+ end
244
286
  response.parameter_block.count_low
245
287
  end
246
288
 
@@ -248,9 +290,18 @@ module RubySMB
248
290
  #
249
291
  # @param new_file_name [String] the new name
250
292
  # @return [WindowsError::ErrorCode] the NTStatus Response code
293
+ # @raise [RubySMB::Error::InvalidPacket] if the response packet is not valid
251
294
  def rename(new_file_name)
252
295
  raw_response = tree.client.send_recv(rename_packet(new_file_name))
253
296
  response = RubySMB::SMB1::Packet::Trans2::SetFileInformationResponse.read(raw_response)
297
+ unless response.valid?
298
+ raise RubySMB::Error::InvalidPacket.new(
299
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
300
+ expected_cmd: RubySMB::SMB1::Packet::Trans2::SetFileInformationResponse::COMMAND,
301
+ received_proto: response.smb_header.protocol,
302
+ received_cmd: response.smb_header.command
303
+ )
304
+ end
254
305
  response.status_code
255
306
  end
256
307
 
@@ -4,6 +4,8 @@ module RubySMB
4
4
  # A SMB1 SMB_COM_CLOSE Request Packet as defined in
5
5
  # [2.2.4.5.1 Request](https://msdn.microsoft.com/en-us/library/ee442151.aspx)
6
6
  class CloseRequest < RubySMB::GenericPacket
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_CLOSE
8
+
7
9
  # A SMB1 Parameter Block as defined by the {CloseRequest}
8
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
9
11
  endian :little
@@ -20,11 +22,6 @@ module RubySMB
20
22
  parameter_block :parameter_block
21
23
  data_block :data_block
22
24
 
23
- def initialize_instance
24
- super
25
- smb_header.command = RubySMB::SMB1::Commands::SMB_COM_CLOSE
26
- end
27
-
28
25
  end
29
26
  end
30
27
  end
@@ -4,6 +4,8 @@ module RubySMB
4
4
  # A SMB1 SMB_COM_CLOSE Response Packet as defined in
5
5
  # [2.2.4.5.2 Response](https://msdn.microsoft.com/en-us/library/ee441667.aspx)
6
6
  class CloseResponse < RubySMB::GenericPacket
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_CLOSE
8
+
7
9
  # A SMB1 Parameter Block as defined by the {CloseResponse}
8
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
9
11
  end
@@ -18,7 +20,6 @@ module RubySMB
18
20
 
19
21
  def initialize_instance
20
22
  super
21
- smb_header.command = RubySMB::SMB1::Commands::SMB_COM_CLOSE
22
23
  smb_header.flags.reply = 1
23
24
  end
24
25
 
@@ -4,6 +4,8 @@ module RubySMB
4
4
  # This class represents an SMB1 Echo Request Packet as defined in
5
5
  # [2.2.4.39.1 Request](https://msdn.microsoft.com/en-us/library/ee441746.aspx)
6
6
  class EchoRequest < RubySMB::GenericPacket
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_ECHO
8
+
7
9
  # The {RubySMB::SMB1::ParameterBlock} specific to this packet type.
8
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
9
11
  uint16 :echo_count, label: 'Echo Count', initial_value: 1
@@ -18,10 +20,6 @@ module RubySMB
18
20
  parameter_block :parameter_block
19
21
  data_block :data_block
20
22
 
21
- def initialize_instance
22
- super
23
- smb_header.command = RubySMB::SMB1::Commands::SMB_COM_ECHO
24
- end
25
23
  end
26
24
  end
27
25
  end
@@ -4,6 +4,8 @@ module RubySMB
4
4
  # This class represents an SMB1 Echo Request Packet as defined in
5
5
  # [2.2.4.39.2 Response](https://msdn.microsoft.com/en-us/library/ee441626.aspx)
6
6
  class EchoResponse < RubySMB::GenericPacket
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_ECHO
8
+
7
9
  # The {RubySMB::SMB1::ParameterBlock} specific to this packet type.
8
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
9
11
  uint16 :sequence_number, label: 'Sequence Number'
@@ -20,7 +22,6 @@ module RubySMB
20
22
 
21
23
  def initialize_instance
22
24
  super
23
- smb_header.command = RubySMB::SMB1::Commands::SMB_COM_ECHO
24
25
  smb_header.flags.reply = 1
25
26
  end
26
27
  end
@@ -1,12 +1,21 @@
1
1
  module RubySMB
2
2
  module SMB1
3
3
  module Packet
4
- # This packet represent an SMB1 Response Packet when the parameter and
4
+ # This packet represent an SMB1 Error Response Packet when the parameter and
5
5
  # data blocks will be empty.
6
6
  class EmptyPacket < RubySMB::GenericPacket
7
+ attr_accessor :original_command
8
+
7
9
  smb_header :smb_header
8
10
  parameter_block :parameter_block
9
11
  data_block :data_block
12
+
13
+ def valid?
14
+ return smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
15
+ smb_header.command == @original_command &&
16
+ parameter_block.word_count == 0 &&
17
+ data_block.byte_count == 0
18
+ end
10
19
  end
11
20
  end
12
21
  end