PEP8: fix E303: too many blank lines (2)
[samba.git] / source4 / dsdb / tests / python / sites.py
1 #!/usr/bin/env python
2 #
3 # Unit tests for sites manipulation in samba
4 # Copyright (C) Matthieu Patou <mat@matws.net> 2011
5 #
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 from __future__ import print_function
20
21 import optparse
22 import sys
23 sys.path.insert(0, "bin/python")
24 import samba
25
26 from samba.tests.subunitrun import TestProgram, SubunitOptions
27
28 import samba.getopt as options
29 from samba import sites
30 from samba import subnets
31 from samba.auth import system_session
32 from samba.samdb import SamDB
33 from samba import gensec
34 from samba.credentials import Credentials, DONT_USE_KERBEROS
35 import samba.tests
36 from samba.tests import delete_force
37 from samba.dcerpc import security
38 from ldb import SCOPE_SUBTREE, LdbError, ERR_INSUFFICIENT_ACCESS_RIGHTS
39
40 parser = optparse.OptionParser("sites.py [options] <host>")
41 sambaopts = options.SambaOptions(parser)
42 parser.add_option_group(sambaopts)
43 parser.add_option_group(options.VersionOptions(parser))
44
45 # use command line creds if available
46 credopts = options.CredentialsOptions(parser)
47 parser.add_option_group(credopts)
48 subunitopts = SubunitOptions(parser)
49 parser.add_option_group(subunitopts)
50
51 opts, args = parser.parse_args()
52
53 if len(args) < 1:
54     parser.print_usage()
55     sys.exit(1)
56
57 host = args[0]
58 if not "://" in host:
59     ldaphost = "ldap://%s" % host
60 else:
61     ldaphost = host
62
63 lp = sambaopts.get_loadparm()
64 creds = credopts.get_credentials(lp)
65
66 #
67 # Tests start here
68 #
69
70
71 class SitesBaseTests(samba.tests.TestCase):
72
73     def setUp(self):
74         super(SitesBaseTests, self).setUp()
75         self.ldb = SamDB(ldaphost, credentials=creds,
76                          session_info=system_session(lp), lp=lp)
77         self.base_dn = self.ldb.domain_dn()
78         self.domain_sid = security.dom_sid(self.ldb.get_domain_sid())
79         self.configuration_dn = self.ldb.get_config_basedn().get_linearized()
80
81     def get_user_dn(self, name):
82         return "CN=%s,CN=Users,%s" % (name, self.base_dn)
83
84
85 # tests on sites
86 class SimpleSitesTests(SitesBaseTests):
87
88     def test_create_and_delete(self):
89         """test creation and deletion of 1 site"""
90
91         sites.create_site(self.ldb, self.ldb.get_config_basedn(),
92                           "testsamba")
93
94         self.assertRaises(sites.SiteAlreadyExistsException,
95                           sites.create_site, self.ldb,
96                           self.ldb.get_config_basedn(),
97                           "testsamba")
98
99         sites.delete_site(self.ldb, self.ldb.get_config_basedn(),
100                           "testsamba")
101
102         self.assertRaises(sites.SiteNotFoundException,
103                           sites.delete_site, self.ldb,
104                           self.ldb.get_config_basedn(),
105                           "testsamba")
106
107     def test_delete_not_empty(self):
108         """test removal of 1 site with servers"""
109
110         self.assertRaises(sites.SiteServerNotEmptyException,
111                           sites.delete_site, self.ldb,
112                           self.ldb.get_config_basedn(),
113                           "Default-First-Site-Name")
114
115
116 # tests for subnets
117 class SimpleSubnetTests(SitesBaseTests):
118
119     def setUp(self):
120         super(SimpleSubnetTests, self).setUp()
121         self.basedn = self.ldb.get_config_basedn()
122         self.sitename = "testsite"
123         self.sitename2 = "testsite2"
124         self.ldb.transaction_start()
125         sites.create_site(self.ldb, self.basedn, self.sitename)
126         sites.create_site(self.ldb, self.basedn, self.sitename2)
127         self.ldb.transaction_commit()
128
129     def tearDown(self):
130         self.ldb.transaction_start()
131         sites.delete_site(self.ldb, self.basedn, self.sitename)
132         sites.delete_site(self.ldb, self.basedn, self.sitename2)
133         self.ldb.transaction_commit()
134         super(SimpleSubnetTests, self).tearDown()
135
136     def test_create_delete(self):
137         """Create a subnet and delete it again."""
138         basedn = self.ldb.get_config_basedn()
139         cidr = "10.11.12.0/24"
140
141         subnets.create_subnet(self.ldb, basedn, cidr, self.sitename)
142
143         self.assertRaises(subnets.SubnetAlreadyExists,
144                           subnets.create_subnet, self.ldb, basedn, cidr,
145                           self.sitename)
146
147         subnets.delete_subnet(self.ldb, basedn, cidr)
148
149         ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
150                               expression='(&(objectclass=subnet)(cn=%s))' % cidr)
151
152         self.assertEqual(len(ret), 0, 'Failed to delete subnet %s' % cidr)
153
154     def test_create_shift_delete(self):
155         """Create a subnet, shift it to another site, then delete it."""
156         basedn = self.ldb.get_config_basedn()
157         cidr = "10.11.12.0/24"
158
159         subnets.create_subnet(self.ldb, basedn, cidr, self.sitename)
160
161         subnets.set_subnet_site(self.ldb, basedn, cidr, self.sitename2)
162
163         ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
164                               expression='(&(objectclass=subnet)(cn=%s))' % cidr)
165
166         sites = ret[0]['siteObject']
167         self.assertEqual(len(sites), 1)
168         self.assertEqual(sites[0],
169                          'CN=testsite2,CN=Sites,%s' % self.ldb.get_config_basedn())
170
171         self.assertRaises(subnets.SubnetAlreadyExists,
172                           subnets.create_subnet, self.ldb, basedn, cidr,
173                           self.sitename)
174
175         subnets.delete_subnet(self.ldb, basedn, cidr)
176
177         ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
178                               expression='(&(objectclass=subnet)(cn=%s))' % cidr)
179
180         self.assertEqual(len(ret), 0, 'Failed to delete subnet %s' % cidr)
181
182     def test_delete_subnet_that_does_not_exist(self):
183         """Ensure we can't delete a site that isn't there."""
184         basedn = self.ldb.get_config_basedn()
185         cidr = "10.15.0.0/16"
186
187         self.assertRaises(subnets.SubnetNotFound,
188                           subnets.delete_subnet, self.ldb, basedn, cidr)
189
190     def get_user_and_ldb(self, username, password, hostname=ldaphost):
191         """Get a connection for a temporarily user that will vanish as soon as
192         the test is over."""
193         user = self.ldb.newuser(username, password)
194         creds_tmp = Credentials()
195         creds_tmp.set_username(username)
196         creds_tmp.set_password(password)
197         creds_tmp.set_domain(creds.get_domain())
198         creds_tmp.set_realm(creds.get_realm())
199         creds_tmp.set_workstation(creds.get_workstation())
200         creds_tmp.set_gensec_features(creds_tmp.get_gensec_features()
201                                       | gensec.FEATURE_SEAL)
202         creds_tmp.set_kerberos_state(DONT_USE_KERBEROS)
203         ldb_target = SamDB(url=hostname, credentials=creds_tmp, lp=lp)
204         self.addCleanup(delete_force, self.ldb, self.get_user_dn(username))
205         return (user, ldb_target)
206
207     def test_rename_delete_good_subnet_to_good_subnet_other_user(self):
208         """Make sure that we can't rename or delete subnets when we aren't
209         admin."""
210         basedn = self.ldb.get_config_basedn()
211         cidr = "10.16.0.0/24"
212         new_cidr = "10.16.1.0/24"
213         subnets.create_subnet(self.ldb, basedn, cidr, self.sitename)
214         user, non_admin_ldb = self.get_user_and_ldb("notadmin", "samba123@")
215         try:
216             subnets.rename_subnet(non_admin_ldb, basedn, cidr, new_cidr)
217         except LdbError as e:
218             self.assertEqual(e.args[0], ERR_INSUFFICIENT_ACCESS_RIGHTS,
219                              ("subnet rename by non-admin failed "
220                               "in the wrong way: %s" % e))
221         else:
222             self.fail("subnet rename by non-admin succeeded: %s" % e)
223
224         ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
225                               expression='(&(objectclass=subnet)(cn=%s))' % cidr)
226
227         self.assertEqual(len(ret), 1, ('Subnet %s destroyed or renamed '
228                                        'by non-admin' % cidr))
229
230         ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
231                               expression=('(&(objectclass=subnet)(cn=%s))'
232                                           % new_cidr))
233
234         self.assertEqual(len(ret), 0,
235                          'New subnet %s created by non-admin' % cidr)
236
237         try:
238             subnets.delete_subnet(non_admin_ldb, basedn, cidr)
239         except LdbError as e:
240             self.assertEqual(e.args[0], ERR_INSUFFICIENT_ACCESS_RIGHTS,
241                              ("subnet delete by non-admin failed "
242                               "in the wrong way: %s" % e))
243         else:
244             self.fail("subnet delete by non-admin succeeded: %s" % e)
245
246         ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
247                               expression='(&(objectclass=subnet)(cn=%s))' % cidr)
248
249         self.assertEqual(len(ret), 1, 'Subnet %s deleted non-admin' % cidr)
250
251         subnets.delete_subnet(self.ldb, basedn, cidr)
252
253     def test_create_good_subnet_other_user(self):
254         """Make sure that we can't create subnets when we aren't admin."""
255         basedn = self.ldb.get_config_basedn()
256         cidr = "10.16.0.0/24"
257         user, non_admin_ldb = self.get_user_and_ldb("notadmin", "samba123@")
258         try:
259             subnets.create_subnet(non_admin_ldb, basedn, cidr, self.sitename)
260         except LdbError as e:
261             self.assertEqual(e.args[0], ERR_INSUFFICIENT_ACCESS_RIGHTS,
262                              ("subnet create by non-admin failed "
263                               "in the wrong way: %s" % e))
264         else:
265             subnets.delete_subnet(self.ldb, basedn, cidr)
266             self.fail("subnet create by non-admin succeeded: %s")
267
268         ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
269                               expression='(&(objectclass=subnet)(cn=%s))' % cidr)
270
271         self.assertEqual(len(ret), 0, 'New subnet %s created by non-admin' % cidr)
272
273     def test_rename_good_subnet_to_good_subnet(self):
274         """Make sure that we can rename subnets"""
275         basedn = self.ldb.get_config_basedn()
276         cidr = "10.16.0.0/24"
277         new_cidr = "10.16.1.0/24"
278
279         subnets.create_subnet(self.ldb, basedn, cidr, self.sitename)
280
281         subnets.rename_subnet(self.ldb, basedn, cidr, new_cidr)
282
283         ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
284                               expression='(&(objectclass=subnet)(cn=%s))' % new_cidr)
285
286         self.assertEqual(len(ret), 1, 'Failed to rename subnet %s' % cidr)
287
288         ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
289                               expression='(&(objectclass=subnet)(cn=%s))' % cidr)
290
291         self.assertEqual(len(ret), 0, 'Failed to remove old subnet during rename %s' % cidr)
292
293         subnets.delete_subnet(self.ldb, basedn, new_cidr)
294
295     def test_rename_good_subnet_to_bad_subnet(self):
296         """Make sure that the CIDR checking runs during rename"""
297         basedn = self.ldb.get_config_basedn()
298         cidr = "10.17.0.0/24"
299         bad_cidr = "10.11.12.0/14"
300
301         subnets.create_subnet(self.ldb, basedn, cidr, self.sitename)
302
303         self.assertRaises(subnets.SubnetInvalid, subnets.rename_subnet,
304                           self.ldb, basedn, cidr, bad_cidr)
305
306         ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
307                               expression='(&(objectclass=subnet)(cn=%s))' % bad_cidr)
308
309         self.assertEqual(len(ret), 0, 'Failed to rename subnet %s' % cidr)
310
311         ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
312                               expression='(&(objectclass=subnet)(cn=%s))' % cidr)
313
314         self.assertEqual(len(ret), 1, 'Failed to remove old subnet during rename %s' % cidr)
315
316         subnets.delete_subnet(self.ldb, basedn, cidr)
317
318     def test_create_bad_ranges(self):
319         """These CIDR ranges all have something wrong with them, and they
320         should all fail."""
321         basedn = self.ldb.get_config_basedn()
322
323         cidrs = [
324             # IPv4
325             # insufficient zeros
326             "10.11.12.0/14",
327             "110.0.0.0/6",
328             "1.0.0.0/0",
329             "10.11.13.1/24",
330             "1.2.3.4/29",
331             "10.11.12.0/21",
332             # out of range mask
333             "110.0.0.0/33",
334             "110.0.0.0/-1",
335             "4.0.0.0/111",
336             # out of range address
337             "310.0.0.0/24",
338             "10.0.0.256/32",
339             "1.1.-20.0/24",
340             # badly formed
341             "1.0.0.0/1e",
342             "1.0.0.0/24.0",
343             "1.0.0.0/1/1",
344             "1.0.0.0",
345             "1.c.0.0/24",
346             "1.2.0.0.0/27",
347             "1.23.0/24",
348             "1.23.0.-7/24",
349             "1.-23.0.7/24",
350             "1.23.-0.7/24",
351             "1.23.0.0/0x10",
352             # IPv6 insufficient zeros -- this could be a subtle one
353             # due to the vagaries of endianness in the 16 bit groups.
354             "aaaa:bbbb:cccc:dddd:eeee:ffff:2222:1100/119",
355             "aaaa:bbbb::/31",
356             "a:b::/31",
357             "c000::/1",
358             "a::b00/119",
359             "1::1/127",
360             "1::2/126",
361             "1::100/119",
362             "1::8000/112",
363             # out of range mask
364             "a:b::/130",
365             "a:b::/-1",
366             "::/129",
367             # An IPv4 address can't be exactly the bitmask (MS ADTS)
368             "128.0.0.0/1",
369             "192.0.0.0/2",
370             "255.192.0.0/10",
371             "255.255.255.0/24",
372             "255.255.255.255/32",
373             "0.0.0.0/0",
374             # The address can't have leading zeros (not RFC 4632, but MS ADTS)
375             "00.1.2.0/24",
376             "003.1.2.0/24",
377             "022.1.0.0/16",
378             "00000000000000000000000003.1.2.0/24",
379             "09876::abfc/126",
380             "0aaaa:bbbb::/32",
381             "009876::abfc/126",
382             "000a:bbbb::/32",
383
384             # How about extraneous zeros later on
385             "3.01.2.0/24",
386             "3.1.2.00/24",
387             "22.001.0.0/16",
388             "3.01.02.0/24",
389             "100a:0bbb:0023::/48",
390             "100a::0023/128",
391
392             # Windows doesn't like the zero IPv4 address
393             "0.0.0.0/8",
394             # or the zero mask on IPv6
395             "::/0",
396
397             # various violations of RFC5952
398             "0:0:0:0:0:0:0:0/8",
399             "0::0/0",
400             "::0:0/48",
401             "::0:4/128",
402             "0::/8",
403             "0::4f/128",
404             "0::42:0:0:0:0/64",
405             "4f::0/48",
406
407             # badly formed -- mostly the wrong arrangement of colons
408             "a::b::0/120",
409             "a::abcdf:0/120",
410             "a::g:0/120",
411             "::0::3/48",
412             "2001:3::110::3/118",
413             "aaaa:bbbb:cccc:dddd:eeee:ffff:2222:1111:0000/128",
414             "a:::5:0/120",
415
416             # non-canonical representations (vs RFC 5952)
417             # "2001:0:c633:63::1:0/120"  is correct
418             "2001:0:c633:63:0:0:1:0/120",
419             "2001::c633:63:0:0:1:0/120",
420             "2001:0:c633:63:0:0:1::/120",
421
422             # "10:0:0:42::/64" is correct
423             "10::42:0:0:0:0/64",
424             "10:0:0:42:0:0:0:0/64",
425
426             # "1::4:5:0:0:8/127" is correct
427             "1:0:0:4:5:0:0:8/127",
428             "1:0:0:4:5::8/127",
429
430             # "2001:db8:0:1:1:1:1:1/128" is correct
431             "2001:db8::1:1:1:1:1/128",
432
433             # IP4 embedded - rejected
434             "a::10.0.0.0/120",
435             "a::10.9.8.7/128",
436
437             # The next ones tinker indirectly with IPv4 embedding,
438             # where Windows has some odd behaviour.
439             #
440             # Samba's libreplace inet_ntop6 expects IPv4 embedding
441             # with addresses in these forms:
442             #
443             #     ::wx:yz
444             #     ::FFFF:wx:yz
445             #
446             # these will be stringified with trailing dottted decimal, thus:
447             #
448             #     ::w.x.y.z
449             #     ::ffff:w.x.y.z
450             #
451             # and this will cause the address to be rejected by Samba,
452             # because it uses a inet_pton / inet_ntop round trip to
453             # ascertain correctness.
454
455             "::ffff:0:0/96",  # this one fails on WIN2012r2
456             "::ffff:aaaa:a000/120",
457             "::ffff:10:0/120",
458             "::ffff:2:300/120",
459             "::3:0/120",
460             "::2:30/124",
461             "::ffff:2:30/124",
462
463             # completely wrong
464             None,
465             "bob",
466             3.1415,
467             False,
468             "10.11.16.0/24\x00hidden bytes past a zero",
469             self,
470         ]
471
472         failures = []
473         for cidr in cidrs:
474             try:
475                 subnets.create_subnet(self.ldb, basedn, cidr, self.sitename)
476             except subnets.SubnetInvalid:
477                 print("%s fails properly" % (cidr,), file=sys.stderr)
478                 continue
479
480             # we are here because it succeeded when it shouldn't have.
481             print("CIDR %s fails to fail" % (cidr,), file=sys.stderr)
482             failures.append(cidr)
483             subnets.delete_subnet(self.ldb, basedn, cidr)
484
485         if failures:
486             print("These bad subnet names were accepted:")
487             for cidr in failures:
488                 print("    %s" % cidr)
489             self.fail()
490
491     def test_create_good_ranges(self):
492         """All of these CIDRs are good, and the subnet creation should
493         succeed."""
494         basedn = self.ldb.get_config_basedn()
495
496         cidrs = [
497             # IPv4
498             "10.11.12.0/24",
499             "10.11.12.0/23",
500             "10.11.12.0/25",
501             "110.0.0.0/7",
502             "1.0.0.0/32",
503             "10.11.13.0/32",
504             "10.11.13.1/32",
505             "99.0.97.0/24",
506             "1.2.3.4/30",
507             "10.11.12.0/22",
508             "0.12.13.0/24",
509             # IPv6
510             "aaaa:bbbb:cccc:dddd:eeee:ffff:2222:1100/120",
511             "aaaa:bbbb:cccc:dddd:eeee:ffff:2222:11f0/124",
512             "aaaa:bbbb:cccc:dddd:eeee:ffff:2222:11fc/126",
513             # don't forget upper case
514             "FFFF:FFFF:FFFF:FFFF:ABCD:EfFF:FFFF:FFeF/128",
515             "9876::ab00/120",
516             "9876::abf0/124",
517             "9876::abfc/126",
518             "aaaa:bbbb::/32",
519             "aaaa:bbba::/31",
520             "aaaa:ba00::/23",
521             "aaaa:bb00::/24",
522             "aaaa:bb00::/77",
523             "::/48",
524             "a:b::/32",
525             "c000::/2",
526             "a::b00/120",
527             "1::2/127",
528             # this pattern of address suffix == mask is forbidden with
529             # IPv4 but OK for IPv6.
530             "8000::/1",
531             "c000::/2",
532             "ffff:ffff:ffc0::/42",
533             "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF/128",
534             # leading zeros are forbidden, but implicit IPv6 zeros
535             # (via "::") are OK.
536             "::1000/116",
537             "::8000/113",
538             # taken to the logical conclusion, "::/0" should be OK, but no.
539             "::/48",
540
541             # Try some reserved ranges, which it might be reasonable
542             # to exclude, but which are not excluded in practice.
543             "129.0.0.0/16",
544             "129.255.0.0/16",
545             "100.64.0.0/10",
546             "127.0.0.0/8",
547             "127.0.0.0/24",
548             "169.254.0.0/16",
549             "169.254.1.0/24",
550             "192.0.0.0/24",
551             "192.0.2.0/24",
552             "198.18.0.0/15",
553             "198.51.100.0/24",
554             "203.0.113.0/24",
555             "224.0.0.0/4",
556             "130.129.0.0/16",
557             "130.255.0.0/16",
558             "192.12.0.0/24",
559             "223.255.255.0/24",
560             "240.255.255.0/24",
561             "224.0.0.0/8",
562             "::/96",
563             "100::/64",
564             "2001:10::/28",
565             "fec0::/10",
566             "ff00::/8",
567             "::1/128",
568             "2001:db8::/32",
569             "2001:10::/28",
570             "2002::/24",
571             "2002:a00::/24",
572             "2002:7f00::/24",
573             "2002:a9fe::/32",
574             "2002:ac10::/28",
575             "2002:c000::/40",
576             "2002:c000:200::/40",
577             "2002:c0a8::/32",
578             "2002:c612::/31",
579             "2002:c633:6400::/40",
580             "2002:cb00:7100::/40",
581             "2002:e000::/20",
582             "2002:f000::/20",
583             "2002:ffff:ffff::/48",
584             "2001::/40",
585             "2001:0:a00::/40",
586             "2001:0:7f00::/40",
587             "2001:0:a9fe::/48",
588             "2001:0:ac10::/44",
589             "2001:0:c000::/56",
590             "2001:0:c000:200::/56",
591             "2001:0:c0a8::/48",
592             "2001:0:c612::/47",
593             "2001:0:c633:6400::/56",
594             "2001:0:cb00:7100::/56",
595             "2001:0:e000::/36",
596             "2001:0:f000::/36",
597             "2001:0:ffff:ffff::/64",
598
599             # non-RFC-5952 versions of these are tested in create_bad_ranges
600             "2001:0:c633:63::1:0/120",
601             "10:0:0:42::/64",
602             "1::4:5:0:0:8/127",
603             "2001:db8:0:1:1:1:1:1/128",
604
605             # The "well-known prefix" 64::ff9b is another IPv4
606             # embedding scheme. Let's try that.
607             "64:ff9b::aaaa:aaaa/127",
608             "64:ff9b::/120",
609             "64:ff9b::ffff:2:3/128",
610         ]
611         failures = []
612
613         for cidr in cidrs:
614             try:
615                 subnets.create_subnet(self.ldb, basedn, cidr, self.sitename)
616             except subnets.SubnetInvalid as e:
617                 print(e)
618                 failures.append(cidr)
619                 continue
620
621             ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
622                                   expression=('(&(objectclass=subnet)(cn=%s))' %
623                                               cidr))
624
625             if len(ret) != 1:
626                 print("%s was not created" % cidr)
627                 failures.append(cidr)
628                 continue
629             subnets.delete_subnet(self.ldb, basedn, cidr)
630
631         if failures:
632             print("These good subnet names were not accepted:")
633             for cidr in failures:
634                 print("    %s" % cidr)
635             self.fail()
636
637
638 TestProgram(module=__name__, opts=subunitopts)