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