ruby_smb 1.0.5 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) 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/anonymous_auth.rb +3 -3
  8. data/examples/append_file.rb +10 -8
  9. data/examples/authenticate.rb +9 -5
  10. data/examples/delete_file.rb +8 -6
  11. data/examples/enum_registry_key.rb +29 -0
  12. data/examples/enum_registry_values.rb +31 -0
  13. data/examples/list_directory.rb +8 -6
  14. data/examples/negotiate.rb +51 -8
  15. data/examples/negotiate_with_netbios_service.rb +9 -5
  16. data/examples/net_share_enum_all.rb +6 -4
  17. data/examples/pipes.rb +13 -13
  18. data/examples/query_service_status.rb +64 -0
  19. data/examples/read_file.rb +8 -6
  20. data/examples/read_file_encryption.rb +56 -0
  21. data/examples/read_registry_key_value.rb +33 -0
  22. data/examples/rename_file.rb +9 -7
  23. data/examples/tree_connect.rb +7 -5
  24. data/examples/write_file.rb +9 -7
  25. data/lib/ruby_smb.rb +4 -1
  26. data/lib/ruby_smb/client.rb +239 -21
  27. data/lib/ruby_smb/client/authentication.rb +27 -8
  28. data/lib/ruby_smb/client/encryption.rb +62 -0
  29. data/lib/ruby_smb/client/negotiation.rb +154 -12
  30. data/lib/ruby_smb/client/signing.rb +19 -0
  31. data/lib/ruby_smb/client/tree_connect.rb +4 -4
  32. data/lib/ruby_smb/client/utils.rb +8 -7
  33. data/lib/ruby_smb/client/winreg.rb +46 -0
  34. data/lib/ruby_smb/crypto.rb +30 -0
  35. data/lib/ruby_smb/dcerpc.rb +40 -0
  36. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  37. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  38. data/lib/ruby_smb/dcerpc/error.rb +6 -0
  39. data/lib/ruby_smb/dcerpc/ndr.rb +260 -16
  40. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  41. data/lib/ruby_smb/dcerpc/request.rb +41 -9
  42. data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
  43. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +38 -0
  44. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  45. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  46. data/lib/ruby_smb/dcerpc/svcctl.rb +479 -0
  47. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
  48. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
  49. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
  50. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
  51. data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
  52. data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
  53. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
  54. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
  55. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
  56. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
  57. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
  58. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
  59. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
  60. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
  61. data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
  62. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
  63. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
  64. data/lib/ruby_smb/dcerpc/winreg.rb +421 -0
  65. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  66. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  67. data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +73 -0
  68. data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
  69. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  70. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  71. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  72. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  73. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  74. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  75. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  76. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  77. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  78. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  79. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +40 -0
  80. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  81. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  82. data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +37 -0
  83. data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
  84. data/lib/ruby_smb/dispatcher/base.rb +1 -1
  85. data/lib/ruby_smb/dispatcher/socket.rb +5 -4
  86. data/lib/ruby_smb/error.rb +28 -1
  87. data/lib/ruby_smb/field/stringz16.rb +17 -1
  88. data/lib/ruby_smb/nbss/session_header.rb +4 -4
  89. data/lib/ruby_smb/smb1/commands.rb +1 -1
  90. data/lib/ruby_smb/smb1/file.rb +8 -14
  91. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
  92. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
  93. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  94. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
  95. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
  96. data/lib/ruby_smb/smb1/pipe.rb +81 -3
  97. data/lib/ruby_smb/smb1/tree.rb +12 -3
  98. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  99. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  100. data/lib/ruby_smb/smb2/file.rb +51 -61
  101. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  102. data/lib/ruby_smb/smb2/packet.rb +2 -0
  103. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  104. data/lib/ruby_smb/smb2/packet/error_packet.rb +2 -4
  105. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
  106. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
  107. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  108. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
  109. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
  110. data/lib/ruby_smb/smb2/pipe.rb +80 -3
  111. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  112. data/lib/ruby_smb/smb2/tree.rb +32 -20
  113. data/lib/ruby_smb/version.rb +1 -1
  114. data/ruby_smb.gemspec +5 -3
  115. data/spec/lib/ruby_smb/client_spec.rb +1583 -102
  116. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  117. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  118. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  119. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1729 -0
  120. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  121. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
  122. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +135 -0
  123. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  124. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  125. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
  126. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
  127. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
  128. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
  129. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
  130. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
  131. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
  132. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
  133. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
  134. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
  135. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
  136. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
  137. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
  138. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
  139. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
  140. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
  141. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
  142. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
  143. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  144. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  145. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
  146. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
  147. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +104 -0
  148. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  149. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  150. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  151. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  152. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  153. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +95 -0
  154. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  155. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +35 -0
  156. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  157. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  158. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +138 -0
  159. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  160. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
  161. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
  162. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +884 -0
  163. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  164. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
  165. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  166. data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
  167. data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
  168. data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
  169. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  170. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  171. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  172. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  173. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +216 -147
  174. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  175. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  176. data/spec/lib/ruby_smb/smb2/file_spec.rb +146 -68
  177. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  178. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  179. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +3 -24
  180. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  181. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  182. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  183. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  184. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
  185. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +226 -148
  186. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  187. data/spec/lib/ruby_smb/smb2/tree_spec.rb +88 -9
  188. metadata +257 -81
  189. metadata.gz.sig +0 -0
  190. data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
  191. data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -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
+
@@ -9,6 +9,7 @@ RSpec.describe RubySMB::SMB2::Packet::TreeConnectRequest do
9
9
  it { is_expected.to respond_to :path_offset }
10
10
  it { is_expected.to respond_to :path_length }
11
11
  it { is_expected.to respond_to :path }
12
+ it { is_expected.to respond_to :tree_connect_request_extension }
12
13
 
13
14
  it 'is little endian' do
14
15
  expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
@@ -30,18 +31,347 @@ RSpec.describe RubySMB::SMB2::Packet::TreeConnectRequest do
30
31
  end
31
32
  end
32
33
 
33
- describe '#encode_path' do
34
- let(:path) { '\\192.168.1.1\\example' }
35
- let(:encoded_path) { path.encode('utf-16le').force_encoding('binary') }
34
+ describe '#structure_size' do
35
+ it 'should be a 16-bit unsigned integer' do
36
+ expect(packet.structure_size).to be_a BinData::Uint16le
37
+ end
38
+
39
+ it 'should have a default value of 9 as per the SMB2 spec' do
40
+ expect(packet.structure_size).to eq 9
41
+ end
42
+ end
43
+
44
+ describe '#flags' do
45
+ it 'should be a 16-bit unsigned integer' do
46
+ expect(packet.flags).to be_a BinData::Uint16le
47
+ end
48
+ end
49
+
50
+ describe '#path_offset' do
51
+ it 'should be a 16-bit unsigned integer' do
52
+ expect(packet.path_offset).to be_a BinData::Uint16le
53
+ end
54
+
55
+ context 'when flags is set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
56
+ it 'should be set to the offset, in bytes, of the full share path name from the beginning of the packet header' do
57
+ packet.flags = described_class::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
58
+ expect(packet.path_offset).to eq(88)
59
+ end
60
+ end
61
+
62
+ context 'when flags is not set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
63
+ it 'should be set to the offset, in bytes, of the full share path name from the beginning of the packet header' do
64
+ expect(packet.path_offset).to eq(72)
65
+ end
66
+ end
67
+ end
68
+
69
+ describe '#path_length' do
70
+ let(:path) { '\\\\server\\path' }
71
+
72
+ it 'should be a 16-bit unsigned integer' do
73
+ expect(packet.path_length).to be_a BinData::Uint16le
74
+ end
75
+
76
+ context 'when flags is set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
77
+ it 'should be the length of the full share path name (unicode) in bytes' do
78
+ packet.flags = described_class::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
79
+ packet.tree_connect_request_extension.path = path
80
+ expect(packet.path_length).to eq(path.length * 2)
81
+ end
82
+ end
83
+
84
+ context 'when flags is not set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
85
+ it 'should be the length of the full share path name (unicode) in bytes' do
86
+ packet.path = path
87
+ expect(packet.path_length).to eq(path.length * 2)
88
+ end
89
+ end
90
+ end
91
+
92
+ describe '#path' do
93
+ it 'should be a unicode string' do
94
+ expect(packet.path).to be_a RubySMB::Field::String16
95
+ end
96
+
97
+ it 'exists if #flags is not set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
98
+ packet.flags = described_class::SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER
99
+ expect(packet.path?).to be true
100
+ end
101
+ end
102
+
103
+ describe '#tree_connect_request_extension' do
104
+ it 'is a TreeConnectRequestExtension structure' do
105
+ expect(packet.tree_connect_request_extension).to be_a RubySMB::SMB2::Packet::TreeConnectRequestExtension
106
+ end
107
+
108
+ it 'exists if #flags is set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
109
+ packet.flags = described_class::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
110
+ expect(packet.tree_connect_request_extension?).to be true
111
+ end
112
+ end
113
+
114
+ it 'reads binary data as expected' do
115
+ data = described_class.new
116
+ expect(described_class.read(data.to_binary_s)).to eq(data)
117
+ data = described_class.new(flags: described_class::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT)
118
+ data.tree_connect_request_extension.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
119
+ expect(described_class.read(data.to_binary_s)).to eq(data)
120
+ end
121
+ end
122
+
123
+ RSpec.describe RubySMB::SMB2::Packet::TreeConnectRequestExtension do
124
+ subject(:packet) { described_class.new }
125
+
126
+ it { is_expected.to respond_to :tree_connect_context_offset }
127
+ it { is_expected.to respond_to :tree_connect_context_count }
128
+ it { is_expected.to respond_to :reserved }
129
+ it { is_expected.to respond_to :path }
130
+ it { is_expected.to respond_to :tree_connect_contexts }
131
+
132
+ it 'is little endian' do
133
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
134
+ end
135
+
136
+ describe '#tree_connect_context_offset' do
137
+ it 'is a 32-bit unsigned integer' do
138
+ expect(packet.tree_connect_context_offset).to be_a BinData::Uint32le
139
+ end
140
+
141
+ it 'is the offset from the start of the SMB2 TREE_CONNECT request of an array of tree connect contexts' do
142
+ tc = RubySMB::SMB2::Packet::TreeConnectRequest.new(
143
+ flags: RubySMB::SMB2::Packet::TreeConnectRequest::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
144
+ )
145
+ expect(tc.tree_connect_request_extension.tree_connect_context_offset).to eq(16)
146
+ end
147
+ end
148
+
149
+ describe '#tree_connect_context_count' do
150
+ it 'should be a 16-bit unsigned integer' do
151
+ expect(packet.tree_connect_context_count).to be_a BinData::Uint16le
152
+ end
153
+
154
+ it 'should be the #tree_connect_contexts size' do
155
+ packet.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
156
+ packet.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
157
+ expect(packet.tree_connect_context_count).to eq(2)
158
+ end
159
+ end
160
+
161
+ describe '#reserved' do
162
+ it 'should be a binary string' do
163
+ expect(packet.reserved).to be_a BinData::String
164
+ end
165
+
166
+ it 'is 10-bytes long' do
167
+ expect(packet.reserved.length).to eq(10)
168
+ end
169
+ end
170
+
171
+ describe '#path' do
172
+ it 'should be a unicode string' do
173
+ expect(packet.path).to be_a RubySMB::Field::String16
174
+ end
175
+ end
176
+
177
+ describe '#tree_connect_contexts' do
178
+ it 'is an Array field' do
179
+ expect(packet.tree_connect_contexts).to be_a BinData::Array
180
+ end
181
+
182
+ it 'has #tree_connect_context_count elements' do
183
+ packet.tree_connect_context_count = 3
184
+ expect(packet.tree_connect_contexts.size).to eq(3)
185
+ end
186
+ end
187
+
188
+ it 'reads binary data as expected' do
189
+ data = described_class.new
190
+ expect(described_class.read(data.to_binary_s)).to eq(data)
191
+ data.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
192
+ data.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
193
+ expect(described_class.read(data.to_binary_s)).to eq(data)
194
+ end
195
+ end
196
+
197
+ RSpec.describe RubySMB::SMB2::Packet::TreeConnectContext do
198
+ subject(:packet) do
199
+ described_class.new(
200
+ context_type: described_class::SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID
201
+ )
202
+ end
203
+
204
+ it { is_expected.to respond_to :context_type }
205
+ it { is_expected.to respond_to :data_length }
206
+ it { is_expected.to respond_to :reserved }
207
+ it { is_expected.to respond_to :data }
208
+
209
+ it 'is little endian' do
210
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
211
+ end
212
+
213
+ describe '#context_type' do
214
+ it 'should be a 16-bit unsigned integer' do
215
+ expect(packet.context_type).to be_a BinData::Uint16le
216
+ end
217
+ end
218
+
219
+ describe '#data_length' do
220
+ it 'should be a 16-bit unsigned integer' do
221
+ expect(packet.data_length).to be_a BinData::Uint16le
222
+ end
223
+
224
+ it 'is the length, in bytes, of the Data field' do
225
+ expect(packet.data_length).to eq(packet.data.to_binary_s.size)
226
+ end
227
+ end
228
+
229
+ describe '#reserved' do
230
+ it 'is a 32-bit unsigned integer' do
231
+ expect(packet.reserved).to be_a BinData::Uint32le
232
+ end
233
+ end
234
+
235
+ describe '#data' do
236
+ it 'is a BinData Choice' do
237
+ expect(packet.data).to be_a BinData::Choice
238
+ end
239
+
240
+ it 'contains the structure defined by #context_type' do
241
+ expect(packet.data).to eq(RubySMB::SMB2::Packet::RemotedIdentityTreeConnectContext.new)
242
+ end
243
+ end
244
+
245
+ it 'reads binary data as expected' do
246
+ data = described_class.new(context_type: 1)
247
+ expect(described_class.read(data.to_binary_s)).to eq(data)
248
+ end
249
+ end
250
+
251
+ RSpec.describe RubySMB::SMB2::Packet::RemotedIdentityTreeConnectContext do
252
+ subject(:packet) { described_class.new }
253
+
254
+ it { is_expected.to respond_to :ticket_type }
255
+ it { is_expected.to respond_to :ticket_size }
256
+ it { is_expected.to respond_to :user }
257
+ it { is_expected.to respond_to :user_name }
258
+ it { is_expected.to respond_to :domain }
259
+ it { is_expected.to respond_to :groups }
260
+ it { is_expected.to respond_to :restricted_groups }
261
+ it { is_expected.to respond_to :privileges }
262
+ it { is_expected.to respond_to :primary_group }
263
+ it { is_expected.to respond_to :owner }
264
+ it { is_expected.to respond_to :default_dacl }
265
+ it { is_expected.to respond_to :device_groups }
266
+ it { is_expected.to respond_to :user_claims }
267
+ it { is_expected.to respond_to :device_claims }
268
+ it { is_expected.to respond_to :ticket_info }
269
+
270
+ it 'is little endian' do
271
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
272
+ end
273
+
274
+ describe '#ticket_type' do
275
+ it 'should be a 16-bit unsigned integer' do
276
+ expect(packet.ticket_type).to be_a BinData::Uint16le
277
+ end
278
+
279
+ it 'should be 1' do
280
+ expect(packet.ticket_type).to eq(1)
281
+ end
282
+ end
283
+
284
+ describe '#ticket_size' do
285
+ it 'should be a 16-bit unsigned integer' do
286
+ expect(packet.ticket_size).to be_a BinData::Uint16le
287
+ end
288
+
289
+ it 'is the total size of this structure' do
290
+ packet.ticket_info = 'Ticket Info'
291
+ expect(packet.ticket_size).to eq(packet.num_bytes)
292
+ end
293
+ end
294
+
295
+ describe '#user' do
296
+ it 'should be a 16-bit unsigned integer' do
297
+ expect(packet.user).to be_a BinData::Uint16le
298
+ end
299
+ end
300
+
301
+ describe '#user_name' do
302
+ it 'should be a 16-bit unsigned integer' do
303
+ expect(packet.user_name).to be_a BinData::Uint16le
304
+ end
305
+ end
36
306
 
37
- it 'sets the path string to a UTF-16LE version of the supplied string' do
38
- packet.encode_path(path)
39
- expect(packet.path).to eq encoded_path
307
+ describe '#domain' do
308
+ it 'should be a 16-bit unsigned integer' do
309
+ expect(packet.domain).to be_a BinData::Uint16le
40
310
  end
311
+ end
41
312
 
42
- it 'updates the #path_length' do
43
- packet.encode_path(path)
44
- expect(packet.path_length).to eq encoded_path.length
313
+ describe '#groups' do
314
+ it 'should be a 16-bit unsigned integer' do
315
+ expect(packet.groups).to be_a BinData::Uint16le
45
316
  end
46
317
  end
318
+
319
+ describe '#restricted_groups' do
320
+ it 'should be a 16-bit unsigned integer' do
321
+ expect(packet.restricted_groups).to be_a BinData::Uint16le
322
+ end
323
+ end
324
+
325
+ describe '#privileges' do
326
+ it 'should be a 16-bit unsigned integer' do
327
+ expect(packet.privileges).to be_a BinData::Uint16le
328
+ end
329
+ end
330
+
331
+ describe '#primary_group' do
332
+ it 'should be a 16-bit unsigned integer' do
333
+ expect(packet.primary_group).to be_a BinData::Uint16le
334
+ end
335
+ end
336
+
337
+ describe '#owner' do
338
+ it 'should be a 16-bit unsigned integer' do
339
+ expect(packet.owner).to be_a BinData::Uint16le
340
+ end
341
+ end
342
+
343
+ describe '#default_dacl' do
344
+ it 'should be a 16-bit unsigned integer' do
345
+ expect(packet.default_dacl).to be_a BinData::Uint16le
346
+ end
347
+ end
348
+
349
+ describe '#device_groups' do
350
+ it 'should be a 16-bit unsigned integer' do
351
+ expect(packet.device_groups).to be_a BinData::Uint16le
352
+ end
353
+ end
354
+
355
+ describe '#user_claims' do
356
+ it 'should be a 16-bit unsigned integer' do
357
+ expect(packet.user_claims).to be_a BinData::Uint16le
358
+ end
359
+ end
360
+
361
+ describe '#device_claims' do
362
+ it 'should be a 16-bit unsigned integer' do
363
+ expect(packet.device_claims).to be_a BinData::Uint16le
364
+ end
365
+ end
366
+
367
+ describe '#ticket_info' do
368
+ it 'should be string' do
369
+ expect(packet.ticket_info).to be_a BinData::String
370
+ end
371
+ end
372
+
373
+ it 'reads binary data as expected' do
374
+ data = described_class.new(ticket_info: 'ticket info')
375
+ expect(described_class.read(data.to_binary_s)).to eq(data)
376
+ end
47
377
  end