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