ruby_smb 3.0.0 → 3.0.4

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 (102) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/examples/anonymous_auth.rb +29 -6
  4. data/examples/auth_capture.rb +28 -0
  5. data/examples/file_server.rb +76 -0
  6. data/examples/read_file.rb +51 -10
  7. data/examples/tree_connect.rb +49 -8
  8. data/lib/ruby_smb/client/authentication.rb +11 -3
  9. data/lib/ruby_smb/client.rb +16 -2
  10. data/lib/ruby_smb/create_actions.rb +21 -0
  11. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_encrypt_file_srv_request.rb +20 -0
  12. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_encrypt_file_srv_response.rb +20 -0
  13. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_open_file_raw_request.rb +21 -0
  14. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_open_file_raw_response.rb +21 -0
  15. data/lib/ruby_smb/dcerpc/encrypting_file_system.rb +44 -0
  16. data/lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_request.rb +22 -0
  17. data/lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_response.rb +20 -0
  18. data/lib/ruby_smb/dcerpc/print_system/rpc_enum_printer_drivers_request.rb +24 -0
  19. data/lib/ruby_smb/dcerpc/print_system/rpc_enum_printer_drivers_response.rb +23 -0
  20. data/lib/ruby_smb/dcerpc/print_system/rpc_get_printer_driver_directory_request.rb +24 -0
  21. data/lib/ruby_smb/dcerpc/print_system/rpc_get_printer_driver_directory_response.rb +22 -0
  22. data/lib/ruby_smb/dcerpc/print_system.rb +69 -0
  23. data/lib/ruby_smb/dcerpc.rb +2 -2
  24. data/lib/ruby_smb/field/nt_status.rb +20 -1
  25. data/lib/ruby_smb/fscc/file_information/file_ea_information.rb +14 -0
  26. data/lib/ruby_smb/fscc/file_information/file_network_open_information.rb +22 -0
  27. data/lib/ruby_smb/fscc/file_information/file_stream_information.rb +16 -0
  28. data/lib/ruby_smb/fscc/file_information.rb +29 -0
  29. data/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information.rb +46 -0
  30. data/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information.rb +19 -0
  31. data/lib/ruby_smb/fscc/file_system_information.rb +22 -0
  32. data/lib/ruby_smb/fscc.rb +1 -0
  33. data/lib/ruby_smb/generic_packet.rb +6 -0
  34. data/lib/ruby_smb/gss/provider/authenticator.rb +4 -0
  35. data/lib/ruby_smb/gss/provider/ntlm.rb +13 -3
  36. data/lib/ruby_smb/server/server_client/negotiation.rb +0 -2
  37. data/lib/ruby_smb/server/server_client/session_setup.rb +43 -32
  38. data/lib/ruby_smb/server/server_client/share_io.rb +28 -0
  39. data/lib/ruby_smb/server/server_client/tree_connect.rb +60 -0
  40. data/lib/ruby_smb/server/server_client.rb +214 -24
  41. data/lib/ruby_smb/server/session.rb +71 -0
  42. data/lib/ruby_smb/server/share/provider/disk.rb +437 -0
  43. data/lib/ruby_smb/server/share/provider/pipe.rb +27 -0
  44. data/lib/ruby_smb/server/share/provider/processor.rb +76 -0
  45. data/lib/ruby_smb/server/share/provider.rb +38 -0
  46. data/lib/ruby_smb/server/share.rb +11 -0
  47. data/lib/ruby_smb/server.rb +35 -3
  48. data/lib/ruby_smb/signing.rb +37 -11
  49. data/lib/ruby_smb/smb1/commands.rb +4 -0
  50. data/lib/ruby_smb/smb1/tree.rb +87 -79
  51. data/lib/ruby_smb/smb1.rb +0 -1
  52. data/lib/ruby_smb/smb2/bit_field/smb2_header_flags.rb +2 -1
  53. data/lib/ruby_smb/smb2/commands.rb +4 -0
  54. data/lib/ruby_smb/smb2/create_context/request.rb +64 -0
  55. data/lib/ruby_smb/smb2/create_context/response.rb +62 -0
  56. data/lib/ruby_smb/smb2/create_context.rb +74 -22
  57. data/lib/ruby_smb/smb2/packet/create_request.rb +44 -11
  58. data/lib/ruby_smb/smb2/packet/create_response.rb +17 -3
  59. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +1 -1
  60. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +2 -2
  61. data/lib/ruby_smb/smb2/packet/query_info_request.rb +43 -0
  62. data/lib/ruby_smb/smb2/packet/query_info_response.rb +23 -0
  63. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +1 -1
  64. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +1 -0
  65. data/lib/ruby_smb/smb2/packet.rb +2 -0
  66. data/lib/ruby_smb/smb2/tree.rb +80 -70
  67. data/lib/ruby_smb/smb2.rb +11 -0
  68. data/lib/ruby_smb/smb_error.rb +110 -0
  69. data/lib/ruby_smb/version.rb +1 -1
  70. data/lib/ruby_smb.rb +2 -0
  71. data/ruby_smb.gemspec +1 -1
  72. data/spec/lib/ruby_smb/client_spec.rb +10 -0
  73. data/spec/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_encrypt_file_srv_request_spec.rb +30 -0
  74. data/spec/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_encrypt_file_srv_response_spec.rb +30 -0
  75. data/spec/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_open_file_raw_request_spec.rb +38 -0
  76. data/spec/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_open_file_raw_response_spec.rb +38 -0
  77. data/spec/lib/ruby_smb/dcerpc/print_system/driver_container_spec.rb +41 -0
  78. data/spec/lib/ruby_smb/dcerpc/print_system/driver_info2_spec.rb +64 -0
  79. data/spec/lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_request_spec.rb +59 -0
  80. data/spec/lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_response_spec.rb +30 -0
  81. data/spec/lib/ruby_smb/dcerpc/print_system/rpc_enum_printer_drivers_request_spec.rb +62 -0
  82. data/spec/lib/ruby_smb/dcerpc/print_system/rpc_enum_printer_drivers_response_spec.rb +54 -0
  83. data/spec/lib/ruby_smb/dcerpc/print_system/rpc_get_printer_driver_directory_request_spec.rb +62 -0
  84. data/spec/lib/ruby_smb/dcerpc/print_system/rpc_get_printer_driver_directory_response_spec.rb +46 -0
  85. data/spec/lib/ruby_smb/field/nt_status_spec.rb +6 -2
  86. data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +4 -0
  87. data/spec/lib/ruby_smb/server/server_client_spec.rb +36 -53
  88. data/spec/lib/ruby_smb/server/session_spec.rb +38 -0
  89. data/spec/lib/ruby_smb/server/share/provider/disk_spec.rb +61 -0
  90. data/spec/lib/ruby_smb/server/share/provider/pipe_spec.rb +31 -0
  91. data/spec/lib/ruby_smb/server/share/provider_spec.rb +13 -0
  92. data/spec/lib/ruby_smb/smb1/tree_spec.rb +3 -3
  93. data/spec/lib/ruby_smb/smb2/bit_field/header_flags_spec.rb +8 -2
  94. data/spec/lib/ruby_smb/smb2/{create_context_spec.rb → create_context/create_context_request_spec.rb} +1 -1
  95. data/spec/lib/ruby_smb/smb2/packet/create_request_spec.rb +5 -5
  96. data/spec/lib/ruby_smb/smb2/packet/create_response_spec.rb +9 -5
  97. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +3 -2
  98. data/spec/lib/ruby_smb/smb2/tree_spec.rb +3 -3
  99. data.tar.gz.sig +0 -0
  100. metadata +71 -7
  101. metadata.gz.sig +0 -0
  102. data/lib/ruby_smb/smb1/create_actions.rb +0 -20
@@ -8,24 +8,35 @@ module RubySMB
8
8
 
9
9
  # Take an SMB1 packet and sign it.
10
10
  #
11
- # @param packet [RubySMB::GenericPacket] the packet to sign
11
+ # @param [RubySMB::GenericPacket] packet The packet to sign.
12
12
  # @return [RubySMB::GenericPacket] the signed packet
13
13
  def smb1_sign(packet)
14
14
  # Pack the Sequence counter into a int64le
15
- packed_sequence_counter = [sequence_counter].pack('Q<')
15
+ packed_sequence_counter = [@sequence_counter].pack('Q<')
16
16
  packet.smb_header.security_features = packed_sequence_counter
17
- signature = OpenSSL::Digest::MD5.digest(session_key + packet.to_binary_s)[0, 8]
17
+ signature = OpenSSL::Digest::MD5.digest(@session_key + packet.to_binary_s)[0, 8]
18
18
  packet.smb_header.security_features = signature
19
19
  @sequence_counter += 1
20
20
 
21
21
  packet
22
22
  end
23
23
 
24
- # Take an SMB2 packet and sign it.
24
+ # Take an SMB2 packet and sign it. This version is an instance method that
25
+ # accesses the necessary values from the object instance.
25
26
  #
26
- # @param packet [RubySMB::GenericPacket] the packet to sign
27
+ # @param [RubySMB::GenericPacket] packet The packet to sign.
27
28
  # @return [RubySMB::GenericPacket] the signed packet
28
29
  def smb2_sign(packet)
30
+ Signing::smb2_sign(packet, @session_key)
31
+ end
32
+
33
+ # Take an SMB2 packet and sign it. This version is a module function that
34
+ # requires the necessary values to be explicitly passed to it.
35
+ #
36
+ # @param [RubySMB::GenericPacket] packet The packet to sign.
37
+ # @param [String] session_key The key to use for signing.
38
+ # @return [RubySMB::GenericPacket] the signed packet
39
+ def self.smb2_sign(packet, session_key)
29
40
  packet.smb2_header.flags.signed = 1
30
41
  packet.smb2_header.signature = "\x00" * 16
31
42
  hmac = OpenSSL::HMAC.digest(OpenSSL::Digest.new('SHA256'), session_key, packet.to_binary_s)
@@ -34,18 +45,33 @@ module RubySMB
34
45
  packet
35
46
  end
36
47
 
37
- # Take an SMB3 packet and sign it.
48
+ # Take an SMB3 packet and sign it. This version is an instance method that
49
+ # accesses the necessary values from the object instance.
38
50
  #
39
- # @param packet [RubySMB::GenericPacket] the packet to sign
51
+ # @param [RubySMB::GenericPacket] packet The packet to sign.
40
52
  # @return [RubySMB::GenericPacket] the signed packet
41
53
  def smb3_sign(packet)
42
- case @dialect
54
+ Signing::smb3_sign(packet, @session_key, @dialect, @preauth_integrity_hash_value)
55
+ end
56
+
57
+ # Take an SMB3 packet and sign it. This version is a module function that
58
+ # requires the necessary values to be explicitly passed to it.
59
+ #
60
+ # @param [RubySMB::GenericPacket] packet The packet to sign.
61
+ # @param [String] session_key The key to use for signing.
62
+ # @param [String] dialect The SMB3 dialect to sign for.
63
+ # @param [String] preauth_integrity_hash The preauth integrity hash as
64
+ # required by the 3.1.1 dialect.
65
+ # @return [RubySMB::GenericPacket] the signed packet
66
+ def self.smb3_sign(packet, session_key, dialect, preauth_integrity_hash=nil)
67
+ case dialect
43
68
  when '0x0300', '0x0302'
44
- signing_key = Crypto::KDF.counter_mode(@session_key, "SMB2AESCMAC\x00", "SmbSign\x00")
69
+ signing_key = Crypto::KDF.counter_mode(session_key, "SMB2AESCMAC\x00", "SmbSign\x00")
45
70
  when '0x0311'
46
- signing_key = Crypto::KDF.counter_mode(@session_key, "SMBSigningKey\x00", @preauth_integrity_hash_value)
71
+ raise ArgumentError.new('the preauth integrity hash is required for the specified dialect') if preauth_integrity_hash.nil?
72
+ signing_key = Crypto::KDF.counter_mode(session_key, "SMBSigningKey\x00", preauth_integrity_hash)
47
73
  else
48
- raise Error::SigningError.new("Dialect #{@dialect.inspect} is incompatible with SMBv3 signing")
74
+ raise Error::SigningError.new("Dialect #{dialect.inspect} is incompatible with SMBv3 signing")
49
75
  end
50
76
 
51
77
  packet.smb2_header.flags.signed = 1
@@ -17,6 +17,10 @@ module RubySMB
17
17
  SMB_COM_NT_TRANSACT_SECONDARY = 0xA1
18
18
  SMB_COM_NT_CREATE_ANDX = 0xA2
19
19
  SMB_COM_NO_ANDX_COMMAND = 0xFF
20
+
21
+ def self.name(value)
22
+ constants.select { |c| c.upcase == c }.find { |c| const_get(c) == value }
23
+ end
20
24
  end
21
25
  end
22
26
  end
@@ -59,8 +59,8 @@ module RubySMB
59
59
  # Make sure we don't modify the caller's hash options
60
60
  opts = opts.dup
61
61
  opts[:filename] = opts[:filename].dup
62
- opts[:filename].prepend('\\') unless opts[:filename].start_with?('\\')
63
- open_file(**opts)
62
+ opts[:filename].prepend('\\') unless opts[:filename].start_with?('\\'.encode(opts[:filename].encoding))
63
+ _open(**opts)
64
64
  end
65
65
 
66
66
  # Open a file on the remote share.
@@ -80,83 +80,12 @@ module RubySMB
80
80
  # @return [RubySMB::SMB1::File] handle to the created file
81
81
  # @raise [RubySMB::Error::InvalidPacket] if the response command is not SMB_COM_NT_CREATE_ANDX
82
82
  # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
83
- def open_file(filename:, flags: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
84
- impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false)
85
- nt_create_andx_request = RubySMB::SMB1::Packet::NtCreateAndxRequest.new
86
- nt_create_andx_request = set_header_fields(nt_create_andx_request)
87
-
88
- nt_create_andx_request.parameter_block.ext_file_attributes.normal = 1
89
-
90
- if flags
91
- nt_create_andx_request.parameter_block.flags = flags
92
- else
93
- nt_create_andx_request.parameter_block.flags.request_extended_response = 1
94
- end
95
-
96
- if options
97
- nt_create_andx_request.parameter_block.create_options = options
98
- else
99
- nt_create_andx_request.parameter_block.create_options.directory_file = 0
100
- nt_create_andx_request.parameter_block.create_options.non_directory_file = 1
101
- end
102
-
103
- if read
104
- nt_create_andx_request.parameter_block.share_access.share_read = 1
105
- nt_create_andx_request.parameter_block.desired_access.read_data = 1
106
- nt_create_andx_request.parameter_block.desired_access.read_ea = 1
107
- nt_create_andx_request.parameter_block.desired_access.read_attr = 1
108
- nt_create_andx_request.parameter_block.desired_access.read_control = 1
109
- end
110
-
111
- if write
112
- nt_create_andx_request.parameter_block.share_access.share_write = 1
113
- nt_create_andx_request.parameter_block.desired_access.write_data = 1
114
- nt_create_andx_request.parameter_block.desired_access.append_data = 1
115
- nt_create_andx_request.parameter_block.desired_access.write_ea = 1
116
- nt_create_andx_request.parameter_block.desired_access.write_attr = 1
117
- end
118
-
119
- if delete
120
- nt_create_andx_request.parameter_block.share_access.share_delete = 1
121
- nt_create_andx_request.parameter_block.desired_access.delete_access = 1
122
- end
123
-
124
- nt_create_andx_request.parameter_block.impersonation_level = impersonation
125
- nt_create_andx_request.parameter_block.create_disposition = disposition
126
-
127
- unicode_enabled = nt_create_andx_request.smb_header.flags2.unicode == 1
128
- nt_create_andx_request.data_block.file_name = add_null_termination(str: filename, unicode: unicode_enabled)
129
-
130
- raw_response = @client.send_recv(nt_create_andx_request)
131
- response = RubySMB::SMB1::Packet::NtCreateAndxResponse.read(raw_response)
132
- unless response.valid?
133
- if response.is_a?(RubySMB::SMB1::Packet::EmptyPacket) &&
134
- response.smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
135
- response.smb_header.command == response.original_command
136
- raise RubySMB::Error::InvalidPacket.new(
137
- 'The response seems to be an SMB1 NtCreateAndxResponse but an '\
138
- 'error occurs while parsing it. It is probably missing the '\
139
- 'required extended information.'
140
- )
141
- end
142
- raise RubySMB::Error::InvalidPacket.new(
143
- expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
144
- expected_cmd: RubySMB::SMB1::Packet::NtCreateAndxResponse::COMMAND,
145
- packet: response
146
- )
147
- end
148
- unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
149
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code
150
- end
151
-
152
- case response.parameter_block.resource_type
153
- when RubySMB::SMB1::ResourceType::BYTE_MODE_PIPE, RubySMB::SMB1::ResourceType::MESSAGE_MODE_PIPE
154
- RubySMB::SMB1::Pipe.new(name: filename, tree: self, response: response)
155
- when RubySMB::SMB1::ResourceType::DISK
156
- RubySMB::SMB1::File.new(name: filename, tree: self, response: response)
157
- else
158
- raise RubySMB::Error::RubySMBError
159
- end
83
+ def open_file(opts)
84
+ # Make sure we don't modify the caller's hash options
85
+ opts = opts.dup
86
+ opts[:filename] = opts[:filename].dup
87
+ opts[:filename] = opts[:filename][1..-1] if opts[:filename].start_with?('\\'.encode(opts[:filename].encoding))
88
+ _open(**opts)
160
89
  end
161
90
 
162
91
  # List `directory` on the remote share.
@@ -264,6 +193,85 @@ module RubySMB
264
193
 
265
194
  private
266
195
 
196
+ def _open(filename:, flags: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
197
+ impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false)
198
+ nt_create_andx_request = RubySMB::SMB1::Packet::NtCreateAndxRequest.new
199
+ nt_create_andx_request = set_header_fields(nt_create_andx_request)
200
+
201
+ nt_create_andx_request.parameter_block.ext_file_attributes.normal = 1
202
+
203
+ if flags
204
+ nt_create_andx_request.parameter_block.flags = flags
205
+ else
206
+ nt_create_andx_request.parameter_block.flags.request_extended_response = 1
207
+ end
208
+
209
+ if options
210
+ nt_create_andx_request.parameter_block.create_options = options
211
+ else
212
+ nt_create_andx_request.parameter_block.create_options.directory_file = 0
213
+ nt_create_andx_request.parameter_block.create_options.non_directory_file = 1
214
+ end
215
+
216
+ if read
217
+ nt_create_andx_request.parameter_block.share_access.share_read = 1
218
+ nt_create_andx_request.parameter_block.desired_access.read_data = 1
219
+ nt_create_andx_request.parameter_block.desired_access.read_ea = 1
220
+ nt_create_andx_request.parameter_block.desired_access.read_attr = 1
221
+ nt_create_andx_request.parameter_block.desired_access.read_control = 1
222
+ end
223
+
224
+ if write
225
+ nt_create_andx_request.parameter_block.share_access.share_write = 1
226
+ nt_create_andx_request.parameter_block.desired_access.write_data = 1
227
+ nt_create_andx_request.parameter_block.desired_access.append_data = 1
228
+ nt_create_andx_request.parameter_block.desired_access.write_ea = 1
229
+ nt_create_andx_request.parameter_block.desired_access.write_attr = 1
230
+ end
231
+
232
+ if delete
233
+ nt_create_andx_request.parameter_block.share_access.share_delete = 1
234
+ nt_create_andx_request.parameter_block.desired_access.delete_access = 1
235
+ end
236
+
237
+ nt_create_andx_request.parameter_block.impersonation_level = impersonation
238
+ nt_create_andx_request.parameter_block.create_disposition = disposition
239
+
240
+ unicode_enabled = nt_create_andx_request.smb_header.flags2.unicode == 1
241
+ nt_create_andx_request.data_block.file_name = add_null_termination(str: filename, unicode: unicode_enabled)
242
+
243
+ raw_response = @client.send_recv(nt_create_andx_request)
244
+ response = RubySMB::SMB1::Packet::NtCreateAndxResponse.read(raw_response)
245
+ unless response.valid?
246
+ if response.is_a?(RubySMB::SMB1::Packet::EmptyPacket) &&
247
+ response.smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
248
+ response.smb_header.command == response.original_command
249
+ raise RubySMB::Error::InvalidPacket.new(
250
+ 'The response seems to be an SMB1 NtCreateAndxResponse but an '\
251
+ 'error occurs while parsing it. It is probably missing the '\
252
+ 'required extended information.'
253
+ )
254
+ end
255
+ raise RubySMB::Error::InvalidPacket.new(
256
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
257
+ expected_cmd: RubySMB::SMB1::Packet::NtCreateAndxResponse::COMMAND,
258
+ packet: response
259
+ )
260
+ end
261
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
262
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
263
+ end
264
+
265
+ case response.parameter_block.resource_type
266
+ when RubySMB::SMB1::ResourceType::BYTE_MODE_PIPE, RubySMB::SMB1::ResourceType::MESSAGE_MODE_PIPE
267
+ RubySMB::SMB1::Pipe.new(name: filename, tree: self, response: response)
268
+ when RubySMB::SMB1::ResourceType::DISK
269
+ RubySMB::SMB1::File.new(name: filename, tree: self, response: response)
270
+ else
271
+ raise RubySMB::Error::RubySMBError
272
+ end
273
+ end
274
+
267
275
  # Sets ParameterBlock options for FIND_FIRST2 and
268
276
  # FIND_NEXT2 requests. In particular we need to do this
269
277
  # to tell the server to ignore the Trans2DataBlock as we are
data/lib/ruby_smb/smb1.rb CHANGED
@@ -5,7 +5,6 @@ module RubySMB
5
5
  # Protocol ID value. Translates to \xFFSMB
6
6
  SMB_PROTOCOL_ID = 0xFF534D42
7
7
 
8
- require 'ruby_smb/smb1/create_actions'
9
8
  require 'ruby_smb/smb1/oplock_levels'
10
9
  require 'ruby_smb/smb1/resource_type'
11
10
  require 'ruby_smb/smb1/commands'
@@ -5,7 +5,8 @@ module RubySMB
5
5
  # [2.2.1.2 SMB2 Packet Header - SYNC](https://msdn.microsoft.com/en-us/library/cc246529.aspx)
6
6
  class Smb2HeaderFlags < BinData::Record
7
7
  endian :little
8
- bit4 :reserved3, label: 'Reserved', initial_value: 0
8
+ bit1 :reserved3, label: 'Reserved', initial_value: 0
9
+ bit3 :priority, label: 'Priority'
9
10
  bit1 :signed, label: 'Packet Signed'
10
11
  bit1 :related_operations, label: 'Chained Request'
11
12
  bit1 :async_command, label: 'ASYNC Command', initial_value: 0
@@ -20,6 +20,10 @@ module RubySMB
20
20
  CHANGE_NOTIFY = 0x0f
21
21
  QUERY_INFO = 0x10
22
22
  SET_INFO = 0x11
23
+
24
+ def self.name(value)
25
+ constants.select { |c| c.upcase == c }.find { |c| const_get(c) == value }
26
+ end
23
27
  end
24
28
  end
25
29
  end
@@ -0,0 +1,64 @@
1
+ require 'ruby_smb/dcerpc/uuid'
2
+
3
+ module RubySMB
4
+ module SMB2
5
+ module CreateContext
6
+ # [2.2.13.2.3 SMB2_CREATE_DURABLE_HANDLE_REQUEST](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/9999d870-b664-4e51-a187-1c3c16a1ae1c)
7
+ class CreateDurableHandleRequest < BinData::Record
8
+ NAME = CREATE_DURABLE_HANDLE
9
+
10
+ endian :little
11
+ string :reserved, label: 'Reserved', length: 16
12
+ end
13
+
14
+ # [2.2.13.2.11 SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5e361a29-81a7-4774-861d-f290ea53a00e)
15
+ class CreateDurableHandleV2Request < BinData::Record
16
+ NAME = CREATE_DURABLE_HANDLE_V2
17
+
18
+ endian :little
19
+ uint32 :timeout, label: 'Timeout'
20
+ struct :flags, label: 'Flags' do
21
+ bit6 :reserved
22
+ bit1 :persistent, label: 'Persistent Handle'
23
+ bit1 :reserved1
24
+ skip length: 3
25
+ end
26
+ string :reserved, length: 8
27
+ uuid :create_guid, label: 'Create GUID'
28
+ end
29
+
30
+ # [2.2.13.2.5 SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5ea40835-5d40-4e85-977d-13cd745d3af8)
31
+ class CreateQueryMaximalAccessRequest < BinData::Record
32
+ NAME = CREATE_QUERY_MAXIMAL_ACCESS
33
+
34
+ default_parameter length: 0
35
+
36
+ endian :little
37
+ file_time :timestamp, label: 'Timestamp', onlyif: -> { length != 0 }
38
+ end
39
+
40
+ # [2.2.13.2.9 SMB2_CREATE_QUERY_ON_DISK_ID](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/6eb9162a-3278-4513-988a-6b52bed30fc3)
41
+ class CreateQueryOnDiskIdRequest < BinData::Record
42
+ NAME = CREATE_QUERY_ON_DISK_ID
43
+
44
+ endian :little
45
+ end
46
+
47
+ class CreateContextRequest < CreateContext
48
+ delayed_io :data, read_abs_offset: -> { abs_offset + data_offset } do
49
+ choice :data, selection: -> { name.snapshot } do
50
+ create_durable_handle_request CREATE_DURABLE_HANDLE, length: :data_length
51
+ create_durable_handle_v2_request CREATE_DURABLE_HANDLE_V2, length: :data_length
52
+ create_query_maximal_access_request CREATE_QUERY_MAXIMAL_ACCESS, length: :data_length
53
+ create_query_on_disk_id_request CREATE_QUERY_ON_DISK_ID, length: :data_length
54
+ string :default, read_length: :data_length
55
+ end
56
+ end
57
+ end
58
+
59
+ class CreateContextArrayRequest < CreateContextArray
60
+ default_parameters type: :create_context_request
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,62 @@
1
+ module RubySMB
2
+ module SMB2
3
+ module CreateContext
4
+ # [2.2.14.2.3 SMB2_CREATE_DURABLE_HANDLE_RESPONSE](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/a3a11598-f228-47da-82bb-9418b9397041)
5
+ class CreateDurableHandleResponse < BinData::Record
6
+ NAME = CREATE_DURABLE_HANDLE
7
+
8
+ endian :little
9
+ string :reserved, label: 'Reserved', length: 8
10
+ end
11
+
12
+ # [2.2.14.2.12 SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/48c1049f-25a4-4f23-9a57-11ddd72ce985)
13
+ class CreateDurableHandleV2Response < BinData::Record
14
+ NAME = CREATE_DURABLE_HANDLE_V2
15
+
16
+ endian :little
17
+ uint32 :timeout, label: 'Timeout'
18
+ struct :flags, label: 'Flags' do
19
+ bit6 :reserved
20
+ bit1 :persistent, label: 'Persistent Handle'
21
+ bit1 :reserved1
22
+ skip length: 3
23
+ end
24
+ end
25
+
26
+ # [2.2.14.2.5 SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/0fe6be15-3a76-4032-9a44-56f846ac6244)
27
+ class CreateQueryMaximalAccessResponse < BinData::Record
28
+ NAME = CREATE_QUERY_MAXIMAL_ACCESS
29
+
30
+ endian :little
31
+ nt_status :query_status, label: 'Query Status'
32
+ file_access_mask :maximal_access, label: 'Maximal Access'
33
+ end
34
+
35
+ # [2.2.14.2.9 SMB2_CREATE_QUERY_ON_DISK_ID](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5c977939-1d8f-4774-9111-21e9195f3aca)
36
+ class CreateQueryOnDiskIdResponse < BinData::Record
37
+ NAME = CREATE_QUERY_ON_DISK_ID
38
+
39
+ endian :little
40
+ uint64 :disk_file_id, label: 'Disk File Id', initial_value: 0xffffffffffffffff
41
+ uint64 :volume_id, label: 'Volume Id'
42
+ string :reserved, label: 'Reserved', length: 16
43
+ end
44
+
45
+ class CreateContextResponse < CreateContext
46
+ delayed_io :data, read_abs_offset: -> { abs_offset + data_offset } do
47
+ choice :data, selection: -> { name.snapshot } do
48
+ create_durable_handle_response CREATE_DURABLE_HANDLE, length: :data_length
49
+ create_durable_handle_v2_response CREATE_DURABLE_HANDLE_V2, length: :data_length
50
+ create_query_maximal_access_response CREATE_QUERY_MAXIMAL_ACCESS, length: :data_length
51
+ create_query_on_disk_id_response CREATE_QUERY_ON_DISK_ID, length: :data_length
52
+ string :default, read_length: :data_length
53
+ end
54
+ end
55
+ end
56
+
57
+ class CreateContextArrayResponse < CreateContextArray
58
+ default_parameters type: :create_context_response
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,29 +1,81 @@
1
1
  module RubySMB
2
2
  module SMB2
3
- # An SMB2_CREATE_CONTEXT struct as defined in
4
- # [2.2.13.2 SMB2_CREATE_CONTEXT Request Values](https://msdn.microsoft.com/en-us/library/cc246504.aspx)
5
- class CreateContext < BinData::Record
6
- endian :little
7
-
8
- uint32 :next_offset, label: 'Offset to next Context'
9
- uint16 :name_offset, label: 'Offset to Name/Tag', initial_value: -> { name.rel_offset }
10
- uint16 :name_length, label: 'Length of Name/Tag', initial_value: -> { name.length }
11
- uint16 :reserved, label: 'Reserved Space'
12
- uint16 :data_offset, label: 'Offset to data', initial_value: -> { calc_data_offset }
13
- uint32 :data_length, label: 'Length of data', initial_value: -> { data.length }
14
- string :name, label: 'Name'
15
- uint32 :reserved2, label: 'Reserved Space'
16
- string :data, label: 'Data'
17
-
18
- private
19
-
20
- def calc_data_offset
21
- if data.empty?
22
- 0
23
- else
24
- data.rel_offset
3
+ module CreateContext
4
+ # Create name constants. Requests and responses have a shared name but some have different structures. Names are
5
+ # normalized to remove the request/response portion.
6
+ # [2.2.13.2 SMB2_CREATE_CONTEXT Request Values](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/75364667-3a93-4e2c-b771-592d8d5e876d)
7
+ CREATE_EA_BUFFER = 'ExtA'.freeze
8
+ CREATE_SD_BUFFER = 'SecD'.freeze
9
+ CREATE_DURABLE_HANDLE = 'DHnQ'.freeze
10
+ CREATE_DURABLE_HANDLE_RECONNECT = 'DHnC'.freeze
11
+ CREATE_ALLOCATION_SIZE = 'AISi'.freeze
12
+ CREATE_QUERY_MAXIMAL_ACCESS = 'MxAc'.freeze
13
+ CREATE_TIMEWARP_TOKEN = 'TWrp'.freeze
14
+ CREATE_QUERY_ON_DISK_ID = 'QFid'.freeze
15
+ CREATE_LEASE = 'RqLs'.freeze
16
+ CREATE_LEASE_V2 = 'RqLs'.freeze
17
+ CREATE_DURABLE_HANDLE_V2 = 'DH2Q'.freeze
18
+ CREATE_DURABLE_HANDLE_RECONNECT_v2 = 'DH2C'.freeze
19
+ CREATE_APP_INSTANCE_ID = "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74".b.freeze
20
+ CREATE_APP_INSTANCE_VERSION = "\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10".b.freeze
21
+ SVHDX_OPEN_DEVICE_CONTEXT = "\x9C\xCB\xCF\x9E\x04\xC1\xE6\x43\x98\x0E\x15\x8D\xA1\xF6\xEC\x83".b.freeze
22
+
23
+ # An SMB2_CREATE_CONTEXT struct as defined in
24
+ # [2.2.13.2 SMB2_CREATE_CONTEXT Request Values](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/75364667-3a93-4e2c-b771-592d8d5e876d?redirectedfrom=MSDN)
25
+ class CreateContext < BinData::Record
26
+ unregister_self
27
+
28
+ endian :little
29
+ uint32 :next_offset, label: 'Offset to next Context'
30
+ uint16 :name_offset, label: 'Offset to Name/Tag', initial_value: -> { buffer.rel_offset }
31
+ uint16 :name_length, label: 'Length of Name/Tag', initial_value: -> { name.num_bytes }
32
+ uint16 :reserved, label: 'Reserved Space'
33
+ uint16 :data_offset, label: 'Offset to data', initial_value: -> { calc_data_offset }
34
+ uint32 :data_length, label: 'Length of data', initial_value: -> { data.num_bytes }
35
+ string :buffer, label: 'Buffer', initial_value: -> { build_buffer }, read_length: -> { calc_buffer_size }
36
+
37
+ delayed_io :name, read_abs_offset: -> { abs_offset + name_offset } do
38
+ string :name, label: 'Name', read_length: :name_length
39
+ end
40
+
41
+ private
42
+
43
+ def build_buffer
44
+ align = 8
45
+ buf = name.dup.tap { |obj| obj.abs_offset = 0 }.to_binary_s { |obj| obj.write_now! }
46
+ buf << "\x00".b * ((align - buf.length % align) % align)
47
+ buf << data.dup.tap { |obj| obj.abs_offset = 0 }.to_binary_s { |obj| obj.write_now! }
48
+ buf << "\x00".b * ((align - buf.length % align) % align)
49
+ end
50
+
51
+ def calc_buffer_size
52
+ align = 8
53
+ size = 0
54
+ size += name_length + ((align - name_length % align) % align)
55
+ size += data_length + ((align - data_length % align) % align)
56
+ size
57
+ end
58
+
59
+ def calc_data_offset
60
+ if data.num_bytes == 0
61
+ 0
62
+ else
63
+ align = 8
64
+ buffer.rel_offset + name_length + ((align - name_length % align) % align)
65
+ end
25
66
  end
26
67
  end
68
+
69
+ class CreateContextArray < BinData::Array
70
+ unregister_self
71
+
72
+ default_parameters read_until: -> { element&.next_offset == 0 }
73
+ endian :little
74
+ end
27
75
  end
28
76
  end
29
77
  end
78
+
79
+ require 'ruby_smb/smb2/bit_field'
80
+ require 'ruby_smb/smb2/create_context/request'
81
+ require 'ruby_smb/smb2/create_context/response'
@@ -4,9 +4,8 @@ module RubySMB
4
4
  # An SMB2 Create Request Packet as defined in
5
5
  # [2.2.13 SMB2 CREATE Request](https://msdn.microsoft.com/en-us/library/cc246502.aspx)
6
6
  class CreateRequest < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB2::Commands::CREATE
8
-
9
7
  require 'ruby_smb/smb1/bit_field/create_options'
8
+ COMMAND = RubySMB::SMB2::Commands::CREATE
10
9
 
11
10
  endian :little
12
11
  smb2_header :smb2_header
@@ -35,17 +34,51 @@ module RubySMB
35
34
  bit8 :reserved4, label: 'Reserved Space'
36
35
  end
37
36
 
38
- uint32 :create_disposition, label: 'Create Disposition'
39
- create_options :create_options
40
- uint16 :name_offset, label: 'Name Offset', initial_value: -> { name.abs_offset }
41
- uint16 :name_length, label: 'Name Length', initial_value: -> { name.do_num_bytes }
42
- uint32 :context_offset, label: 'Create Context Offset', initial_value: -> { context.abs_offset }
43
- uint32 :context_length, label: 'Create Context Length', initial_value: -> { context.do_num_bytes }
44
- string16 :name, label: 'File Name'
45
- uint32 :reserved5, label: 'Reserved Space'
37
+ uint32 :create_disposition, label: 'Create Disposition'
38
+ create_options :create_options
39
+ uint16 :name_offset, label: 'Name Offset', initial_value: -> { calc_name_offset }
40
+ uint16 :name_length, label: 'Name Length', initial_value: -> { name.num_bytes }
41
+ uint32 :contexts_offset, label: 'Create Contexts Offset', initial_value: -> { calc_contexts_offset }
42
+ uint32 :contexts_length, label: 'Create Contexts Length'
43
+ count_bytes_remaining :bytes_remaining
44
+ string :buffer, label: 'Buffer', initial_value: -> { build_buffer }, read_length: :bytes_remaining
45
+
46
+ delayed_io :name, label: 'File Name', read_abs_offset: :name_offset do
47
+ string16 read_length: :name_length
48
+ end
49
+
50
+ delayed_io :contexts, label: 'Context Array', read_abs_offset: :contexts_offset, onlyif: -> { contexts_offset != 0 } do
51
+ buffer length: :contexts_length do
52
+ create_context_array_request :contexts
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def build_buffer
59
+ align = 8
60
+ buf = name.dup.tap { |obj| obj.abs_offset = 0 }.to_binary_s { |obj| obj.write_now! }
61
+ buf << "\x00".b * ((align - buf.length % align) % align)
62
+ buf << contexts.map(&:to_binary_s).join
63
+ buf << "\x00".b * ((align - buf.length % align) % align)
64
+ end
46
65
 
47
- array :context, label: 'Contexts', type: :create_context, read_until: :eof
66
+ def calc_contexts_offset
67
+ if contexts.num_bytes == 0
68
+ 0
69
+ else
70
+ align = 8
71
+ buffer.rel_offset + name_length + ((align - name_length % align) % align)
72
+ end
73
+ end
48
74
 
75
+ def calc_name_offset
76
+ if name.num_bytes == 0
77
+ 0
78
+ else
79
+ buffer.rel_offset
80
+ end
81
+ end
49
82
  end
50
83
  end
51
84
  end
@@ -21,15 +21,29 @@ module RubySMB
21
21
  file_attributes :file_attributes, label: 'File Attributes'
22
22
  uint32 :reserved, label: 'Reserved Space'
23
23
  smb2_fileid :file_id, label: 'File ID'
24
- uint32 :context_offset, label: 'Create Context Offset', initial_value: -> { context.abs_offset }
25
- uint32 :context_length, label: 'Create Context Length', initial_value: -> { context.do_num_bytes }
24
+ uint32 :contexts_offset, label: 'Create Contexts Offset'
25
+ uint32 :contexts_length, label: 'Create Contexts Length'
26
+ count_bytes_remaining :bytes_remaining
27
+ string :buffer, label: 'Buffer', initial_value: -> { build_buffer }, read_length: :bytes_remaining
26
28
 
27
- array :context, label: 'Contexts', type: :create_context, read_until: :eof
29
+ delayed_io :contexts, label: 'Context Array', read_abs_offset: -> { contexts_offset }, onlyif: -> { contexts_offset != 0 } do
30
+ buffer length: :contexts_length do
31
+ create_context_array_response :contexts
32
+ end
33
+ end
28
34
 
29
35
  def initialize_instance
30
36
  super
31
37
  smb2_header.flags.reply = 1
32
38
  end
39
+
40
+ private
41
+
42
+ def build_buffer
43
+ align = 8
44
+ buf = contexts.map(&:to_binary_s).join
45
+ buf << "\x00".b * ((align - buf.length % align) % align)
46
+ end
33
47
  end
34
48
  end
35
49
  end
@@ -25,7 +25,7 @@ module RubySMB
25
25
  uint16 :name_offset, label: 'File Name Offset', initial_value: -> { name.abs_offset }
26
26
  uint16 :name_length, label: 'File Name Length', initial_value: -> { name.do_num_bytes }
27
27
  uint32 :output_length, label: 'Output Buffer Length'
28
- string16 :name, label: 'Name/Search Pattern'
28
+ string16 :name, label: 'Name/Search Pattern', read_length: :name_length
29
29
 
30
30
  end
31
31
  end