a33229e474097b5ae20ab864ca60e7fd3cbc3a7d
[samba.git] / python / samba / tests / blackbox / ndrdump.py
1 # Blackbox tests for ndrdump
2 # Copyright (C) 2008 Andrew Tridgell <tridge@samba.org>
3 # Copyright (C) 2008 Andrew Bartlett <abartlet@samba.org>
4 # Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
5 # based on test_smbclient.sh
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20
21 from __future__ import print_function
22 """Blackbox tests for ndrdump."""
23
24 import os
25 import re
26 from samba.tests import BlackboxTestCase, BlackboxProcessError
27
28 for p in ["../../../../../source4/librpc/tests",
29           "../../../../../librpc/tests"]:
30     data_path_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), p))
31     print(data_path_dir)
32     if os.path.exists(data_path_dir):
33         break
34
35
36 class NdrDumpTests(BlackboxTestCase):
37     """Blackbox tests for ndrdump."""
38
39     def data_path(self, name):
40         return os.path.join(data_path_dir, name)
41
42     def test_ndrdump_with_in(self):
43         self.check_run(("ndrdump samr samr_CreateUser in %s" %
44                        (self.data_path("samr-CreateUser-in.dat"))))
45
46     def test_ndrdump_with_out(self):
47         self.check_run(("ndrdump samr samr_CreateUser out %s" %
48                        (self.data_path("samr-CreateUser-out.dat"))))
49
50     def test_ndrdump_context_file(self):
51         self.check_run(
52             ("ndrdump --context-file %s samr samr_CreateUser out %s" %
53                 (self.data_path("samr-CreateUser-in.dat"),
54                 self.data_path("samr-CreateUser-out.dat"))))
55
56     def test_ndrdump_with_validate(self):
57         self.check_run(("ndrdump --validate samr samr_CreateUser in %s" %
58                        (self.data_path("samr-CreateUser-in.dat"))))
59
60     def test_ndrdump_with_hex_decode_function(self):
61         self.check_run(
62             ("ndrdump dns decode_dns_name_packet in --hex-input %s" %
63                 self.data_path("dns-decode_dns_name_packet-hex.dat")))
64
65     def test_ndrdump_with_hex_struct_name(self):
66         expected = open(self.data_path("dns-decode_dns_name_packet-hex.txt")).read()
67         try:
68             actual = self.check_output(
69                 "ndrdump dns dns_name_packet struct --hex-input %s" %
70                 self.data_path("dns-decode_dns_name_packet-hex.dat"))
71         except BlackboxProcessError as e:
72             self.fail(e)
73
74         # check_output will return bytes
75         # convert expected to bytes for python 3
76         self.assertEqual(actual, expected.encode('utf-8'))
77
78     def test_ndrdump_with_binary_struct_name(self):
79         # Prefix of the expected unparsed PAC data (without times, as
80         # these vary by host)
81         expected = '''pull returned Success
82     PAC_DATA: struct PAC_DATA
83         num_buffers              : 0x00000005 (5)
84         version                  : 0x00000000 (0)
85         buffers: ARRAY(5)'''
86         try:
87             actual = self.check_output(
88                 "ndrdump krb5pac PAC_DATA struct %s" %
89                 self.data_path("krb5pac-PAC_DATA.dat"))
90         except BlackboxProcessError as e:
91             self.fail(e)
92
93         # check_output will return bytes
94         # convert expected to bytes for python 3
95         self.assertEqual(actual[:len(expected)],
96                          expected.encode('utf-8'))
97         self.assertTrue(actual.endswith(b"dump OK\n"))
98
99     def test_ndrdump_with_binary_struct_number(self):
100         expected = '''pull returned Success
101     GUID                     : 33323130-3534-3736-3839-616263646566
102 dump OK
103 '''
104         try:
105             actual = self.check_output(
106                 "ndrdump misc 0 struct %s" %
107                 self.data_path("misc-GUID.dat"))
108         except BlackboxProcessError as e:
109             self.fail(e)
110
111         # check_output will return bytes
112         # convert expected to bytes for python 3
113         self.assertEqual(actual, expected.encode('utf-8'))
114
115     def test_ndrdump_with_enum_not_struct(self):
116         expected = '''Public structure 'netr_SchannelType' not found
117 '''
118         try:
119             actual = self.check_exit_code(
120                 "ndrdump misc netr_SchannelType --input=x struct",
121                 1)
122         except BlackboxProcessError as e:
123             self.fail(e)
124
125         # check_output will return bytes
126         # convert expected to bytes for python 3
127         self.assertEqual(actual, expected.encode('utf-8'))
128
129     def test_ndrdump_input_cmdline_short_struct_name(self):
130         expected = '''pull returned Buffer Size Error
131 '''
132         try:
133             actual = self.check_exit_code(
134                 "ndrdump -d0 misc GUID struct --input=abcdefg", 2)
135         except BlackboxProcessError as e:
136             self.fail(e)
137
138         # check_output will return bytes
139         # convert expected to bytes for python 3
140         self.assertEqual(actual, expected.encode('utf-8'))
141
142     def test_ndrdump_input_cmdline_short_struct_name_dump(self):
143         expected = '''pull returned Buffer Size Error
144 6 bytes consumed
145 [0000] 61 62 63 64 65 66 67                               abcdefg ''' \
146         '''
147 '''
148         try:
149             actual = self.check_exit_code(
150                 "ndrdump -d0 misc GUID struct --input=abcdefg --dump-data", 2)
151         except BlackboxProcessError as e:
152             self.fail(e)
153
154         # check_output will return bytes
155         # convert expected to bytes for python 3
156         self.assertEqual(actual, expected.encode('utf-8'))
157
158     def test_ndrdump_input_cmdline_short_struct_name_print_fail(self):
159         expected = '''pull returned Buffer Size Error
160 6 bytes consumed
161 [0000] 61 62 63 64 65 66 67                               abcdefg ''' \
162         '''
163 WARNING! 1 unread bytes
164 [0000] 67                                                 g ''' \
165     '''
166 WARNING: pull of GUID was incomplete, therefore the parse below may SEGFAULT
167     GUID                     : 64636261-6665-0000-0000-000000000000
168 dump of failed-to-parse GUID complete
169 '''
170         try:
171             actual = self.check_exit_code(
172                 "ndrdump -d0 misc GUID struct --input=abcdefg --dump-data --print-after-parse-failure", 2)
173         except BlackboxProcessError as e:
174             self.fail(e)
175
176         # check_output will return bytes
177         # convert expected to bytes for python 3
178         self.assertEqual(actual, expected.encode('utf-8'))
179
180     def test_ndrdump_fuzzed_clusapi_QueryAllValues(self):
181         expected = b'''pull returned Success
182 WARNING! 53 unread bytes
183 [0000] 00 FF 00 00 FF 00 00 00   00 09 00 00 00 08 00 33   ........ .......3
184 [0010] 33 32 37 36 32 36 39 33   32 37 36 38 34 01 00 00   32762693 27684...
185 [0020] 80 32 0D FF 00 00 FF 00   00 00 00 08 00 00 00 1C   .2...... ........
186 [0030] F1 29 08 00 00                                     .)... ''' \
187         b'''
188     clusapi_QueryAllValues: struct clusapi_QueryAllValues
189         out: struct clusapi_QueryAllValues
190             pcbData                  : *
191                 pcbData                  : 0x01000000 (16777216)
192             ppData                   : *
193                 ppData: ARRAY(1)
194                     ppData                   : NULL
195             rpc_status               : *
196                 rpc_status               : WERR_OK
197             result                   : WERR_NOT_ENOUGH_MEMORY
198 dump OK
199 '''
200         try:
201             actual = self.check_output(
202                 'ndrdump clusapi clusapi_QueryAllValues out ' +\
203                 '--base64-input --input=' +\
204                 'AAAAAQEAAAAAAAAAAAAAAAgAAAAA/wAA/wAAAAAJAAAACAAzMzI3NjI2OTMyNzY4NAEAAIAyDf8AAP8AAAAACAAAABzxKQgAAA==')
205         except BlackboxProcessError as e:
206             self.fail(e)
207         self.assertEqual(actual, expected)
208
209     def test_ndrdump_fuzzed_IOXIDResolver_ResolveOxid(self):
210         expected = '''pull returned Character Conversion Error
211 '''
212         try:
213             actual = self.check_exit_code(
214                 'ndrdump IOXIDResolver ResolveOxid out ' +\
215                 '--base64-input --input=' +\
216                 'c87PMf7CBAUAAAAADgQMBASjfPqKw0KPld6DY87PMfQ=',
217                 2)
218         except BlackboxProcessError as e:
219             self.fail(e)
220         self.assertRegex(actual.decode('utf8'), expected + "$")
221
222     def test_ndrdump_fuzzed_IOXIDResolver_ResolveOxid2(self):
223         expected = '''pull returned Buffer Size Error
224 '''
225         try:
226             actual = self.check_exit_code(
227                 'ndrdump IOXIDResolver ResolveOxid2 out ' +\
228                 '--base64-input --input=' +\
229                 'AAAAAQ0K9Q0AAAAAAAAAA6ampqampqampqampqampqampqampqamNAAAAAAtNDQ=',
230                 2)
231         except BlackboxProcessError as e:
232             self.fail(e)
233         self.assertRegex(actual.decode('utf8'), expected + "$")
234
235     def test_ndrdump_fuzzed_IOXIDResolver_ServerAlive2(self):
236         expected = b'''pull returned Success
237 WARNING! 46 unread bytes
238 [0000] 0D 36 0A 0A 0A 0A 0A 00   00 00 00 00 00 00 03 00   .6...... ........
239 [0010] 00 00 01 00 00 33 39 36   31 36 31 37 37 36 38 34   .....396 16177684
240 [0020] 32 34 FC 85 AC 49 0B 61   87 0A 0A 0A F5 00         24...I.a ......
241     ServerAlive: struct ServerAlive
242         out: struct ServerAlive
243             result                   : DOS code 0x01000000
244 dump OK
245 '''
246         try:
247             actual = self.check_output(
248                 'ndrdump IOXIDResolver ServerAlive out ' +\
249                 '--base64-input --input=' +\
250                 'AAAAAQ02CgoKCgoAAAAAAAAAAwAAAAEAADM5NjE2MTc3Njg0MjT8haxJC2GHCgoK9QA=')
251         except BlackboxProcessError as e:
252             self.fail(e)
253         self.assertEqual(actual, expected)
254
255     def test_ndrdump_fuzzed_IRemoteActivation_RemoteActivation(self):
256         expected = '''pull returned Buffer Size Error
257 '''
258         try:
259             actual = self.check_exit_code(
260                 'ndrdump IRemoteActivation RemoteActivation out ' +\
261                 '--base64-input --input=' +\
262                 'AAAAAQAAAAAAAABKAAD/AAAAAP4AAAAAAAAASgAAAAAAAAABIiIjIiIiIiIiIiIiIiMiAAAAAAD/AAAAAAAA',
263                 2)
264         except BlackboxProcessError as e:
265             self.fail(e)
266         self.assertRegex(actual.decode('utf8'), expected + "$")
267
268     def test_ndrdump_fuzzed_ntlmsssp_AUTHENTICATE_MESSAGE(self):
269         expected = open(self.data_path("fuzzed_ntlmssp-AUTHENTICATE_MESSAGE.txt")).read()
270         try:
271             actual = self.check_output(
272                 "ndrdump ntlmssp AUTHENTICATE_MESSAGE struct --base64-input %s --validate" %
273                 self.data_path("fuzzed_ntlmssp-AUTHENTICATE_MESSAGE.b64.txt"))
274         except BlackboxProcessError as e:
275             self.fail(e)
276         # check_output will return bytes
277         # convert expected to bytes for python 3
278         self.assertEqual(actual, expected.encode('utf-8'))
279
280     def test_ndrdump_fuzzed_PackagesBlob(self):
281         expected = 'ndr_pull_string: ndr_pull_error\\(Buffer Size Error\\):'
282         command = (
283             "ndrdump drsblobs package_PackagesBlob struct --input='aw=='"
284             " --base64-input")
285         try:
286             actual = self.check_exit_code(command, 2)
287         except BlackboxProcessError as e:
288             self.fail(e)
289         # check_output will return bytes
290         # convert expected to bytes for python 3
291         self.assertRegex(actual.decode('utf8'), expected)
292
293     def test_ndrdump_fuzzed_drsuapi_DsAddEntry_1(self):
294         expected = open(self.data_path("fuzzed_drsuapi_DsAddEntry_1.txt")).read()
295         try:
296             actual = self.check_output(
297                 "ndrdump drsuapi drsuapi_DsAddEntry in --base64-input --validate %s" %
298                 self.data_path("fuzzed_drsuapi_DsAddEntry_1.b64.txt"))
299         except BlackboxProcessError as e:
300             self.fail(e)
301         # check_output will return bytes
302         # convert expected to bytes for python 3
303         self.assertEqual(actual, expected.encode('utf-8'))
304
305     def test_ndrdump_fuzzed_drsuapi_DsaAddressListItem_V1(self):
306         expected = "Maximum Recursion Exceeded"
307         try:
308             self.check_output(
309                 "ndrdump drsuapi 17 out --base64-input %s" %
310                 self.data_path(
311                     "fuzzed_drsuapi_DsaAddressListItem_V1-in.b64.txt"))
312             self.fail("Input should have been rejected with %s" % expected)
313         except BlackboxProcessError as e:
314             if expected not in str(e):
315                 self.fail(e)
316
317     def test_ndrdump_fuzzed_drsuapi_DsReplicaAttribute(self):
318         expected = open(self.data_path("fuzzed_drsuapi_DsReplicaAttribute.txt")).read()
319         try:
320             actual = self.check_output(
321                 "ndrdump drsuapi drsuapi_DsReplicaAttribute struct --base64-input --validate %s" %
322                 self.data_path("fuzzed_drsuapi_DsReplicaAttribute.b64.txt"))
323         except BlackboxProcessError as e:
324             self.fail(e)
325         # check_output will return bytes
326         # convert expected to bytes for python 3
327         self.assertEqual(actual, expected.encode('utf-8'))
328
329     # This is a good example of a union with an empty default
330     # and no buffers to parse.
331     def test_ndrdump_fuzzed_spoolss_EnumForms(self):
332         expected_head = b'''pull returned Success
333 WARNING! 2 unread bytes
334 [0000] 00 00                                              .. ''' b'''
335     spoolss_EnumForms: struct spoolss_EnumForms
336         out: struct spoolss_EnumForms
337             count                    : *
338                 count                    : 0x00000100 (256)
339             info                     : *
340                 info                     : *
341                     info: ARRAY(256)
342                         info                     : union spoolss_FormInfo(case 0)
343                         info                     : union spoolss_FormInfo(case 0)
344 '''
345         expected_tail = b'''info                     : union spoolss_FormInfo(case 0)
346                         info                     : union spoolss_FormInfo(case 0)
347                         info                     : union spoolss_FormInfo(case 0)
348                         info                     : union spoolss_FormInfo(case 0)
349                         info                     : union spoolss_FormInfo(case 0)
350                         info                     : union spoolss_FormInfo(case 0)
351             needed                   : *
352                 needed                   : 0x00000000 (0)
353             result                   : DOS code 0xa9a9a900
354 dump OK
355 '''
356         try:
357             actual = self.check_output(
358                 "ndrdump spoolss spoolss_EnumForms out --base64-input " +\
359                 "--input AAAAAQAAAAAAAAAAAAEAAACpqakAAA="
360                 )
361         except BlackboxProcessError as e:
362             self.fail(e)
363         self.assertEqual(actual[:len(expected_head)],
364                          expected_head)
365         self.assertTrue(actual.endswith(expected_tail))
366
367     # This is a good example of a union with scalars and buffers
368     def test_ndrdump_xattr_NTACL(self):
369
370         expected_head =  open(self.data_path("xattr_NTACL.txt")).read().encode('utf8')
371         expected_tail = b'''dump OK
372 '''
373         try:
374             actual = self.check_output(
375                 "ndrdump xattr xattr_NTACL struct --hex-input %s --validate" %
376                 self.data_path("xattr_NTACL.dat"))
377         except BlackboxProcessError as e:
378             self.fail(e)
379
380         self.assertEqual(actual[:len(expected_head)],
381                          expected_head)
382         self.assertTrue(actual.endswith(expected_tail))
383
384     # Confirm parsing of dnsProperty records
385     def test_ndrdump_dnsp_DnssrvRpcRecord(self):
386
387         expected = open(self.data_path("dnsp-DnssrvRpcRecord.txt")).read().encode('utf8')
388         try:
389             actual = self.check_output(
390                 "ndrdump dnsp dnsp_DnssrvRpcRecord struct " +\
391                 "--input BQAPAAXwAAC3AAAAAAADhAAAAAAAAAAAAAoBAAA= "+\
392                 "--base64-input --validate")
393         except BlackboxProcessError as e:
394             self.fail(e)
395
396         self.assertEqual(actual, expected)
397
398     # Test a --validate push of a NULL union pointer
399     def test_ndrdump_fuzzed_NULL_union_PAC_BUFFER(self):
400         expected = b'''pull returned Success
401 WARNING! 13 unread bytes
402 [0000] F5 FF 00 3C 3C 25 FF 70   16 1F A0 12 84            ...<<%.p .....
403     PAC_BUFFER: struct PAC_BUFFER
404         type                     : UNKNOWN_ENUM_VALUE (1094251328)
405         _ndr_size                : 0x048792c6 (75993798)
406         info                     : NULL
407         _pad                     : 0x06000000 (100663296)
408 push returned Success
409 pull returned Success
410     PAC_BUFFER: struct PAC_BUFFER
411         type                     : UNKNOWN_ENUM_VALUE (1094251328)
412         _ndr_size                : 0x00000000 (0)
413         info                     : NULL
414         _pad                     : 0x00000000 (0)
415 WARNING! orig bytes:29 validated pushed bytes:16
416 WARNING! orig and validated differ at byte 0x04 (4)
417 WARNING! orig byte[0x04] = 0xC6 validated byte[0x04] = 0x00
418 dump OK
419 '''
420         try:
421             actual = self.check_output(
422                 "ndrdump krb5pac PAC_BUFFER struct --validate --input " +\
423                 "QPM4QcaShwQAAAAAAAAABvX/ADw8Jf9wFh+gEoQ= --base64-input")
424         except BlackboxProcessError as e:
425             self.fail(e)
426
427         self.assertEqual(actual, expected)
428
429     # Test a --validate push of a NULL struct pointer
430     def test_ndrdump_fuzzed_NULL_struct_ntlmssp_CHALLENGE_MESSAGE(self):
431         expected =  open(self.data_path("fuzzed_ntlmssp-CHALLENGE_MESSAGE.txt")).read().encode('utf8')
432         try:
433             actual = self.check_exit_code(
434                 "ndrdump ntlmssp CHALLENGE_MESSAGE struct --validate --input " +\
435                 "'AAAACwIAAAAAJwIAAAAAAAcAAAAAAAAAAIAbhG8uyk9dAL0mQE73MAAAAAAAAAAA' --base64-input",
436                 1)
437         except BlackboxProcessError as e:
438             self.fail(e)
439
440         # Filter out the C source file and line number
441         regex = rb"\.\./\.\./librpc/ndr/ndr\.c:[0-9]+"
442         actual = re.sub(regex, b"", actual)
443         expected = re.sub(regex, b"", expected)
444
445         self.assertEqual(actual, expected)
446
447     # Test a print of NULL pointer in manually-written ndr_drsuapi.c
448     def test_fuzzed_drsuapi_DsGetNCChanges(self):
449         expected =  open(self.data_path("fuzzed_drsuapi_DsGetNCChanges.txt"), 'rb').read()
450         try:
451             actual = self.check_output(
452                 "ndrdump drsuapi 3 out --base64-input --input " +\
453                 "AQAAAAEAAAAGAKoAAAAGAKoGAAMAAQAAAAYAEwAAAAAAAAAA/wAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAABbAAAAAAAAAAAAAAkRAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPkAAAAAAAABAAD4BgATAAAAAAAAAAD/AAAAAAAAAD8AAAAAAAAAAAAAAAAAAAAAAFsAAAAAAAAAAAAABgAQAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAMAAAABAAAACREAAAEAAAABAAAAAAAAAAYAEAABAAgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAA=")
454         except BlackboxProcessError as e:
455             self.fail(e)
456
457         self.assertEqual(actual, expected)
458
459     def test_ndrdump_fuzzed_ndr_compression(self):
460         expected = 'pull returned Buffer Size Error'
461         command = (
462             "ndrdump drsuapi 3 out --base64-input "
463             "--input BwAAAAcAAAAGAAAAAwAgICAgICAJAAAAICAgIAkAAAAgIAAA//////8=")
464         try:
465             actual = self.check_exit_code(command, 2)
466         except BlackboxProcessError as e:
467             self.fail(e)
468         # check_output will return bytes
469         # convert expected to bytes for python 3
470         self.assertRegex(actual.decode('utf8'), expected + '$')
471
472     def test_ndrdump_short_dnsProperty(self):
473         expected = b'''pull returned Success
474     dnsp_DnsProperty_short: struct dnsp_DnsProperty_short
475         wDataLength              : 0x00000000 (0)
476         namelength               : 0x00000000 (0)
477         flag                     : 0x00000000 (0)
478         version                  : 0x00000001 (1)
479         id                       : DSPROPERTY_ZONE_NS_SERVERS_DA (146)
480         data                     : union dnsPropertyData(case 0)
481         name                     : 0x00000000 (0)
482 dump OK
483 '''
484         command = (
485             "ndrdump dnsp dnsp_DnsProperty_short struct --base64-input "
486             "--input AAAAAAAAAAAAAAAAAQAAAJIAAAAAAAAA")
487         try:
488             actual = self.check_output(command)
489         except BlackboxProcessError as e:
490             self.fail(e)
491         self.assertEqual(actual, expected)