ruby_smb 3.0.0 → 3.0.4

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