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