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