provision: fix nTSecurityDescriptor of containers in the DnsZones (bug #9481)
[metze/samba/wip.git] / source4 / scripting / python / samba / provision / sambadns.py
1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning DNS for a Samba4 server
3 #
4 # Copyright (C) Kai Blin <kai@samba.org> 2011
5 # Copyright (C) Amitay Isaacs <amitay@gmail.com> 2011
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 """DNS-related provisioning"""
22
23 import os
24 import uuid
25 import shutil
26 import time
27 import ldb
28 from base64 import b64encode
29 import samba
30 from samba.ndr import ndr_pack, ndr_unpack
31 from samba import setup_file
32 from samba.dcerpc import dnsp, misc, security
33 from samba.dsdb import (
34     DS_DOMAIN_FUNCTION_2000,
35     DS_DOMAIN_FUNCTION_2003,
36     DS_DOMAIN_FUNCTION_2008_R2
37     )
38 from samba.provision.descriptor import (
39     get_domain_descriptor,
40     get_domain_delete_protected1_descriptor,
41     get_domain_delete_protected2_descriptor,
42     get_dns_partition_descriptor,
43     get_dns_forest_microsoft_dns_descriptor,
44     get_dns_domain_microsoft_dns_descriptor
45     )
46 from samba.provision.common import (
47     setup_path,
48     setup_add_ldif,
49     setup_modify_ldif,
50     setup_ldb
51     )
52
53
54 def get_domainguid(samdb, domaindn):
55     res = samdb.search(base=domaindn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"])
56     domainguid =  str(ndr_unpack(misc.GUID, res[0]["objectGUID"][0]))
57     return domainguid
58
59
60 def get_dnsadmins_sid(samdb, domaindn):
61     res = samdb.search(base="CN=DnsAdmins,CN=Users,%s" % domaindn, scope=ldb.SCOPE_BASE,
62                        attrs=["objectSid"])
63     dnsadmins_sid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0])
64     return dnsadmins_sid
65
66
67 class ARecord(dnsp.DnssrvRpcRecord):
68
69     def __init__(self, ip_addr, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE):
70         super(ARecord, self).__init__()
71         self.wType = dnsp.DNS_TYPE_A
72         self.rank = rank
73         self.dwSerial = serial
74         self.dwTtlSeconds = ttl
75         self.data = ip_addr
76
77
78 class AAAARecord(dnsp.DnssrvRpcRecord):
79
80     def __init__(self, ip6_addr, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE):
81         super(AAAARecord, self).__init__()
82         self.wType = dnsp.DNS_TYPE_AAAA
83         self.rank = rank
84         self.dwSerial = serial
85         self.dwTtlSeconds = ttl
86         self.data = ip6_addr
87
88
89 class CNameRecord(dnsp.DnssrvRpcRecord):
90
91     def __init__(self, cname, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE):
92         super(CNameRecord, self).__init__()
93         self.wType = dnsp.DNS_TYPE_CNAME
94         self.rank = rank
95         self.dwSerial = serial
96         self.dwTtlSeconds = ttl
97         self.data = cname
98
99
100 class NSRecord(dnsp.DnssrvRpcRecord):
101
102     def __init__(self, dns_server, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE):
103         super(NSRecord, self).__init__()
104         self.wType = dnsp.DNS_TYPE_NS
105         self.rank = rank
106         self.dwSerial = serial
107         self.dwTtlSeconds = ttl
108         self.data = dns_server
109
110
111 class SOARecord(dnsp.DnssrvRpcRecord):
112
113     def __init__(self, mname, rname, serial=1, refresh=900, retry=600,
114                  expire=86400, minimum=3600, ttl=3600, rank=dnsp.DNS_RANK_ZONE):
115         super(SOARecord, self).__init__()
116         self.wType = dnsp.DNS_TYPE_SOA
117         self.rank = rank
118         self.dwSerial = serial
119         self.dwTtlSeconds = ttl
120         soa = dnsp.soa()
121         soa.serial = serial
122         soa.refresh = refresh
123         soa.retry = retry
124         soa.expire = expire
125         soa.mname = mname
126         soa.rname = rname
127         self.data = soa
128
129
130 class SRVRecord(dnsp.DnssrvRpcRecord):
131
132     def __init__(self, target, port, priority=0, weight=100, serial=1, ttl=900,
133                 rank=dnsp.DNS_RANK_ZONE):
134         super(SRVRecord, self).__init__()
135         self.wType = dnsp.DNS_TYPE_SRV
136         self.rank = rank
137         self.dwSerial = serial
138         self.dwTtlSeconds = ttl
139         srv = dnsp.srv()
140         srv.nameTarget = target
141         srv.wPort = port
142         srv.wPriority = priority
143         srv.wWeight = weight
144         self.data = srv
145
146
147 class TXTRecord(dnsp.DnssrvRpcRecord):
148
149     def __init__(self, slist, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE):
150         super(TXTRecord, self).__init__()
151         self.wType = dnsp.DNS_TYPE_TXT
152         self.rank = rank
153         self.dwSerial = serial
154         self.dwTtlSeconds = ttl
155         stringlist = dnsp.string_list()
156         stringlist.count = len(slist)
157         stringlist.str = slist
158         self.data = stringlist
159
160
161 class TypeProperty(dnsp.DnsProperty):
162
163     def __init__(self, zone_type=dnsp.DNS_ZONE_TYPE_PRIMARY):
164         super(TypeProperty, self).__init__()
165         self.wDataLength = 1
166         self.version = 1
167         self.id = dnsp.DSPROPERTY_ZONE_TYPE
168         self.data = zone_type
169
170
171 class AllowUpdateProperty(dnsp.DnsProperty):
172
173     def __init__(self, allow_update=dnsp.DNS_ZONE_UPDATE_SECURE):
174         super(AllowUpdateProperty, self).__init__()
175         self.wDataLength = 1
176         self.version = 1
177         self.id = dnsp.DSPROPERTY_ZONE_ALLOW_UPDATE
178         self.data = allow_update
179
180
181 class SecureTimeProperty(dnsp.DnsProperty):
182
183     def __init__(self, secure_time=0):
184         super(SecureTimeProperty, self).__init__()
185         self.wDataLength = 1
186         self.version = 1
187         self.id = dnsp.DSPROPERTY_ZONE_SECURE_TIME
188         self.data = secure_time
189
190
191 class NorefreshIntervalProperty(dnsp.DnsProperty):
192
193     def __init__(self, norefresh_interval=0):
194         super(NorefreshIntervalProperty, self).__init__()
195         self.wDataLength = 1
196         self.version = 1
197         self.id = dnsp.DSPROPERTY_ZONE_NOREFRESH_INTERVAL
198         self.data = norefresh_interval
199
200
201 class RefreshIntervalProperty(dnsp.DnsProperty):
202
203     def __init__(self, refresh_interval=0):
204         super(RefreshIntervalProperty, self).__init__()
205         self.wDataLength = 1
206         self.version = 1
207         self.id = dnsp.DSPROPERTY_ZONE_REFRESH_INTERVAL
208         self.data = refresh_interval
209
210
211 class AgingStateProperty(dnsp.DnsProperty):
212
213     def __init__(self, aging_enabled=0):
214         super(AgingStateProperty, self).__init__()
215         self.wDataLength = 1
216         self.version = 1
217         self.id = dnsp.DSPROPERTY_ZONE_AGING_STATE
218         self.data = aging_enabled
219
220
221 class AgingEnabledTimeProperty(dnsp.DnsProperty):
222
223     def __init__(self, next_cycle_hours=0):
224         super(AgingEnabledTimeProperty, self).__init__()
225         self.wDataLength = 1
226         self.version = 1;
227         self.id = dnsp.DSPROPERTY_ZONE_AGING_ENABLED_TIME
228         self.data = next_cycle_hours
229
230
231 def setup_dns_partitions(samdb, domainsid, domaindn, forestdn, configdn,
232         serverdn):
233     domainzone_dn = "DC=DomainDnsZones,%s" % domaindn
234     forestzone_dn = "DC=ForestDnsZones,%s" % forestdn
235     descriptor = get_dns_partition_descriptor(domainsid)
236     setup_add_ldif(samdb, setup_path("provision_dnszones_partitions.ldif"), {
237         "DOMAINZONE_DN": domainzone_dn,
238         "FORESTZONE_DN": forestzone_dn,
239         "SECDESC"      : b64encode(descriptor)
240         })
241
242     domainzone_guid = get_domainguid(samdb, domainzone_dn)
243     forestzone_guid = get_domainguid(samdb, forestzone_dn)
244
245     domainzone_guid = str(uuid.uuid4())
246     forestzone_guid = str(uuid.uuid4())
247
248     domainzone_dns = ldb.Dn(samdb, domainzone_dn).canonical_ex_str().strip()
249     forestzone_dns = ldb.Dn(samdb, forestzone_dn).canonical_ex_str().strip()
250
251     protected1_desc = get_domain_delete_protected1_descriptor(domainsid)
252     protected2_desc = get_domain_delete_protected2_descriptor(domainsid)
253     setup_add_ldif(samdb, setup_path("provision_dnszones_add.ldif"), {
254         "DOMAINZONE_DN": domainzone_dn,
255         "FORESTZONE_DN": forestzone_dn,
256         "DOMAINZONE_GUID": domainzone_guid,
257         "FORESTZONE_GUID": forestzone_guid,
258         "DOMAINZONE_DNS": domainzone_dns,
259         "FORESTZONE_DNS": forestzone_dns,
260         "CONFIGDN": configdn,
261         "SERVERDN": serverdn,
262         "LOSTANDFOUND_DESCRIPTOR": b64encode(protected2_desc),
263         "INFRASTRUCTURE_DESCRIPTOR": b64encode(protected1_desc),
264         })
265
266     setup_modify_ldif(samdb, setup_path("provision_dnszones_modify.ldif"), {
267         "CONFIGDN": configdn,
268         "SERVERDN": serverdn,
269         "DOMAINZONE_DN": domainzone_dn,
270         "FORESTZONE_DN": forestzone_dn,
271     })
272
273
274 def add_dns_accounts(samdb, domaindn):
275     setup_add_ldif(samdb, setup_path("provision_dns_accounts_add.ldif"), {
276         "DOMAINDN": domaindn,
277         })
278
279
280 def add_dns_container(samdb, domaindn, prefix, domain_sid, dnsadmins_sid, forest=False):
281     name_map = {'DnsAdmins': str(dnsadmins_sid)}
282     if forest is True:
283         sd_val = get_dns_forest_microsoft_dns_descriptor(domain_sid,
284                                                          name_map=name_map)
285     else:
286         sd_val = get_dns_domain_microsoft_dns_descriptor(domain_sid,
287                                                          name_map=name_map)
288     # CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
289     msg = ldb.Message(ldb.Dn(samdb, "CN=MicrosoftDNS,%s,%s" % (prefix, domaindn)))
290     msg["objectClass"] = ["top", "container"]
291     msg["nTSecurityDescriptor"] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_ADD,
292         "nTSecurityDescriptor")
293     samdb.add(msg)
294
295
296 def add_rootservers(samdb, domaindn, prefix):
297     rootservers = {}
298     rootservers["a.root-servers.net"] = "198.41.0.4"
299     rootservers["b.root-servers.net"] = "192.228.79.201"
300     rootservers["c.root-servers.net"] = "192.33.4.12"
301     rootservers["d.root-servers.net"] = "128.8.10.90"
302     rootservers["e.root-servers.net"] = "192.203.230.10"
303     rootservers["f.root-servers.net"] = "192.5.5.241"
304     rootservers["g.root-servers.net"] = "192.112.36.4"
305     rootservers["h.root-servers.net"] = "128.63.2.53"
306     rootservers["i.root-servers.net"] = "192.36.148.17"
307     rootservers["j.root-servers.net"] = "192.58.128.30"
308     rootservers["k.root-servers.net"] = "193.0.14.129"
309     rootservers["l.root-servers.net"] = "199.7.83.42"
310     rootservers["m.root-servers.net"] = "202.12.27.33"
311
312     rootservers_v6 = {}
313     rootservers_v6["a.root-servers.net"] = "2001:503:ba3e::2:30"
314     rootservers_v6["f.root-servers.net"] = "2001:500:2f::f"
315     rootservers_v6["h.root-servers.net"] = "2001:500:1::803f:235"
316     rootservers_v6["j.root-servers.net"] = "2001:503:c27::2:30"
317     rootservers_v6["k.root-servers.net"] = "2001:7fd::1"
318     rootservers_v6["m.root-servers.net"] = "2001:dc3::35"
319
320     container_dn = "DC=RootDNSServers,CN=MicrosoftDNS,%s,%s" % (prefix, domaindn)
321
322     # Add DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
323     msg = ldb.Message(ldb.Dn(samdb, container_dn))
324     props = []
325     props.append(ndr_pack(TypeProperty(zone_type=dnsp.DNS_ZONE_TYPE_CACHE)))
326     props.append(ndr_pack(AllowUpdateProperty(allow_update=dnsp.DNS_ZONE_UPDATE_OFF)))
327     props.append(ndr_pack(SecureTimeProperty()))
328     props.append(ndr_pack(NorefreshIntervalProperty()))
329     props.append(ndr_pack(RefreshIntervalProperty()))
330     props.append(ndr_pack(AgingStateProperty()))
331     props.append(ndr_pack(AgingEnabledTimeProperty()))
332     msg["objectClass"] = ["top", "dnsZone"]
333     msg["cn"] = ldb.MessageElement("Zone", ldb.FLAG_MOD_ADD, "cn")
334     msg["dNSProperty"] = ldb.MessageElement(props, ldb.FLAG_MOD_ADD, "dNSProperty")
335     samdb.add(msg)
336
337     # Add DC=@,DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
338     record = []
339     for rserver in rootservers:
340         record.append(ndr_pack(NSRecord(rserver, serial=0, ttl=0, rank=dnsp.DNS_RANK_ROOT_HINT)))
341
342     msg = ldb.Message(ldb.Dn(samdb, "DC=@,%s" % container_dn))
343     msg["objectClass"] = ["top", "dnsNode"]
344     msg["dnsRecord"] = ldb.MessageElement(record, ldb.FLAG_MOD_ADD, "dnsRecord")
345     samdb.add(msg)
346
347     # Add DC=<rootserver>,DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
348     for rserver in rootservers:
349         record = [ndr_pack(ARecord(rootservers[rserver], serial=0, ttl=0, rank=dnsp.DNS_RANK_ROOT_HINT))]
350         # Add AAAA record as well (How does W2K* add IPv6 records?)
351         #if rserver in rootservers_v6:
352         #    record.append(ndr_pack(AAAARecord(rootservers_v6[rserver], serial=0, ttl=0)))
353         msg = ldb.Message(ldb.Dn(samdb, "DC=%s,%s" % (rserver, container_dn)))
354         msg["objectClass"] = ["top", "dnsNode"]
355         msg["dnsRecord"] = ldb.MessageElement(record, ldb.FLAG_MOD_ADD, "dnsRecord")
356         samdb.add(msg)
357
358 def add_at_record(samdb, container_dn, prefix, hostname, dnsdomain, hostip, hostip6):
359
360     fqdn_hostname = "%s.%s" % (hostname, dnsdomain)
361
362     at_records = []
363
364     # SOA record
365     at_soa_record = SOARecord(fqdn_hostname, "hostmaster.%s" % dnsdomain)
366     at_records.append(ndr_pack(at_soa_record))
367
368     # NS record
369     at_ns_record = NSRecord(fqdn_hostname)
370     at_records.append(ndr_pack(at_ns_record))
371
372     if hostip is not None:
373         # A record
374         at_a_record = ARecord(hostip)
375         at_records.append(ndr_pack(at_a_record))
376
377     if hostip6 is not None:
378         # AAAA record
379         at_aaaa_record = AAAARecord(hostip6)
380         at_records.append(ndr_pack(at_aaaa_record))
381
382     msg = ldb.Message(ldb.Dn(samdb, "DC=@,%s" % container_dn))
383     msg["objectClass"] = ["top", "dnsNode"]
384     msg["dnsRecord"] = ldb.MessageElement(at_records, ldb.FLAG_MOD_ADD, "dnsRecord")
385     samdb.add(msg)
386
387
388 def add_srv_record(samdb, container_dn, prefix, host, port):
389     srv_record = SRVRecord(host, port)
390     msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
391     msg["objectClass"] = ["top", "dnsNode"]
392     msg["dnsRecord"] = ldb.MessageElement(ndr_pack(srv_record), ldb.FLAG_MOD_ADD, "dnsRecord")
393     samdb.add(msg)
394
395
396 def add_ns_record(samdb, container_dn, prefix, host):
397     ns_record = NSRecord(host)
398     msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
399     msg["objectClass"] = ["top", "dnsNode"]
400     msg["dnsRecord"] = ldb.MessageElement(ndr_pack(ns_record), ldb.FLAG_MOD_ADD, "dnsRecord")
401     samdb.add(msg)
402
403
404 def add_ns_glue_record(samdb, container_dn, prefix, host):
405     ns_record = NSRecord(host, rank=dnsp.DNS_RANK_NS_GLUE)
406     msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
407     msg["objectClass"] = ["top", "dnsNode"]
408     msg["dnsRecord"] = ldb.MessageElement(ndr_pack(ns_record), ldb.FLAG_MOD_ADD, "dnsRecord")
409     samdb.add(msg)
410
411
412 def add_cname_record(samdb, container_dn, prefix, host):
413     cname_record = CNameRecord(host)
414     msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
415     msg["objectClass"] = ["top", "dnsNode"]
416     msg["dnsRecord"] = ldb.MessageElement(ndr_pack(cname_record), ldb.FLAG_MOD_ADD, "dnsRecord")
417     samdb.add(msg)
418
419
420 def add_host_record(samdb, container_dn, prefix, hostip, hostip6):
421     host_records = []
422     if hostip:
423         a_record = ARecord(hostip)
424         host_records.append(ndr_pack(a_record))
425     if hostip6:
426         aaaa_record = AAAARecord(hostip6)
427         host_records.append(ndr_pack(aaaa_record))
428     if host_records:
429         msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
430         msg["objectClass"] = ["top", "dnsNode"]
431         msg["dnsRecord"] = ldb.MessageElement(host_records, ldb.FLAG_MOD_ADD, "dnsRecord")
432         samdb.add(msg)
433
434
435 def add_domain_record(samdb, domaindn, prefix, dnsdomain, domainsid, dnsadmins_sid):
436     # DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
437     sddl = "O:SYG:BAD:AI" \
438     "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)" \
439     "(A;;CC;;;AU)" \
440     "(A;;RPLCLORC;;;WD)" \
441     "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
442     "(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)" \
443     "(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;%s)" \
444     "(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)" \
445     "(OA;CIID;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
446     "(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
447     "(A;CIID;LC;;;RU)" \
448     "(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
449     "S:AI" % dnsadmins_sid
450     sec = security.descriptor.from_sddl(sddl, domainsid)
451     props = []
452     props.append(ndr_pack(TypeProperty()))
453     props.append(ndr_pack(AllowUpdateProperty()))
454     props.append(ndr_pack(SecureTimeProperty()))
455     props.append(ndr_pack(NorefreshIntervalProperty(norefresh_interval=168)))
456     props.append(ndr_pack(RefreshIntervalProperty(refresh_interval=168)))
457     props.append(ndr_pack(AgingStateProperty()))
458     props.append(ndr_pack(AgingEnabledTimeProperty()))
459     msg = ldb.Message(ldb.Dn(samdb, "DC=%s,CN=MicrosoftDNS,%s,%s" % (dnsdomain, prefix, domaindn)))
460     msg["objectClass"] = ["top", "dnsZone"]
461     msg["ntSecurityDescriptor"] = ldb.MessageElement(ndr_pack(sec), ldb.FLAG_MOD_ADD,
462         "nTSecurityDescriptor")
463     msg["dNSProperty"] = ldb.MessageElement(props, ldb.FLAG_MOD_ADD, "dNSProperty")
464     samdb.add(msg)
465
466
467 def add_msdcs_record(samdb, forestdn, prefix, dnsforest):
468     # DC=_msdcs.<DNSFOREST>,CN=MicrosoftDNS,<PREFIX>,<FORESTDN>
469     msg = ldb.Message(ldb.Dn(samdb, "DC=_msdcs.%s,CN=MicrosoftDNS,%s,%s" %
470                                     (dnsforest, prefix, forestdn)))
471     msg["objectClass"] = ["top", "dnsZone"]
472     samdb.add(msg)
473
474
475 def add_dc_domain_records(samdb, domaindn, prefix, site, dnsdomain, hostname,
476         hostip, hostip6):
477
478     fqdn_hostname = "%s.%s" % (hostname, dnsdomain)
479
480     # Set up domain container - DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
481     domain_container_dn = ldb.Dn(samdb, "DC=%s,CN=MicrosoftDNS,%s,%s" %
482                                     (dnsdomain, prefix, domaindn))
483
484     # DC=@ record
485     add_at_record(samdb, domain_container_dn, "DC=@", hostname, dnsdomain,
486             hostip, hostip6)
487
488     # DC=<HOSTNAME> record
489     add_host_record(samdb, domain_container_dn, "DC=%s" % hostname, hostip,
490             hostip6)
491
492     # DC=_kerberos._tcp record
493     add_srv_record(samdb, domain_container_dn, "DC=_kerberos._tcp",
494             fqdn_hostname, 88)
495
496     # DC=_kerberos._tcp.<SITENAME>._sites record
497     add_srv_record(samdb, domain_container_dn, "DC=_kerberos._tcp.%s._sites" %
498             site, fqdn_hostname, 88)
499
500     # DC=_kerberos._udp record
501     add_srv_record(samdb, domain_container_dn, "DC=_kerberos._udp",
502             fqdn_hostname, 88)
503
504     # DC=_kpasswd._tcp record
505     add_srv_record(samdb, domain_container_dn, "DC=_kpasswd._tcp",
506             fqdn_hostname, 464)
507
508     # DC=_kpasswd._udp record
509     add_srv_record(samdb, domain_container_dn, "DC=_kpasswd._udp",
510             fqdn_hostname, 464)
511
512     # DC=_ldap._tcp record
513     add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp", fqdn_hostname,
514             389)
515
516     # DC=_ldap._tcp.<SITENAME>._sites record
517     add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp.%s._sites" %
518             site, fqdn_hostname, 389)
519
520     # FIXME: The number of SRV records depend on the various roles this DC has.
521     #        _gc and _msdcs records are added if the we are the forest dc and not subdomain dc
522     #
523     # Assumption: current DC is GC and add all the entries
524
525     # DC=_gc._tcp record
526     add_srv_record(samdb, domain_container_dn, "DC=_gc._tcp", fqdn_hostname,
527             3268)
528
529     # DC=_gc._tcp.<SITENAME>,_sites record
530     add_srv_record(samdb, domain_container_dn, "DC=_gc._tcp.%s._sites" % site,
531             fqdn_hostname, 3268)
532
533     # DC=_msdcs record
534     add_ns_glue_record(samdb, domain_container_dn, "DC=_msdcs", fqdn_hostname)
535
536     # FIXME: Following entries are added only if DomainDnsZones and ForestDnsZones partitions
537     #        are created
538     #
539     # Assumption: Additional entries won't hurt on os_level = 2000
540
541     # DC=_ldap._tcp.<SITENAME>._sites.DomainDnsZones
542     add_srv_record(samdb, domain_container_dn,
543             "DC=_ldap._tcp.%s._sites.DomainDnsZones" % site, fqdn_hostname,
544             389)
545
546     # DC=_ldap._tcp.<SITENAME>._sites.ForestDnsZones
547     add_srv_record(samdb, domain_container_dn,
548             "DC=_ldap._tcp.%s._sites.ForestDnsZones" % site, fqdn_hostname,
549             389)
550
551     # DC=_ldap._tcp.DomainDnsZones
552     add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp.DomainDnsZones",
553                     fqdn_hostname, 389)
554
555     # DC=_ldap._tcp.ForestDnsZones
556     add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp.ForestDnsZones",
557                     fqdn_hostname, 389)
558
559     # DC=DomainDnsZones
560     add_host_record(samdb, domain_container_dn, "DC=DomainDnsZones", hostip,
561             hostip6)
562
563     # DC=ForestDnsZones
564     add_host_record(samdb, domain_container_dn, "DC=ForestDnsZones", hostip,
565             hostip6)
566
567
568 def add_dc_msdcs_records(samdb, forestdn, prefix, site, dnsforest, hostname,
569                             hostip, hostip6, domainguid, ntdsguid):
570
571     fqdn_hostname = "%s.%s" % (hostname, dnsforest)
572
573     # Set up forest container - DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
574     forest_container_dn = ldb.Dn(samdb, "DC=_msdcs.%s,CN=MicrosoftDNS,%s,%s" %
575                                     (dnsforest, prefix, forestdn))
576
577     # DC=@ record
578     add_at_record(samdb, forest_container_dn, "DC=@", hostname, dnsforest,
579             None, None)
580
581     # DC=_kerberos._tcp.dc record
582     add_srv_record(samdb, forest_container_dn, "DC=_kerberos._tcp.dc",
583             fqdn_hostname, 88)
584
585     # DC=_kerberos._tcp.<SITENAME>._sites.dc record
586     add_srv_record(samdb, forest_container_dn,
587             "DC=_kerberos._tcp.%s._sites.dc" % site, fqdn_hostname, 88)
588
589     # DC=_ldap._tcp.dc record
590     add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.dc",
591             fqdn_hostname, 389)
592
593     # DC=_ldap._tcp.<SITENAME>._sites.dc record
594     add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.%s._sites.dc" %
595             site, fqdn_hostname, 389)
596
597     # DC=_ldap._tcp.<SITENAME>._sites.gc record
598     add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.%s._sites.gc" %
599             site, fqdn_hostname, 3268)
600
601     # DC=_ldap._tcp.gc record
602     add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.gc",
603             fqdn_hostname, 3268)
604
605     # DC=_ldap._tcp.pdc record
606     add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.pdc",
607             fqdn_hostname, 389)
608
609     # DC=gc record
610     add_host_record(samdb, forest_container_dn, "DC=gc", hostip, hostip6)
611
612     # DC=_ldap._tcp.<DOMAINGUID>.domains record
613     add_srv_record(samdb, forest_container_dn,
614             "DC=_ldap._tcp.%s.domains" % domainguid, fqdn_hostname, 389)
615
616     # DC=<NTDSGUID>
617     add_cname_record(samdb, forest_container_dn, "DC=%s" % ntdsguid,
618             fqdn_hostname)
619
620
621 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
622                         dnsdomain, dns_keytab_path, dnspass):
623     """Add DNS specific bits to a secrets database.
624
625     :param secretsdb: Ldb Handle to the secrets database
626     :param names: Names shortcut
627     :param machinepass: Machine password
628     """
629     try:
630         os.unlink(os.path.join(private_dir, dns_keytab_path))
631     except OSError:
632         pass
633
634     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
635             "REALM": realm,
636             "DNSDOMAIN": dnsdomain,
637             "DNS_KEYTAB": dns_keytab_path,
638             "DNSPASS_B64": b64encode(dnspass),
639             "HOSTNAME": names.hostname,
640             "DNSNAME" : '%s.%s' % (
641                 names.netbiosname.lower(), names.dnsdomain.lower())
642             })
643
644
645 def create_dns_dir(logger, paths):
646     """Write out a DNS zone file, from the info in the current database.
647
648     :param logger: Logger object
649     :param paths: paths object
650     """
651     dns_dir = os.path.dirname(paths.dns)
652
653     try:
654         shutil.rmtree(dns_dir, True)
655     except OSError:
656         pass
657
658     os.mkdir(dns_dir, 0770)
659
660     if paths.bind_gid is not None:
661         try:
662             os.chown(dns_dir, -1, paths.bind_gid)
663             # chmod needed to cope with umask
664             os.chmod(dns_dir, 0770)
665         except OSError:
666             if not os.environ.has_key('SAMBA_SELFTEST'):
667                 logger.error("Failed to chown %s to bind gid %u" % (
668                     dns_dir, paths.bind_gid))
669
670
671 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
672                      hostip, hostip6, hostname, realm, domainguid,
673                      ntdsguid, site):
674     """Write out a DNS zone file, from the info in the current database.
675
676     :param paths: paths object
677     :param dnsdomain: DNS Domain name
678     :param domaindn: DN of the Domain
679     :param hostip: Local IPv4 IP
680     :param hostip6: Local IPv6 IP
681     :param hostname: Local hostname
682     :param realm: Realm name
683     :param domainguid: GUID of the domain.
684     :param ntdsguid: GUID of the hosts nTDSDSA record.
685     """
686     assert isinstance(domainguid, str)
687
688     if hostip6 is not None:
689         hostip6_base_line = "            IN AAAA    " + hostip6
690         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
691         gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
692     else:
693         hostip6_base_line = ""
694         hostip6_host_line = ""
695         gc_msdcs_ip6_line = ""
696
697     if hostip is not None:
698         hostip_base_line = "            IN A    " + hostip
699         hostip_host_line = hostname + "        IN A    " + hostip
700         gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
701     else:
702         hostip_base_line = ""
703         hostip_host_line = ""
704         gc_msdcs_ip_line = ""
705
706     # we need to freeze the zone while we update the contents
707     if targetdir is None:
708         rndc = ' '.join(lp.get("rndc command"))
709         os.system(rndc + " freeze " + lp.get("realm"))
710
711     setup_file(setup_path("provision.zone"), paths.dns, {
712             "HOSTNAME": hostname,
713             "DNSDOMAIN": dnsdomain,
714             "REALM": realm,
715             "HOSTIP_BASE_LINE": hostip_base_line,
716             "HOSTIP_HOST_LINE": hostip_host_line,
717             "DOMAINGUID": domainguid,
718             "DATESTRING": time.strftime("%Y%m%d%H"),
719             "DEFAULTSITE": site,
720             "NTDSGUID": ntdsguid,
721             "HOSTIP6_BASE_LINE": hostip6_base_line,
722             "HOSTIP6_HOST_LINE": hostip6_host_line,
723             "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
724             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
725         })
726
727     if paths.bind_gid is not None:
728         try:
729             os.chown(paths.dns, -1, paths.bind_gid)
730             # chmod needed to cope with umask
731             os.chmod(paths.dns, 0664)
732         except OSError:
733             if not os.environ.has_key('SAMBA_SELFTEST'):
734                 logger.error("Failed to chown %s to bind gid %u" % (
735                     paths.dns, paths.bind_gid))
736
737     if targetdir is None:
738         os.system(rndc + " unfreeze " + lp.get("realm"))
739
740
741 def tdb_copy(logger, file1, file2):
742     """Copy tdb file using tdbbackup utility and rename it
743     """
744     # Find the location of tdbbackup tool
745     dirs = ["bin", samba.param.bin_dir()] + os.getenv('PATH').split(os.pathsep)
746     for d in dirs:
747         toolpath = os.path.join(d, "tdbbackup")
748         if os.path.exists(toolpath):
749             break
750     status = os.system("%s -s '.dns' %s" % (toolpath, file1))
751     if status == 0:
752         os.rename("%s.dns" % file1, file2)
753     else:
754         raise Exception("Error copying %s" % file1)
755
756
757 def create_samdb_copy(samdb, logger, paths, names, domainsid, domainguid):
758     """Create a copy of samdb and give write permissions to named for dns partitions
759     """
760     private_dir = paths.private_dir
761     samldb_dir = os.path.join(private_dir, "sam.ldb.d")
762     dns_dir = os.path.dirname(paths.dns)
763     dns_samldb_dir = os.path.join(dns_dir, "sam.ldb.d")
764
765     # Find the partitions and corresponding filenames
766     partfile = {}
767     res = samdb.search(base="@PARTITION", scope=ldb.SCOPE_BASE, attrs=["partition"])
768     for tmp in res[0]["partition"]:
769         (nc, fname) = tmp.split(':')
770         partfile[nc.upper()] = fname
771
772     # Create empty domain partition
773     domaindn = names.domaindn.upper()
774     domainpart_file = os.path.join(dns_dir, partfile[domaindn])
775     try:
776         os.mkdir(dns_samldb_dir)
777         file(domainpart_file, 'w').close()
778
779         # Fill the basedn and @OPTION records in domain partition
780         dom_ldb = samba.Ldb(domainpart_file)
781         domainguid_line = "objectGUID: %s\n-" % domainguid
782         descr = b64encode(get_domain_descriptor(domainsid))
783         setup_add_ldif(dom_ldb, setup_path("provision_basedn.ldif"), {
784             "DOMAINDN" : names.domaindn,
785             "DOMAINGUID" : domainguid_line,
786             "DOMAINSID" : str(domainsid),
787             "DESCRIPTOR" : descr})
788         setup_add_ldif(dom_ldb,
789             setup_path("provision_basedn_options.ldif"), None)
790     except:
791         logger.error(
792             "Failed to setup database for BIND, AD based DNS cannot be used")
793         raise
794     del partfile[domaindn]
795
796     # Link dns partitions and metadata
797     domainzonedn = "DC=DOMAINDNSZONES,%s" % names.domaindn.upper()
798     forestzonedn = "DC=FORESTDNSZONES,%s" % names.rootdn.upper()
799     domainzone_file = partfile[domainzonedn]
800     forestzone_file = partfile[forestzonedn]
801     metadata_file = "metadata.tdb"
802     try:
803         os.link(os.path.join(samldb_dir, metadata_file),
804             os.path.join(dns_samldb_dir, metadata_file))
805         os.link(os.path.join(private_dir, domainzone_file),
806             os.path.join(dns_dir, domainzone_file))
807         os.link(os.path.join(private_dir, forestzone_file),
808             os.path.join(dns_dir, forestzone_file))
809     except OSError:
810         logger.error(
811             "Failed to setup database for BIND, AD based DNS cannot be used")
812         raise
813     del partfile[domainzonedn]
814     del partfile[forestzonedn]
815
816     # Copy root, config, schema partitions (and any other if any)
817     # Since samdb is open in the current process, copy them in a child process
818     try:
819         tdb_copy(logger,
820                  os.path.join(private_dir, "sam.ldb"),
821                  os.path.join(dns_dir, "sam.ldb"))
822         for nc in partfile:
823             pfile = partfile[nc]
824             tdb_copy(logger,
825                      os.path.join(private_dir, pfile),
826                      os.path.join(dns_dir, pfile))
827     except:
828         logger.error(
829             "Failed to setup database for BIND, AD based DNS cannot be used")
830         raise
831
832     # Give bind read/write permissions dns partitions
833     if paths.bind_gid is not None:
834         try:
835             os.chown(samldb_dir, -1, paths.bind_gid)
836             os.chmod(samldb_dir, 0750)
837
838             for dirname, dirs, files in os.walk(dns_dir):
839                 for d in dirs:
840                     dpath = os.path.join(dirname, d)
841                     os.chown(dpath, -1, paths.bind_gid)
842                     os.chmod(dpath, 0770)
843                 for f in files:
844                     if f.endswith('.ldb') or f.endswith('.tdb'):
845                         fpath = os.path.join(dirname, f)
846                         os.chown(fpath, -1, paths.bind_gid)
847                         os.chmod(fpath, 0660)
848         except OSError:
849             if not os.environ.has_key('SAMBA_SELFTEST'):
850                 logger.error(
851                     "Failed to set permissions to sam.ldb* files, fix manually")
852     else:
853         if not os.environ.has_key('SAMBA_SELFTEST'):
854             logger.warning("""Unable to find group id for BIND,
855                 set permissions to sam.ldb* files manually""")
856
857
858 def create_dns_update_list(lp, logger, paths):
859     """Write out a dns_update_list file"""
860     # note that we use no variable substitution on this file
861     # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
862     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
863     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
864
865
866 def create_named_conf(paths, realm, dnsdomain, dns_backend):
867     """Write out a file containing zone statements suitable for inclusion in a
868     named.conf file (including GSS-TSIG configuration).
869
870     :param paths: all paths
871     :param realm: Realm name
872     :param dnsdomain: DNS Domain name
873     :param dns_backend: DNS backend type
874     :param keytab_name: File name of DNS keytab file
875     """
876
877     if dns_backend == "BIND9_FLATFILE":
878         setup_file(setup_path("named.conf"), paths.namedconf, {
879                     "DNSDOMAIN": dnsdomain,
880                     "REALM": realm,
881                     "ZONE_FILE": paths.dns,
882                     "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
883                     "NAMED_CONF": paths.namedconf,
884                     "NAMED_CONF_UPDATE": paths.namedconf_update
885                     })
886
887         setup_file(setup_path("named.conf.update"), paths.namedconf_update)
888
889     elif dns_backend == "BIND9_DLZ":
890         setup_file(setup_path("named.conf.dlz"), paths.namedconf, {
891                     "NAMED_CONF": paths.namedconf,
892                     "MODULESDIR" : samba.param.modules_dir(),
893                     })
894
895
896 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
897     keytab_name):
898     """Write out a file containing zone statements suitable for inclusion in a
899     named.conf file (including GSS-TSIG configuration).
900
901     :param path: Path of the new named.conf file.
902     :param realm: Realm name
903     :param dnsdomain: DNS Domain name
904     :param private_dir: Path to private directory
905     :param keytab_name: File name of DNS keytab file
906     """
907     setup_file(setup_path("named.txt"), path, {
908             "DNSDOMAIN": dnsdomain,
909             "DNSNAME" : dnsname,
910             "REALM": realm,
911             "DNS_KEYTAB": keytab_name,
912             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
913             "PRIVATE_DIR": private_dir
914         })
915
916
917 def is_valid_dns_backend(dns_backend):
918     return dns_backend in ("BIND9_FLATFILE", "BIND9_DLZ", "SAMBA_INTERNAL", "NONE")
919
920
921 def is_valid_os_level(os_level):
922     return DS_DOMAIN_FUNCTION_2000 <= os_level <= DS_DOMAIN_FUNCTION_2008_R2
923
924
925 def create_dns_legacy(samdb, domainsid, forestdn, dnsadmins_sid):
926     # Set up MicrosoftDNS container
927     add_dns_container(samdb, forestdn, "CN=System", domainsid, dnsadmins_sid)
928     # Add root servers
929     add_rootservers(samdb, forestdn, "CN=System")
930
931
932 def fill_dns_data_legacy(samdb, domainsid, forestdn, dnsdomain, site, hostname,
933                          hostip, hostip6, dnsadmins_sid):
934     # Add domain record
935     add_domain_record(samdb, forestdn, "CN=System", dnsdomain, domainsid,
936                       dnsadmins_sid)
937
938     # Add DNS records for a DC in domain
939     add_dc_domain_records(samdb, forestdn, "CN=System", site, dnsdomain,
940                           hostname, hostip, hostip6)
941
942
943 def create_dns_partitions(samdb, domainsid, names, domaindn, forestdn,
944                           dnsadmins_sid):
945     # Set up additional partitions (DomainDnsZones, ForstDnsZones)
946     setup_dns_partitions(samdb, domainsid, domaindn, forestdn,
947                         names.configdn, names.serverdn)
948
949     # Set up MicrosoftDNS containers
950     add_dns_container(samdb, domaindn, "DC=DomainDnsZones", domainsid,
951                       dnsadmins_sid)
952     add_dns_container(samdb, forestdn, "DC=ForestDnsZones", domainsid,
953                       dnsadmins_sid, forest=True)
954
955
956 def fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn,
957                             dnsdomain, dnsforest, hostname, hostip, hostip6,
958                             domainguid, ntdsguid, dnsadmins_sid, autofill=True):
959     """Fill data in various AD partitions
960
961     :param samdb: LDB object connected to sam.ldb file
962     :param domainsid: Domain SID (as dom_sid object)
963     :param site: Site name to create hostnames in
964     :param domaindn: DN of the domain
965     :param forestdn: DN of the forest
966     :param dnsdomain: DNS name of the domain
967     :param dnsforest: DNS name of the forest
968     :param hostname: Host name of this DC
969     :param hostip: IPv4 addresses
970     :param hostip6: IPv6 addresses
971     :param domainguid: Domain GUID
972     :param ntdsguid: NTDS GUID
973     :param dnsadmins_sid: SID for DnsAdmins group
974     :param autofill: Create DNS records (using fixed template)
975     """
976
977     ##### Set up DC=DomainDnsZones,<DOMAINDN>
978     # Add rootserver records
979     add_rootservers(samdb, domaindn, "DC=DomainDnsZones")
980
981     # Add domain record
982     add_domain_record(samdb, domaindn, "DC=DomainDnsZones", dnsdomain,
983                       domainsid, dnsadmins_sid)
984
985     # Add DNS records for a DC in domain
986     if autofill:
987         add_dc_domain_records(samdb, domaindn, "DC=DomainDnsZones", site,
988                               dnsdomain, hostname, hostip, hostip6)
989
990     ##### Set up DC=ForestDnsZones,<DOMAINDN>
991     # Add _msdcs record
992     add_msdcs_record(samdb, forestdn, "DC=ForestDnsZones", dnsforest)
993
994     # Add DNS records for a DC in forest
995     if autofill:
996         add_dc_msdcs_records(samdb, forestdn, "DC=ForestDnsZones", site,
997                              dnsforest, hostname, hostip, hostip6,
998                              domainguid, ntdsguid)
999
1000
1001 def setup_ad_dns(samdb, secretsdb, domainsid, names, paths, lp, logger,
1002         dns_backend, os_level, site, dnspass=None, hostip=None, hostip6=None,
1003         targetdir=None):
1004     """Provision DNS information (assuming GC role)
1005
1006     :param samdb: LDB object connected to sam.ldb file
1007     :param secretsdb: LDB object connected to secrets.ldb file
1008     :param domainsid: Domain SID (as dom_sid object)
1009     :param names: Names shortcut
1010     :param paths: Paths shortcut
1011     :param lp: Loadparm object
1012     :param logger: Logger object
1013     :param dns_backend: Type of DNS backend
1014     :param os_level: Functional level (treated as os level)
1015     :param site: Site to create hostnames in
1016     :param dnspass: Password for bind's DNS account
1017     :param hostip: IPv4 address
1018     :param hostip6: IPv6 address
1019     :param targetdir: Target directory for creating DNS-related files for BIND9
1020     """
1021
1022     if not is_valid_dns_backend(dns_backend):
1023         raise Exception("Invalid dns backend: %r" % dns_backend)
1024
1025     if not is_valid_os_level(os_level):
1026         raise Exception("Invalid os level: %r" % os_level)
1027
1028     if dns_backend == "NONE":
1029         logger.info("No DNS backend set, not configuring DNS")
1030         return
1031
1032     # Add dns accounts (DnsAdmins, DnsUpdateProxy) in domain
1033     logger.info("Adding DNS accounts")
1034     add_dns_accounts(samdb, names.domaindn)
1035
1036     # If dns_backend is BIND9_FLATFILE
1037     #   Populate only CN=MicrosoftDNS,CN=System,<FORESTDN>
1038     #
1039     # If dns_backend is SAMBA_INTERNAL or BIND9_DLZ
1040     #   Populate DNS partitions
1041
1042     # If os_level < 2003 (DS_DOMAIN_FUNCTION_2000)
1043     #   All dns records are in CN=MicrosoftDNS,CN=System,<FORESTDN>
1044     #
1045     # If os_level >= 2003 (DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008,
1046     #                        DS_DOMAIN_FUNCTION_2008_R2)
1047     #   Root server records are in CN=MicrosoftDNS,CN=System,<FORESTDN>
1048     #   Domain records are in CN=MicrosoftDNS,CN=System,<FORESTDN>
1049     #   Domain records are in CN=MicrosoftDNS,DC=DomainDnsZones,<DOMAINDN>
1050     #   Forest records are in CN=MicrosoftDNS,DC=ForestDnsZones,<FORESTDN>
1051     domaindn = names.domaindn
1052     forestdn = samdb.get_root_basedn().get_linearized()
1053
1054     dnsdomain = names.dnsdomain.lower()
1055     dnsforest = dnsdomain
1056
1057     hostname = names.netbiosname.lower()
1058
1059     dnsadmins_sid = get_dnsadmins_sid(samdb, domaindn)
1060     domainguid = get_domainguid(samdb, domaindn)
1061
1062     # Create CN=System
1063     logger.info("Creating CN=MicrosoftDNS,CN=System,%s" % forestdn)
1064     create_dns_legacy(samdb, domainsid, forestdn, dnsadmins_sid)
1065
1066     if os_level == DS_DOMAIN_FUNCTION_2000:
1067         # Populating legacy dns
1068         logger.info("Populating CN=MicrosoftDNS,CN=System,%s" % forestdn)
1069         fill_dns_data_legacy(samdb, domainsid, forestdn, dnsdomain, site,
1070                              hostname, hostip, hostip6, dnsadmins_sid)
1071
1072     elif dns_backend in ("SAMBA_INTERNAL", "BIND9_DLZ") and \
1073             os_level >= DS_DOMAIN_FUNCTION_2003:
1074
1075         # Create DNS partitions
1076         logger.info("Creating DomainDnsZones and ForestDnsZones partitions")
1077         create_dns_partitions(samdb, domainsid, names, domaindn, forestdn,
1078                               dnsadmins_sid)
1079
1080         # Populating dns partitions
1081         logger.info("Populating DomainDnsZones and ForestDnsZones partitions")
1082         fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn,
1083                                 dnsdomain, dnsforest, hostname, hostip, hostip6,
1084                                 domainguid, names.ntdsguid, dnsadmins_sid)
1085
1086     if dns_backend.startswith("BIND9_"):
1087         setup_bind9_dns(samdb, secretsdb, domainsid, names, paths, lp, logger,
1088             dns_backend, os_level, site=site, dnspass=dnspass, hostip=hostip,
1089             hostip6=hostip6, targetdir=targetdir)
1090
1091
1092 def setup_bind9_dns(samdb, secretsdb, domainsid, names, paths, lp, logger,
1093         dns_backend, os_level, site=None, dnspass=None, hostip=None,
1094         hostip6=None, targetdir=None):
1095     """Provision DNS information (assuming BIND9 backend in DC role)
1096
1097     :param samdb: LDB object connected to sam.ldb file
1098     :param secretsdb: LDB object connected to secrets.ldb file
1099     :param domainsid: Domain SID (as dom_sid object)
1100     :param names: Names shortcut
1101     :param paths: Paths shortcut
1102     :param lp: Loadparm object
1103     :param logger: Logger object
1104     :param dns_backend: Type of DNS backend
1105     :param os_level: Functional level (treated as os level)
1106     :param site: Site to create hostnames in
1107     :param dnspass: Password for bind's DNS account
1108     :param hostip: IPv4 address
1109     :param hostip6: IPv6 address
1110     :param targetdir: Target directory for creating DNS-related files for BIND9
1111     """
1112
1113     if (not is_valid_dns_backend(dns_backend) or
1114         not dns_backend.startswith("BIND9_")):
1115         raise Exception("Invalid dns backend: %r" % dns_backend)
1116
1117     if not is_valid_os_level(os_level):
1118         raise Exception("Invalid os level: %r" % os_level)
1119
1120     domaindn = names.domaindn
1121
1122     domainguid = get_domainguid(samdb, domaindn)
1123
1124     secretsdb_setup_dns(secretsdb, names,
1125                         paths.private_dir, realm=names.realm,
1126                         dnsdomain=names.dnsdomain,
1127                         dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1128
1129     create_dns_dir(logger, paths)
1130
1131     if dns_backend == "BIND9_FLATFILE":
1132         create_zone_file(lp, logger, paths, targetdir, site=site,
1133                          dnsdomain=names.dnsdomain, hostip=hostip,
1134                          hostip6=hostip6, hostname=names.hostname,
1135                          realm=names.realm, domainguid=domainguid,
1136                          ntdsguid=names.ntdsguid)
1137
1138     if dns_backend == "BIND9_DLZ" and os_level >= DS_DOMAIN_FUNCTION_2003:
1139         create_samdb_copy(samdb, logger, paths, names, domainsid, domainguid)
1140
1141     create_named_conf(paths, realm=names.realm,
1142                       dnsdomain=names.dnsdomain, dns_backend=dns_backend)
1143
1144     create_named_txt(paths.namedtxt,
1145                      realm=names.realm, dnsdomain=names.dnsdomain,
1146                      dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1147                      private_dir=paths.private_dir,
1148                      keytab_name=paths.dns_keytab)
1149     logger.info("See %s for an example configuration include file for BIND",
1150                 paths.namedconf)
1151     logger.info("and %s for further documentation required for secure DNS "
1152                 "updates", paths.namedtxt)