ruby_smb 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -4
  4. data/.travis.yml +3 -5
  5. data/Gemfile +6 -2
  6. data/examples/negotiate.rb +51 -8
  7. data/examples/read_file_encryption.rb +56 -0
  8. data/lib/ruby_smb.rb +4 -0
  9. data/lib/ruby_smb/client.rb +172 -16
  10. data/lib/ruby_smb/client/authentication.rb +27 -8
  11. data/lib/ruby_smb/client/encryption.rb +62 -0
  12. data/lib/ruby_smb/client/negotiation.rb +133 -12
  13. data/lib/ruby_smb/client/signing.rb +19 -0
  14. data/lib/ruby_smb/client/tree_connect.rb +4 -4
  15. data/lib/ruby_smb/client/utils.rb +8 -7
  16. data/lib/ruby_smb/crypto.rb +30 -0
  17. data/lib/ruby_smb/dispatcher/socket.rb +2 -2
  18. data/lib/ruby_smb/error.rb +28 -1
  19. data/lib/ruby_smb/smb1/commands.rb +1 -1
  20. data/lib/ruby_smb/smb1/file.rb +4 -4
  21. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
  22. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
  23. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  24. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
  25. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
  26. data/lib/ruby_smb/smb1/pipe.rb +2 -2
  27. data/lib/ruby_smb/smb1/tree.rb +3 -3
  28. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  29. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  30. data/lib/ruby_smb/smb2/file.rb +25 -43
  31. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  32. data/lib/ruby_smb/smb2/packet.rb +2 -0
  33. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  34. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
  35. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +49 -3
  36. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  37. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
  38. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
  39. data/lib/ruby_smb/smb2/pipe.rb +3 -16
  40. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  41. data/lib/ruby_smb/smb2/tree.rb +23 -17
  42. data/lib/ruby_smb/version.rb +1 -1
  43. data/ruby_smb.gemspec +3 -1
  44. data/spec/lib/ruby_smb/client_spec.rb +1256 -57
  45. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  46. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  47. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  48. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  49. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  50. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  51. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  52. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  53. data/spec/lib/ruby_smb/smb2/file_spec.rb +86 -62
  54. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  55. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  56. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  57. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  58. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  59. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  60. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
  61. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +0 -40
  62. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  63. data/spec/lib/ruby_smb/smb2/tree_spec.rb +53 -8
  64. metadata +124 -75
  65. metadata.gz.sig +0 -0
@@ -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
@@ -84,7 +84,7 @@ module RubySMB
84
84
  end
85
85
  data
86
86
  rescue Errno::EINVAL, Errno::ECONNABORTED, Errno::ECONNRESET, TypeError, NoMethodError => e
87
- raise RubySMB::Error::CommunicationError, "An error occured reading from the Socket #{e.message}"
87
+ raise RubySMB::Error::CommunicationError, "An error occurred reading from the Socket #{e.message}"
88
88
  end
89
89
  end
90
90
  end
@@ -53,7 +53,28 @@ module RubySMB
53
53
  end
54
54
 
55
55
  # Raised when a response packet has a NTStatus code that was unexpected.
56
- 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
57
78
 
58
79
  # Raised when an error occurs with the underlying socket.
59
80
  class CommunicationError < RubySMBError; end
@@ -65,5 +86,11 @@ module RubySMB
65
86
  # Raised when trying to parse raw binary into a BitField and the data
66
87
  # is invalid.
67
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
68
95
  end
69
96
  end
@@ -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
@@ -91,7 +91,7 @@ module RubySMB
91
91
  )
92
92
  end
93
93
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
94
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
94
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
95
95
  end
96
96
  response.status_code
97
97
  end
@@ -127,7 +127,7 @@ module RubySMB
127
127
  )
128
128
  end
129
129
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
130
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
130
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
131
131
  end
132
132
 
133
133
  if response.is_a?(RubySMB::SMB1::Packet::ReadAndxResponse)
@@ -176,7 +176,7 @@ module RubySMB
176
176
  )
177
177
  end
178
178
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
179
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
179
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
180
180
  end
181
181
 
182
182
  response.data_block.data.to_binary_s
@@ -244,7 +244,7 @@ module RubySMB
244
244
  )
245
245
  end
246
246
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
247
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
247
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
248
248
  end
249
249
  bytes_written = response.parameter_block.count_low + (response.parameter_block.count_high << 16)
250
250
  total_bytes_written += bytes_written
@@ -4,7 +4,7 @@ module RubySMB
4
4
  # A SMB1 SMB_COM_SESSION_SETUP_ANDX Request Packet, without NTLMSSP as defined in
5
5
  # [2.2.4.53.1 Request](https://msdn.microsoft.com/en-us/library/ee441849.aspx)
6
6
  class SessionSetupLegacyRequest < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
8
8
 
9
9
  # A SMB1 Parameter Block as defined by the {SessionSetupRequest}
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
@@ -1,10 +1,10 @@
1
1
  module RubySMB
2
2
  module SMB1
3
3
  module Packet
4
- # A SMB1 SMB_COM_SESSION_SETUP Legacy Response Packet as defined in
4
+ # A SMB1 SMB_COM_SESSION_SETUP_ANDX Legacy Response Packet as defined in
5
5
  # [2.2.4.53.2 Response](https://msdn.microsoft.com/en-us/library/ee442143.aspx)
6
6
  class SessionSetupLegacyResponse < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
8
8
 
9
9
  # A SMB1 Parameter Block as defined by the {SessionSetupResponse}
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
@@ -4,7 +4,7 @@ module RubySMB
4
4
  # A SMB1 SMB_COM_SESSION_SETUP_ANDX Request Packet as defined in
5
5
  # [2.2.4.6.1](https://msdn.microsoft.com/en-us/library/cc246328.aspx)
6
6
  class SessionSetupRequest < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
8
8
 
9
9
  # A SMB1 Parameter Block as defined by the {SessionSetupRequest}
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
@@ -1,10 +1,10 @@
1
1
  module RubySMB
2
2
  module SMB1
3
3
  module Packet
4
- # A SMB1 SMB_COM_SESSION_SETUP Response Packet as defined in
4
+ # A SMB1 SMB_COM_SESSION_SETUP_ANDX Response Packet as defined in
5
5
  # [2.2.4.6.2](https://msdn.microsoft.com/en-us/library/cc246329.aspx)
6
6
  class SessionSetupResponse < RubySMB::GenericPacket
7
- COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
7
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
8
8
 
9
9
  # A SMB1 Parameter Block as defined by the {SessionSetupResponse}
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
@@ -43,7 +43,7 @@ module RubySMB
43
43
 
44
44
  # Represents the specific layout of the DataBlock for a {WriteAndxRequest} Packet.
45
45
  class DataBlock < RubySMB::SMB1::DataBlock
46
- uint8 :pad, label: 'Pad'
46
+ string :pad, label: 'Pad', length: 1
47
47
  string :data, label: 'Data'
48
48
  end
49
49
 
@@ -47,7 +47,7 @@ module RubySMB
47
47
  end
48
48
 
49
49
  unless response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW or response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
50
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
50
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
51
51
  end
52
52
 
53
53
  response
@@ -106,7 +106,7 @@ module RubySMB
106
106
  end
107
107
  unless [WindowsError::NTStatus::STATUS_SUCCESS,
108
108
  WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW].include?(trans_nmpipe_response.status_code)
109
- raise RubySMB::Error::UnexpectedStatusCode, trans_nmpipe_response.status_code.name
109
+ raise RubySMB::Error::UnexpectedStatusCode, trans_nmpipe_response.status_code
110
110
  end
111
111
 
112
112
  raw_data = trans_nmpipe_response.data_block.trans_data.read_data.to_binary_s
@@ -131,7 +131,7 @@ module RubySMB
131
131
  )
132
132
  end
133
133
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
134
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
134
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
135
135
  end
136
136
 
137
137
  case response.parameter_block.resource_type
@@ -191,7 +191,7 @@ module RubySMB
191
191
  )
192
192
  end
193
193
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
194
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
194
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
195
195
  end
196
196
 
197
197
  results = response.results(type, unicode: unicode)
@@ -226,7 +226,7 @@ module RubySMB
226
226
  )
227
227
  end
228
228
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
229
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
229
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
230
230
  end
231
231
 
232
232
  results += response.results(type, unicode: unicode)
@@ -4,7 +4,8 @@ module RubySMB
4
4
  # The SessionsFlags bit-field for a {RubySMB::SMB2::Packet::SessionSetupResponse}
5
5
  class SessionFlags < BinData::Record
6
6
  endian :little
7
- bit6 :reserved3, label: 'Reserved', initial_value: 0
7
+ bit5 :reserved3, label: 'Reserved', initial_value: 0
8
+ bit1 :encrypt_data, label: 'Encrypt Data', initial_value: 0
8
9
  bit1 :null, label: 'ASYNC Command', initial_value: 0
9
10
  bit1 :guest, label: 'Is Guest?', initial_value: 0
10
11
  resume_byte_alignment
@@ -8,7 +8,7 @@ module RubySMB
8
8
  bit2 :reserved1, label: 'Reserved Space'
9
9
  bit1 :vdo_caching, label: 'VDO Caching'
10
10
  bit1 :auto_caching, label: 'Auto Caching'
11
- bit2 :reserved2
11
+ bit2 :reserved2, label: 'Reserved Space'
12
12
  bit1 :dfs_root, label: 'DFS Root'
13
13
  bit1 :dfs, label: 'DFS'
14
14
  # byte boundary
@@ -20,9 +20,11 @@ module RubySMB
20
20
  bit1 :namespace_caching, label: 'Namespace Caching'
21
21
  bit1 :shared_delete, label: 'Force Shared Delete'
22
22
  bit1 :restrict_exclusive_opens, label: 'Restrict Exclusive Opens'
23
-
24
- bit8 :reserved3, label: 'Reserved Space'
25
- bit8 :reserved4, label: 'Reserved Space'
23
+ # byte boundary
24
+ bit5 :reserved3, label: 'Reserved Space'
25
+ bit1 :identity_remoting, label: 'Identity Remoting'
26
+ bit2 :reserved4, label: 'Reserved Space'
27
+ bit8 :reserved5, label: 'Reserved Space'
26
28
 
27
29
  def caching_type
28
30
  if vdo_caching == 1 && auto_caching.zero?
@@ -52,7 +52,12 @@ module RubySMB
52
52
  # @return [RubySMB::SMB2::Tree]
53
53
  attr_accessor :tree
54
54
 
55
- def initialize(tree:, response:, name:)
55
+ # Whether or not encryption is required (SMB 3.x)
56
+ # @!attribute [rw] encryption_required
57
+ # @return [Boolean]
58
+ attr_accessor :encryption_required
59
+
60
+ def initialize(tree:, response:, name:, encrypt: false)
56
61
  raise ArgumentError, 'No Tree Provided' if tree.nil?
57
62
  raise ArgumentError, 'No Response Provided' if response.nil?
58
63
 
@@ -66,6 +71,7 @@ module RubySMB
66
71
  @last_write = response.last_write.to_datetime
67
72
  @size = response.end_of_file
68
73
  @size_on_disk = response.allocation_size
74
+ @encryption_required = encrypt
69
75
  end
70
76
 
71
77
  # Appends the supplied data to the end of the file.
@@ -83,7 +89,7 @@ module RubySMB
83
89
  # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
84
90
  def close
85
91
  close_request = set_header_fields(RubySMB::SMB2::Packet::CloseRequest.new)
86
- raw_response = tree.client.send_recv(close_request)
92
+ raw_response = tree.client.send_recv(close_request, encrypt: @encryption_required)
87
93
  response = RubySMB::SMB2::Packet::CloseResponse.read(raw_response)
88
94
  unless response.valid?
89
95
  raise RubySMB::Error::InvalidPacket.new(
@@ -94,7 +100,7 @@ module RubySMB
94
100
  )
95
101
  end
96
102
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
97
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
103
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
98
104
  end
99
105
  response.status_code
100
106
  end
@@ -116,7 +122,7 @@ module RubySMB
116
122
  end
117
123
 
118
124
  read_request = read_packet(read_length: atomic_read_size, offset: offset)
119
- raw_response = tree.client.send_recv(read_request)
125
+ raw_response = tree.client.send_recv(read_request, encrypt: @encryption_required)
120
126
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
121
127
  unless response.valid?
122
128
  raise RubySMB::Error::InvalidPacket.new(
@@ -127,7 +133,7 @@ module RubySMB
127
133
  )
128
134
  end
129
135
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
130
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
136
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
131
137
  end
132
138
 
133
139
  data = response.buffer.to_binary_s
@@ -139,7 +145,7 @@ module RubySMB
139
145
  atomic_read_size = remaining_bytes if remaining_bytes < tree.client.server_max_read_size
140
146
 
141
147
  read_request = read_packet(read_length: atomic_read_size, offset: offset)
142
- raw_response = tree.client.send_recv(read_request)
148
+ raw_response = tree.client.send_recv(read_request, encrypt: @encryption_required)
143
149
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
144
150
  unless response.valid?
145
151
  raise RubySMB::Error::InvalidPacket.new(
@@ -150,7 +156,7 @@ module RubySMB
150
156
  )
151
157
  end
152
158
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
153
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
159
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
154
160
  end
155
161
 
156
162
  data << response.buffer.to_binary_s
@@ -170,10 +176,10 @@ module RubySMB
170
176
  read_request.offset = offset
171
177
  read_request
172
178
  end
173
-
179
+
174
180
  def send_recv_read(read_length: 0, offset: 0)
175
181
  read_request = read_packet(read_length: read_length, offset: offset)
176
- raw_response = tree.client.send_recv(read_request)
182
+ raw_response = tree.client.send_recv(read_request, encrypt: @encryption_required)
177
183
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
178
184
  unless response.valid?
179
185
  raise RubySMB::Error::InvalidPacket.new(
@@ -183,20 +189,8 @@ module RubySMB
183
189
  received_cmd: response.smb2_header.command
184
190
  )
185
191
  end
186
- if response.status_code == WindowsError::NTStatus::STATUS_PENDING
187
- sleep 1
188
- raw_response = tree.client.dispatcher.recv_packet
189
- response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
190
- unless response.valid?
191
- raise RubySMB::Error::InvalidPacket.new(
192
- expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
193
- expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
194
- received_proto: response.smb2_header.protocol,
195
- received_cmd: response.smb2_header.command
196
- )
197
- end
198
- elsif response.status_code != WindowsError::NTStatus::STATUS_SUCCESS
199
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
192
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
193
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
200
194
  end
201
195
  response.buffer.to_binary_s
202
196
  end
@@ -206,7 +200,7 @@ module RubySMB
206
200
  # @return [WindowsError::ErrorCode] the NTStatus Response code
207
201
  # @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
208
202
  def delete
209
- raw_response = tree.client.send_recv(delete_packet)
203
+ raw_response = tree.client.send_recv(delete_packet, encrypt: @encryption_required)
210
204
  response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
211
205
  unless response.valid?
212
206
  raise RubySMB::Error::InvalidPacket.new(
@@ -256,7 +250,7 @@ module RubySMB
256
250
 
257
251
  while buffer.length > 0 do
258
252
  write_request = write_packet(data: buffer.slice!(0,atomic_write_size), offset: offset)
259
- raw_response = tree.client.send_recv(write_request)
253
+ raw_response = tree.client.send_recv(write_request, encrypt: @encryption_required)
260
254
  response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
261
255
  unless response.valid?
262
256
  raise RubySMB::Error::InvalidPacket.new(
@@ -286,10 +280,10 @@ module RubySMB
286
280
  write_request.buffer = data
287
281
  write_request
288
282
  end
289
-
283
+
290
284
  def send_recv_write(data:'', offset: 0)
291
285
  pkt = write_packet(data: data, offset: offset)
292
- raw_response = tree.client.send_recv(pkt)
286
+ raw_response = tree.client.send_recv(pkt, encrypt: @encryption_required)
293
287
  response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
294
288
  unless response.valid?
295
289
  raise RubySMB::Error::InvalidPacket.new(
@@ -299,31 +293,19 @@ module RubySMB
299
293
  received_cmd: response.smb2_header.command
300
294
  )
301
295
  end
302
- if response.status_code == WindowsError::NTStatus::STATUS_PENDING
303
- sleep 1
304
- raw_response = tree.client.dispatcher.recv_packet
305
- response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
306
- unless response.valid?
307
- raise RubySMB::Error::InvalidPacket.new(
308
- expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
309
- expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
310
- received_proto: response.smb2_header.protocol,
311
- received_cmd: response.smb2_header.command
312
- )
313
- end
314
- elsif response.status_code != WindowsError::NTStatus::STATUS_SUCCESS
315
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
296
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
297
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
316
298
  end
317
299
  response.write_count
318
300
  end
319
-
301
+
320
302
  # Rename a file
321
303
  #
322
304
  # @param new_file_name [String] the new name
323
305
  # @return [WindowsError::ErrorCode] the NTStatus Response code
324
306
  # @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
325
307
  def rename(new_file_name)
326
- raw_response = tree.client.send_recv(rename_packet(new_file_name))
308
+ raw_response = tree.client.send_recv(rename_packet(new_file_name), encrypt: @encryption_required)
327
309
  response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
328
310
  unless response.valid?
329
311
  raise RubySMB::Error::InvalidPacket.new(
@@ -0,0 +1,108 @@
1
+ module RubySMB
2
+ module SMB2
3
+
4
+ # An SMB2 PREAUTH_INTEGRITY_CAPABILITIES context struct as defined in
5
+ # [2.2.3.1.1 SMB2_PREAUTH_INTEGRITY_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5a07bd66-4734-4af8-abcf-5a44ff7ee0e5)
6
+ class PreauthIntegrityCapabilities < BinData::Record
7
+ SHA_512 = 0x0001
8
+ HASH_ALGORITM_MAP = {
9
+ SHA_512 => 'SHA512'
10
+ }
11
+
12
+ endian :little
13
+
14
+ uint16 :hash_algorithm_count, label: 'Hash Algorithm Count', initial_value: -> { hash_algorithms.size }
15
+ uint16 :salt_length, label: 'Salt Length', initial_value: -> { salt.num_bytes }
16
+ array :hash_algorithms, label: 'Hash Algorithms', type: :uint16, initial_length: -> { hash_algorithm_count }
17
+ string :salt, label: 'Salt', read_length: -> { salt_length }
18
+ end
19
+
20
+ # An SMB2 ENCRYPTION_CAPABILITIES context struct as defined in
21
+ # [2.2.3.1.2 SMB2_ENCRYPTION_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/16693be7-2b27-4d3b-804b-f605bde5bcdd)
22
+ class EncryptionCapabilities < BinData::Record
23
+ AES_128_CCM = 0x0001
24
+ AES_128_GCM = 0x0002
25
+ ENCRYPTION_ALGORITHM_MAP = {
26
+ AES_128_CCM => 'AES-128-CCM',
27
+ AES_128_GCM => 'AES-128-GCM'
28
+ }
29
+
30
+ endian :little
31
+
32
+ uint16 :cipher_count, label: 'Cipher Count', initial_value: -> { ciphers.size }
33
+ array :ciphers, label: 'Ciphers', type: :uint16, initial_length: -> { cipher_count }
34
+ end
35
+
36
+ # An SMB2 COMPRESSION_CAPABILITIES context struct as defined in
37
+ # [2.2.3.1.3 SMB2_COMPRESSION_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/78e0c942-ab41-472b-b117-4a95ebe88271)
38
+ class CompressionCapabilities < BinData::Record
39
+ # Flags
40
+ # Chained compression is not supported.
41
+ SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE = 0x00000000
42
+ # Chained compression is supported on this connection.
43
+ SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED = 0x00000001
44
+
45
+ # Compression Algorithms
46
+ NONE = 0x0000
47
+ LZNT1 = 0x0001
48
+ LZ77 = 0x0002
49
+ LZ77_Huffman = 0x0003
50
+ Pattern_V1 = 0x0004
51
+ COMPRESSION_ALGORITHM_MAP = {
52
+ NONE => 'NONE',
53
+ LZNT1 => 'LZNT1',
54
+ LZ77 => 'LZ77',
55
+ LZ77_Huffman => 'LZ77_Huffman',
56
+ Pattern_V1 => 'Pattern_V1'
57
+ }
58
+
59
+ endian :little
60
+
61
+ uint16 :compression_algorithm_count, label: 'Compression Algorithm Count', initial_value: -> { compression_algorithms.size }
62
+ uint16 :padding, label: 'Padding', initial_value: 0
63
+ uint32 :flags, label: 'Flags'
64
+ array :compression_algorithms, label: 'Compression Algorithms', type: :uint16, initial_length: -> { compression_algorithm_count }
65
+ end
66
+
67
+ # An SMB2 NETNAME_NEGOTIATE_CONTEXT_ID context struct as defined in
68
+ # [2.2.3.1.4 SMB2_NETNAME_NEGOTIATE_CONTEXT_ID](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/ca6726bd-b9cf-43d9-b0bc-d127d3c993b3)
69
+ class NetnameNegotiateContextId < BinData::Record
70
+ endian :little
71
+
72
+ stringz16 :net_name, label: 'Net Name'
73
+ end
74
+
75
+
76
+ # An SMB2 NEGOTIATE_CONTEXT struct as defined in
77
+ # [2.2.3.1 SMB2 NEGOTIATE_CONTEXT Request Values](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/15332256-522e-4a53-8cd7-0bd17678a2f7)
78
+ class NegotiateContext < BinData::Record
79
+ # The NegotiateContext Data field contains a list of preauthentication integrity hash functions as well as an optional salt value, as specified in section 2.2.3.1.1.
80
+ SMB2_PREAUTH_INTEGRITY_CAPABILITIES = 0x0001
81
+ # The NegotiateContext Data field contains a list of encryption algorithms, as specified in section 2.2.3.1.2.
82
+ SMB2_ENCRYPTION_CAPABILITIES = 0x0002
83
+ # The NegotiateContext Data field contains a list of compression algorithms, as specified in section 2.2.3.1.3.
84
+ SMB2_COMPRESSION_CAPABILITIES = 0x0003
85
+ # The NegotiateContext Data field contains the server name to which the client connects.
86
+ SMB2_NETNAME_NEGOTIATE_CONTEXT_ID = 0x0005
87
+
88
+ endian :little
89
+
90
+ string :pad, label: 'Padding', length: -> { pad_length }
91
+ uint16 :context_type, label: 'Context Type'
92
+ uint16 :data_length, label: 'Data Length', initial_value: -> { data.num_bytes }
93
+ uint32 :reserved, label: 'Reserved', initial_value: 0
94
+ choice :data, label: 'Data', selection: -> { context_type } do
95
+ preauth_integrity_capabilities SMB2_PREAUTH_INTEGRITY_CAPABILITIES, label: 'Preauthentication Integrity Capabilities'
96
+ encryption_capabilities SMB2_ENCRYPTION_CAPABILITIES, label: 'Encryption Capabilities'
97
+ compression_capabilities SMB2_COMPRESSION_CAPABILITIES, label: 'Compression Capabilities'
98
+ netname_negotiate_context_id SMB2_NETNAME_NEGOTIATE_CONTEXT_ID, label: 'Netname Negotiate Context ID'
99
+ end
100
+
101
+ def pad_length
102
+ offset = pad.abs_offset % 8
103
+ (8 - offset) % 8
104
+ end
105
+
106
+ end
107
+ end
108
+ end