ruby_smb 1.0.3 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +3 -2
  5. data/Gemfile +6 -2
  6. data/README.md +35 -47
  7. data/examples/enum_registry_key.rb +28 -0
  8. data/examples/enum_registry_values.rb +30 -0
  9. data/examples/negotiate.rb +51 -8
  10. data/examples/pipes.rb +2 -1
  11. data/examples/read_file_encryption.rb +56 -0
  12. data/examples/read_registry_key_value.rb +32 -0
  13. data/lib/ruby_smb.rb +4 -1
  14. data/lib/ruby_smb/client.rb +233 -22
  15. data/lib/ruby_smb/client/authentication.rb +70 -33
  16. data/lib/ruby_smb/client/echo.rb +20 -2
  17. data/lib/ruby_smb/client/encryption.rb +62 -0
  18. data/lib/ruby_smb/client/negotiation.rb +172 -24
  19. data/lib/ruby_smb/client/signing.rb +19 -0
  20. data/lib/ruby_smb/client/tree_connect.rb +24 -18
  21. data/lib/ruby_smb/client/utils.rb +8 -7
  22. data/lib/ruby_smb/client/winreg.rb +46 -0
  23. data/lib/ruby_smb/crypto.rb +30 -0
  24. data/lib/ruby_smb/dcerpc.rb +38 -0
  25. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  26. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  27. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  28. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  29. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  30. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  31. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  32. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  33. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  34. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  35. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  36. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  37. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  38. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  39. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  40. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  41. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  42. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  43. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  44. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  45. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  46. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  47. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  48. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  49. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  50. data/lib/ruby_smb/dispatcher/socket.rb +4 -3
  51. data/lib/ruby_smb/error.rb +68 -2
  52. data/lib/ruby_smb/generic_packet.rb +33 -4
  53. data/lib/ruby_smb/smb1/commands.rb +1 -1
  54. data/lib/ruby_smb/smb1/file.rb +66 -15
  55. data/lib/ruby_smb/smb1/packet/close_request.rb +2 -5
  56. data/lib/ruby_smb/smb1/packet/close_response.rb +2 -1
  57. data/lib/ruby_smb/smb1/packet/echo_request.rb +2 -4
  58. data/lib/ruby_smb/smb1/packet/echo_response.rb +2 -1
  59. data/lib/ruby_smb/smb1/packet/empty_packet.rb +10 -1
  60. data/lib/ruby_smb/smb1/packet/logoff_request.rb +2 -4
  61. data/lib/ruby_smb/smb1/packet/logoff_response.rb +2 -1
  62. data/lib/ruby_smb/smb1/packet/negotiate_request.rb +2 -5
  63. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +3 -7
  64. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +4 -4
  65. data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +2 -4
  66. data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +2 -1
  67. data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +2 -1
  68. data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +2 -1
  69. data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +2 -4
  70. data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +2 -1
  71. data/lib/ruby_smb/smb1/packet/read_andx_request.rb +2 -5
  72. data/lib/ruby_smb/smb1/packet/read_andx_response.rb +2 -1
  73. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -1
  74. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +3 -2
  75. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
  76. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +3 -2
  77. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_request.rb +0 -1
  78. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_response.rb +3 -2
  79. data/lib/ruby_smb/smb1/packet/trans/request.rb +2 -5
  80. data/lib/ruby_smb/smb1/packet/trans/response.rb +2 -1
  81. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +1 -1
  82. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +1 -1
  83. data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +2 -1
  84. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +8 -2
  85. data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +2 -1
  86. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +8 -2
  87. data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +2 -1
  88. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +2 -1
  89. data/lib/ruby_smb/smb1/packet/trans2/request.rb +2 -4
  90. data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +2 -4
  91. data/lib/ruby_smb/smb1/packet/trans2/response.rb +2 -1
  92. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +2 -1
  93. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +2 -1
  94. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +2 -4
  95. data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +13 -3
  96. data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
  97. data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -1
  98. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +3 -6
  99. data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
  100. data/lib/ruby_smb/smb1/pipe.rb +87 -6
  101. data/lib/ruby_smb/smb1/tree.rb +50 -3
  102. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  103. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  104. data/lib/ruby_smb/smb2/file.rb +103 -25
  105. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  106. data/lib/ruby_smb/smb2/packet.rb +2 -0
  107. data/lib/ruby_smb/smb2/packet/close_request.rb +2 -4
  108. data/lib/ruby_smb/smb2/packet/close_response.rb +2 -1
  109. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  110. data/lib/ruby_smb/smb2/packet/create_request.rb +2 -4
  111. data/lib/ruby_smb/smb2/packet/create_response.rb +2 -1
  112. data/lib/ruby_smb/smb2/packet/echo_request.rb +2 -4
  113. data/lib/ruby_smb/smb2/packet/echo_response.rb +2 -1
  114. data/lib/ruby_smb/smb2/packet/error_packet.rb +15 -3
  115. data/lib/ruby_smb/smb2/packet/ioctl_request.rb +2 -5
  116. data/lib/ruby_smb/smb2/packet/ioctl_response.rb +2 -1
  117. data/lib/ruby_smb/smb2/packet/logoff_request.rb +2 -4
  118. data/lib/ruby_smb/smb2/packet/logoff_response.rb +2 -1
  119. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -17
  120. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +52 -5
  121. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +2 -4
  122. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +8 -2
  123. data/lib/ruby_smb/smb2/packet/read_request.rb +2 -4
  124. data/lib/ruby_smb/smb2/packet/read_response.rb +2 -1
  125. data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
  126. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -1
  127. data/lib/ruby_smb/smb2/packet/set_info_request.rb +2 -4
  128. data/lib/ruby_smb/smb2/packet/set_info_response.rb +2 -1
  129. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  130. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +93 -10
  131. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +10 -22
  132. data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +2 -4
  133. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +2 -1
  134. data/lib/ruby_smb/smb2/packet/write_request.rb +2 -4
  135. data/lib/ruby_smb/smb2/packet/write_response.rb +2 -1
  136. data/lib/ruby_smb/smb2/pipe.rb +86 -12
  137. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  138. data/lib/ruby_smb/smb2/tree.rb +65 -21
  139. data/lib/ruby_smb/version.rb +1 -1
  140. data/ruby_smb.gemspec +5 -3
  141. data/spec/lib/ruby_smb/client_spec.rb +1612 -108
  142. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  143. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  144. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  145. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  146. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  147. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  148. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  149. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  150. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  151. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  152. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  153. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  154. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  155. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  156. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  157. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  158. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  159. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  160. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  161. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  162. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  163. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  164. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  165. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  166. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  167. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +2 -2
  168. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  169. data/spec/lib/ruby_smb/generic_packet_spec.rb +52 -4
  170. data/spec/lib/ruby_smb/smb1/file_spec.rb +191 -2
  171. data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +68 -0
  172. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  173. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  174. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  175. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  176. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +11 -2
  177. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +11 -2
  178. data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +40 -0
  179. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +272 -149
  180. data/spec/lib/ruby_smb/smb1/tree_spec.rb +44 -7
  181. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  182. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  183. data/spec/lib/ruby_smb/smb2/file_spec.rb +323 -6
  184. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  185. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  186. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +78 -0
  187. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  188. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  189. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +8 -0
  190. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  191. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  192. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -22
  193. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +286 -149
  194. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  195. data/spec/lib/ruby_smb/smb2/tree_spec.rb +261 -2
  196. metadata +191 -83
  197. metadata.gz.sig +0 -0
  198. data/lib/ruby_smb/smb1/dcerpc.rb +0 -67
  199. data/lib/ruby_smb/smb2/dcerpc.rb +0 -70
  200. data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +0 -37
@@ -8,6 +8,7 @@ RSpec.describe RubySMB::SMB2::Packet::NegotiateRequest do
8
8
  it { is_expected.to respond_to :reserved1 }
9
9
  it { is_expected.to respond_to :capabilities }
10
10
  it { is_expected.to respond_to :client_guid }
11
+ it { is_expected.to respond_to :negotiate_context_info}
11
12
  it { is_expected.to respond_to :client_start_time }
12
13
  it { is_expected.to respond_to :dialects }
13
14
 
@@ -45,6 +46,11 @@ RSpec.describe RubySMB::SMB2::Packet::NegotiateRequest do
45
46
  it 'should be a 16-bit unsigned integer' do
46
47
  expect(packet.dialect_count).to be_a BinData::Uint16le
47
48
  end
49
+
50
+ it 'is initially set to the #dialects array size' do
51
+ packet.dialects = [1,2,3]
52
+ expect(packet.dialect_count).to eq(3)
53
+ end
48
54
  end
49
55
 
50
56
  describe '#security_mode' do
@@ -69,7 +75,65 @@ RSpec.describe RubySMB::SMB2::Packet::NegotiateRequest do
69
75
  end
70
76
  end
71
77
 
78
+ describe '#negotiate_context_info' do
79
+ it 'only exists if the 0x0311 dialect is included' do
80
+ packet.dialects << 0x0311
81
+ expect(packet.negotiate_context_info?).to be true
82
+ end
83
+
84
+ it 'does not exist if the 0x0311 dialect is not included' do
85
+ packet.dialects << 0x0300
86
+ expect(packet.negotiate_context_info?).to be false
87
+ end
88
+
89
+ it 'should be a Struct field' do
90
+ expect(packet.negotiate_context_info).to be_a BinData::Struct
91
+ end
92
+
93
+ subject(:struct) do
94
+ packet.dialects << 0x0311
95
+ packet.negotiate_context_info
96
+ end
97
+ it { is_expected.to respond_to :negotiate_context_offset }
98
+ it { is_expected.to respond_to :negotiate_context_count }
99
+
100
+ describe '#negotiate_context_offset' do
101
+ it 'should be a 32-bit unsigned integer' do
102
+ expect(struct.negotiate_context_offset).to be_a BinData::Uint32le
103
+ end
104
+
105
+ it 'is set to the #negotiate_context_list absolute offset' do
106
+ expect(struct.negotiate_context_offset).to eq(packet.negotiate_context_list.abs_offset)
107
+ end
108
+ end
109
+
110
+ describe '#negotiate_context_count' do
111
+ it 'should be a 16-bit unsigned integer' do
112
+ expect(struct.negotiate_context_count).to be_a BinData::Uint16le
113
+ end
114
+
115
+ it 'is set to the #negotiate_context_list array size' do
116
+ nc = RubySMB::SMB2::NegotiateContext.new(
117
+ context_type: RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES
118
+ )
119
+ packet.negotiate_context_list << nc
120
+ packet.negotiate_context_list << nc
121
+ expect(struct.negotiate_context_count).to eq(2)
122
+ end
123
+ end
124
+ end
125
+
72
126
  describe '#client_start_time' do
127
+ it 'does not exist if the 0x0311 dialect is included' do
128
+ packet.dialects << 0x0311
129
+ expect(packet.client_start_time?).to be false
130
+ end
131
+
132
+ it 'only exists if the 0x0311 dialect is not included' do
133
+ packet.dialects << 0x0300
134
+ expect(packet.client_start_time?).to be true
135
+ end
136
+
73
137
  it 'should be a Filetime field' do
74
138
  expect(packet.client_start_time).to be_a RubySMB::Field::FileTime
75
139
  end
@@ -85,15 +149,51 @@ RSpec.describe RubySMB::SMB2::Packet::NegotiateRequest do
85
149
  end
86
150
  end
87
151
 
152
+ describe '#pad' do
153
+ it 'only exists if the 0x0311 dialect is included' do
154
+ packet.dialects << 0x0311
155
+ expect(packet.pad?).to be true
156
+ end
157
+
158
+ it 'does not exist if the 0x0311 dialect is not included' do
159
+ packet.dialects << 0x0300
160
+ expect(packet.pad?).to be false
161
+ end
162
+
163
+ it 'should be a binary string' do
164
+ expect(packet.pad).to be_a BinData::String
165
+ end
166
+
167
+ it 'should keep #negotiate_context_list 8-byte aligned' do
168
+ packet.dialects << 0x0311
169
+ expect(packet.negotiate_context_list.abs_offset % 8).to eq 0
170
+ end
171
+ end
172
+
173
+ describe '#negotiate_context_list' do
174
+ it 'only exists if the 0x0311 dialect is included' do
175
+ packet.dialects << 0x0311
176
+ expect(packet.negotiate_context_list?).to be true
177
+ end
178
+
179
+ it 'does not exist if the 0x0311 dialect is not included' do
180
+ packet.dialects << 0x0300
181
+ expect(packet.negotiate_context_list?).to be false
182
+ end
183
+
184
+ it 'is an array field as per the SMB spec' do
185
+ expect(packet.negotiate_context_list).to be_a BinData::Array
186
+ end
187
+ end
188
+
88
189
  describe '#add_dialect' do
89
190
  it 'adds the dialect to the Dialects array' do
90
191
  packet.add_dialect 0x0201
91
192
  expect(packet.dialects).to include(0x0201)
92
193
  end
93
194
 
94
- it 'updates the #dialect_count field' do
95
- packet.add_dialect 0x0201
96
- expect(packet.dialect_count).to eq 1
195
+ it 'raises an ArgumentError exceptionif it is not an Integer' do
196
+ expect { packet.add_dialect('dialect') }.to raise_error(ArgumentError)
97
197
  end
98
198
  end
99
199
 
@@ -119,4 +219,39 @@ RSpec.describe RubySMB::SMB2::Packet::NegotiateRequest do
119
219
  expect(packet.dialect_count).to eq 3
120
220
  end
121
221
  end
222
+
223
+ describe '#add_negotiate_context' do
224
+ it 'raises an ArgumentError exceptionif it is not a NegotiateContext structure' do
225
+ expect { packet.add_negotiate_context('nc') }.to raise_error(ArgumentError)
226
+ end
227
+
228
+ it 'updates the NegotiateContext#pad length to make sure the structure is 8-byte aligned' do
229
+ packet.dialects << 0x0311
230
+ [
231
+ RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES,
232
+ RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES,
233
+ RubySMB::SMB2::NegotiateContext::SMB2_COMPRESSION_CAPABILITIES,
234
+ RubySMB::SMB2::NegotiateContext::SMB2_NETNAME_NEGOTIATE_CONTEXT_ID
235
+ ].each do |context_type|
236
+ nc = RubySMB::SMB2::NegotiateContext.new(context_type: context_type)
237
+ packet.add_negotiate_context(nc)
238
+ expect(packet.negotiate_context_list.last.context_type.abs_offset % 8).to eq 0
239
+ end
240
+ end
241
+ end
242
+
243
+ it 'reads binary data as expected' do
244
+ data = described_class.new
245
+ data.set_dialects([0x0202, 0x0210, 0x0311])
246
+ [
247
+ RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES,
248
+ RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES,
249
+ RubySMB::SMB2::NegotiateContext::SMB2_COMPRESSION_CAPABILITIES,
250
+ RubySMB::SMB2::NegotiateContext::SMB2_NETNAME_NEGOTIATE_CONTEXT_ID
251
+ ].each do |context_type|
252
+ nc = RubySMB::SMB2::NegotiateContext.new(context_type: context_type)
253
+ data.add_negotiate_context(nc)
254
+ expect(described_class.read(data.to_binary_s)).to eq(data)
255
+ end
256
+ end
122
257
  end
@@ -61,12 +61,28 @@ RSpec.describe RubySMB::SMB2::Packet::NegotiateResponse do
61
61
  end
62
62
 
63
63
  describe '#negotiate_context_count' do
64
+ it 'only exists if the 0x0311 dialect is included' do
65
+ packet.dialect_revision = 0x0311
66
+ expect(packet.negotiate_context_count?).to be true
67
+ end
68
+
69
+ it 'does not exist if the 0x0311 dialect is not included' do
70
+ packet.dialect_revision = 0x0300
71
+ expect(packet.negotiate_context_count?).to be false
72
+ end
73
+
64
74
  it 'is a 16-bit unsigned integer' do
65
75
  expect(packet.negotiate_context_count).to be_a BinData::Uint16le
66
76
  end
67
77
 
68
- it 'has a default value of 0' do
69
- expect(packet.negotiate_context_count).to eq 0
78
+ it 'is set to the #negotiate_context_list array size' do
79
+ packet.dialect_revision = 0x0311
80
+ nc = RubySMB::SMB2::NegotiateContext.new(
81
+ context_type: RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES
82
+ )
83
+ packet.negotiate_context_list << nc
84
+ packet.negotiate_context_list << nc
85
+ expect(packet.negotiate_context_count).to eq(2)
70
86
  end
71
87
  end
72
88
 
@@ -134,6 +150,16 @@ RSpec.describe RubySMB::SMB2::Packet::NegotiateResponse do
134
150
  end
135
151
 
136
152
  describe '#negotiate_context_offset' do
153
+ it 'only exists if the 0x0311 dialect is included' do
154
+ packet.dialect_revision = 0x0311
155
+ expect(packet.negotiate_context_offset?).to be true
156
+ end
157
+
158
+ it 'does not exist if the 0x0311 dialect is not included' do
159
+ packet.dialect_revision = 0x0300
160
+ expect(packet.negotiate_context_offset?).to be false
161
+ end
162
+
137
163
  it 'is a 32-bit unsigned integer' do
138
164
  expect(packet.negotiate_context_offset).to be_a BinData::Uint32le
139
165
  end
@@ -144,4 +170,96 @@ RSpec.describe RubySMB::SMB2::Packet::NegotiateResponse do
144
170
  expect(packet.security_buffer).to be_a BinData::String
145
171
  end
146
172
  end
173
+
174
+ describe '#pad' do
175
+ it 'only exists if the 0x0311 dialect is included' do
176
+ packet.dialect_revision = 0x0311
177
+ expect(packet.pad?).to be true
178
+ end
179
+
180
+ it 'does not exist if the 0x0311 dialect is not included' do
181
+ packet.dialect_revision = 0x0300
182
+ expect(packet.pad?).to be false
183
+ end
184
+
185
+ it 'should be a binary string' do
186
+ expect(packet.pad).to be_a BinData::String
187
+ end
188
+
189
+ it 'should keep #negotiate_context_list 8-byte aligned' do
190
+ packet.dialect_revision = 0x0311
191
+ expect(packet.negotiate_context_list.abs_offset % 8).to eq 0
192
+ end
193
+ end
194
+
195
+ describe '#negotiate_context_list' do
196
+ it 'only exists if the 0x0311 dialect is included' do
197
+ packet.dialect_revision = 0x0311
198
+ expect(packet.negotiate_context_list?).to be true
199
+ end
200
+
201
+ it 'does not exist if the 0x0311 dialect is not included' do
202
+ packet.dialect_revision = 0x0300
203
+ expect(packet.negotiate_context_list?).to be false
204
+ end
205
+
206
+ it 'is an array field as per the SMB spec' do
207
+ expect(packet.negotiate_context_list).to be_a BinData::Array
208
+ end
209
+ end
210
+
211
+ describe '#find_negotiate_context' do
212
+ before :example do
213
+ packet.add_negotiate_context(
214
+ RubySMB::SMB2::NegotiateContext.new(context_type: RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES)
215
+ )
216
+ packet.add_negotiate_context(
217
+ RubySMB::SMB2::NegotiateContext.new(context_type: RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES)
218
+ )
219
+ end
220
+
221
+ it 'returns the expected Negotiate Context structure' do
222
+ expect(packet.find_negotiate_context(RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES)).to eq(packet.negotiate_context_list[1])
223
+ end
224
+
225
+ it 'returns nil if the Negotiate Context structure is not found' do
226
+ expect(packet.find_negotiate_context(10)).to be nil
227
+ end
228
+ end
229
+
230
+ describe '#add_negotiate_context' do
231
+ it 'raises an ArgumentError exception if it is not a NegotiateContext structure' do
232
+ expect { packet.add_negotiate_context('nc') }.to raise_error(ArgumentError)
233
+ end
234
+
235
+ it 'updates the NegotiateContext#pad length to make sure the structure is 8-byte aligned' do
236
+ packet.dialect_revision = 0x0311
237
+ [
238
+ RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES,
239
+ RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES,
240
+ RubySMB::SMB2::NegotiateContext::SMB2_COMPRESSION_CAPABILITIES,
241
+ RubySMB::SMB2::NegotiateContext::SMB2_NETNAME_NEGOTIATE_CONTEXT_ID
242
+ ].each do |context_type|
243
+ nc = RubySMB::SMB2::NegotiateContext.new(context_type: context_type)
244
+ packet.add_negotiate_context(nc)
245
+ expect(packet.negotiate_context_list.last.context_type.abs_offset % 8).to eq 0
246
+ end
247
+ end
248
+ end
249
+
250
+ it 'reads binary data as expected' do
251
+ data = described_class.new
252
+ data.dialect_revision = 0x0311
253
+ data.security_buffer = 'security buf test'
254
+ [
255
+ RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES,
256
+ RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES,
257
+ RubySMB::SMB2::NegotiateContext::SMB2_COMPRESSION_CAPABILITIES,
258
+ RubySMB::SMB2::NegotiateContext::SMB2_NETNAME_NEGOTIATE_CONTEXT_ID
259
+ ].each do |context_type|
260
+ nc = RubySMB::SMB2::NegotiateContext.new(context_type: context_type)
261
+ data.add_negotiate_context(nc)
262
+ expect(described_class.read(data.to_binary_s)).to eq(data)
263
+ end
264
+ end
147
265
  end
@@ -60,5 +60,13 @@ RSpec.describe RubySMB::SMB2::Packet::QueryDirectoryResponse do
60
60
  packet.buffer = names_blob
61
61
  expect(packet.results(RubySMB::Fscc::FileInformation::FileNamesInformation)).to eq names_array
62
62
  end
63
+
64
+ context 'when the File Information is not a valid' do
65
+ it 'raises an InvalidPacket exception' do
66
+ packet.buffer = names_blob
67
+ allow(RubySMB::Fscc::FileInformation::FileNamesInformation).to receive(:read).and_raise(IOError)
68
+ expect { packet.results(RubySMB::Fscc::FileInformation::FileNamesInformation) }.to raise_error(RubySMB::Error::InvalidPacket)
69
+ end
70
+ end
63
71
  end
64
72
  end
@@ -0,0 +1,220 @@
1
+ RSpec.describe RubySMB::SMB2::Packet::TransformHeader do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :protocol }
5
+ it { is_expected.to respond_to :signature }
6
+ it { is_expected.to respond_to :nonce }
7
+ it { is_expected.to respond_to :original_message_size }
8
+ it { is_expected.to respond_to :flags }
9
+ it { is_expected.to respond_to :session_id }
10
+ it { is_expected.to respond_to :encrypted_data }
11
+
12
+ it 'is little endian' do
13
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
14
+ end
15
+
16
+ describe '#protocol' do
17
+ it 'is a 32-bit field' do
18
+ expect(packet.protocol).to be_a BinData::Bit32
19
+ end
20
+
21
+ it 'has an initial value of 0xFD534D42' do
22
+ expect(packet.protocol).to eq(0xFD534D42)
23
+ end
24
+ end
25
+
26
+ describe '#signature' do
27
+ it 'is a String' do
28
+ expect(packet.signature).to be_a BinData::String
29
+ end
30
+ end
31
+
32
+ describe '#nonce' do
33
+ it 'is a String' do
34
+ expect(packet.nonce).to be_a BinData::String
35
+ end
36
+ end
37
+
38
+ describe '#original_message_size ' do
39
+ it 'is a 32-bit unsigned integer' do
40
+ expect(packet.original_message_size).to be_a BinData::Uint32le
41
+ end
42
+ end
43
+
44
+ describe '#flags' do
45
+ it 'is a 16-bit unsigned integer' do
46
+ expect(packet.flags).to be_a BinData::Uint16le
47
+ end
48
+ end
49
+
50
+ describe '#session_id' do
51
+ it 'is a 64-bit unsigned integer' do
52
+ expect(packet.session_id).to be_a BinData::Uint64le
53
+ end
54
+ end
55
+
56
+ describe '#encrypted_data' do
57
+ it 'is an Array' do
58
+ expect(packet.encrypted_data).to be_a BinData::Array
59
+ end
60
+ end
61
+
62
+ describe '#decrypt' do
63
+ let(:key) { "\x56\x89\xd1\xbb\xf7\x45\xc0\xb6\x68\x81\x07\xe4\x7d\x35\xaf\xd3".b }
64
+ let(:data) { 'data'.b }
65
+ before :example do
66
+ packet.original_message_size = data.length
67
+ end
68
+
69
+ it 'raises the expected exception if the given algorithm is invalid' do
70
+ expect { packet.decrypt(key, algorithm: 'RC4') }.to raise_error(
71
+ RubySMB::Error::EncryptionError,
72
+ 'Error while decrypting with \'RC4\' (ArgumentError: Invalid algorithm, must be either AES-128-CCM or AES-128-GCM)'
73
+ )
74
+ end
75
+
76
+ context 'with AES-128-GCM algorithm (default)' do
77
+ before :example do
78
+ begin
79
+ OpenSSL::Cipher.new('AES-128-GCM')
80
+ rescue
81
+ skip(
82
+ "This test cannot be run since the version of OpenSSL the ruby "\
83
+ "OpenSSL extension was built with (#{OpenSSL::OPENSSL_VERSION}) "\
84
+ "does not support AES-128-GCM cipher")
85
+ end
86
+ packet.encrypted_data = "\x06\x45\x16\x36".bytes
87
+ packet.signature = "\x63\xb2\xf9\xe0\xb7\x43\xdb\xaf\x26\x8e\xd7\x42\xd3\xb2\xde\x0d"
88
+ packet.nonce = "\xe1\xb0\xa7\x20\xd9\xd9\x69\x3c\x79\xd0\x9c\x53\x00\x00\x00\x00"
89
+ end
90
+
91
+ it 'generates a cipher using OpenSSL::Cipher' do
92
+ expect(OpenSSL::Cipher).to receive(:new).with('AES-128-GCM').and_call_original
93
+ packet.decrypt(key)
94
+ end
95
+
96
+ it 'returns the expected decrypted string' do
97
+ expect(packet.decrypt(key)).to eq(data)
98
+ end
99
+
100
+ it 'raises the expected exception if an error occurs' do
101
+ allow(OpenSSL::Cipher).to receive(:new).and_raise(
102
+ RuntimeError.new('unsupported cipher algorithm (AES-128-GCM)'))
103
+ expect { packet.decrypt(key) }.to raise_error(
104
+ RubySMB::Error::EncryptionError,
105
+ 'Error while decrypting with \'AES-128-GCM\' (RuntimeError: unsupported cipher algorithm (AES-128-GCM))'
106
+ )
107
+ end
108
+ end
109
+
110
+ context 'with AES-128-CCM algorithm' do
111
+ before :example do
112
+ packet.encrypted_data = "\xf0\x05\x61\x91".bytes
113
+ packet.signature = "\xdd\x51\x9a\xc5\x6d\x38\x68\xdc\x36\x89\xb8\x99\xd8\x4a\xb8\x4a".b
114
+ packet.nonce = "\x8a\x6e\x2a\x87\x11\x61\x85\xd2\x15\x69\xf7\x00\x00\x00\x00\x00".b
115
+ end
116
+
117
+ it 'generates a cipher using OpenSSL::CCM' do
118
+ expect(OpenSSL::CCM).to receive(:new).with('AES', key, 16).and_call_original
119
+ packet.decrypt(key, algorithm: 'AES-128-CCM')
120
+ end
121
+
122
+ it 'returns the expected decrypted string' do
123
+ expect(packet.decrypt(key, algorithm: 'AES-128-CCM')).to eq(data)
124
+ end
125
+
126
+ it 'raises the expected exception if an error occurs' do
127
+ allow(OpenSSL::CCM).to receive(:new).and_raise(
128
+ OpenSSL::CCMError.new('unsupported cipher algorithm (AES-128-CCM)'))
129
+ expect { packet.decrypt(key, algorithm: 'AES-128-CCM') }.to raise_error(
130
+ RubySMB::Error::EncryptionError,
131
+ 'Error while decrypting with \'AES-128-CCM\' (OpenSSL::CCMError: unsupported cipher algorithm (AES-128-CCM))'
132
+ )
133
+ end
134
+ end
135
+ end
136
+
137
+ describe '#encrypt' do
138
+ let(:key) { "\x56\x89\xd1\xbb\xf7\x45\xc0\xb6\x68\x81\x07\xe4\x7d\x35\xaf\xd3".b }
139
+ let(:struct) { RubySMB::SMB2::Packet::TreeConnectRequest.new }
140
+
141
+ it 'raises the expected exception if the given algorithm is invalid' do
142
+ expect { packet.encrypt(struct, key, algorithm: 'RC4') }.to raise_error(
143
+ RubySMB::Error::EncryptionError,
144
+ 'Error while encrypting with \'RC4\' (ArgumentError: Invalid algorithm, must be either AES-128-CCM or AES-128-GCM)'
145
+ )
146
+ end
147
+
148
+ context 'with AES-128-GCM algorithm (default)' do
149
+ before :example do
150
+ begin
151
+ OpenSSL::Cipher.new('AES-128-GCM')
152
+ rescue
153
+ skip(
154
+ "This test cannot be run since the version of OpenSSL the ruby "\
155
+ "OpenSSL extension was built with (#{OpenSSL::OPENSSL_VERSION}) "\
156
+ "does not support AES-128-GCM cipher")
157
+ end
158
+ end
159
+
160
+ it 'generates a cipher using OpenSSL::Cipher' do
161
+ expect(OpenSSL::Cipher).to receive(:new).with('AES-128-GCM').and_call_original
162
+ packet.encrypt(struct, key)
163
+ end
164
+
165
+ it 'encrypts a BinData structure' do
166
+ packet.encrypt(struct, key)
167
+ expect(packet.decrypt(key)).to eq(struct.to_binary_s)
168
+ end
169
+
170
+ it 'encrypts a string' do
171
+ packet.encrypt('data', key)
172
+ expect(packet.decrypt(key)).to eq('data')
173
+ end
174
+
175
+ it 'raises the expected exception if an error occurs' do
176
+ allow(OpenSSL::Cipher).to receive(:new).and_raise(
177
+ RuntimeError.new('unsupported cipher algorithm (AES-128-GCM)'))
178
+ expect { packet.encrypt('data', key) }.to raise_error(
179
+ RubySMB::Error::EncryptionError,
180
+ 'Error while encrypting with \'AES-128-GCM\' (RuntimeError: unsupported cipher algorithm (AES-128-GCM))'
181
+ )
182
+ end
183
+ end
184
+
185
+ context 'with AES-128-CCM algorithm' do
186
+ it 'generates a cipher using OpenSSL::CCM' do
187
+ expect(OpenSSL::CCM).to receive(:new).with('AES', key, 16).and_call_original
188
+ packet.encrypt(struct, key, algorithm: 'AES-128-CCM')
189
+ end
190
+
191
+ it 'encrypts a BinData structure' do
192
+ packet.encrypt(struct, key, algorithm: 'AES-128-CCM')
193
+ expect(packet.decrypt(key, algorithm: 'AES-128-CCM')).to eq(struct.to_binary_s)
194
+ end
195
+
196
+ it 'encrypts a string' do
197
+ packet.encrypt('data', key, algorithm: 'AES-128-CCM')
198
+ expect(packet.decrypt(key, algorithm: 'AES-128-CCM')).to eq('data')
199
+ end
200
+
201
+ it 'raises the expected exception if an error occurs' do
202
+ allow(OpenSSL::CCM).to receive(:new).and_raise(
203
+ OpenSSL::CCMError.new('unsupported cipher algorithm (AES-128-CCM)'))
204
+ expect { packet.encrypt('data', key, algorithm: 'AES-128-CCM') }.to raise_error(
205
+ RubySMB::Error::EncryptionError,
206
+ 'Error while encrypting with \'AES-128-CCM\' (OpenSSL::CCMError: unsupported cipher algorithm (AES-128-CCM))'
207
+ )
208
+ end
209
+ end
210
+ end
211
+
212
+ it 'reads binary data as expected' do
213
+ data = described_class.new
214
+ key = "\x56\x89\xd1\xbb\xf7\x45\xc0\xb6\x68\x81\x07\xe4\x7d\x35\xaf\xd3".b
215
+ struct = RubySMB::SMB2::Packet::TreeConnectRequest.new
216
+ data.encrypt(struct, key, algorithm: 'AES-128-CCM')
217
+ expect(described_class.read(data.to_binary_s)).to eq(data)
218
+ end
219
+ end
220
+