provision: Only calculate ForestDNSZone GUID if we need it
[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     del partfile[domaindn]
805
806     # Link dns partitions and metadata
807     domainzonedn = "DC=DOMAINDNSZONES,%s" % names.domaindn.upper()
808     forestzonedn = "DC=FORESTDNSZONES,%s" % names.rootdn.upper()
809     domainzone_file = partfile[domainzonedn]
810     forestzone_file = partfile[forestzonedn]
811     metadata_file = "metadata.tdb"
812     try:
813         os.link(os.path.join(samldb_dir, metadata_file),
814             os.path.join(dns_samldb_dir, metadata_file))
815         os.link(os.path.join(private_dir, domainzone_file),
816             os.path.join(dns_dir, domainzone_file))
817         os.link(os.path.join(private_dir, forestzone_file),
818             os.path.join(dns_dir, forestzone_file))
819     except OSError:
820         logger.error(
821             "Failed to setup database for BIND, AD based DNS cannot be used")
822         raise
823     del partfile[domainzonedn]
824     del partfile[forestzonedn]
825
826     # Copy root, config, schema partitions (and any other if any)
827     # Since samdb is open in the current process, copy them in a child process
828     try:
829         tdb_copy(os.path.join(private_dir, "sam.ldb"),
830                  os.path.join(dns_dir, "sam.ldb"))
831         for nc in partfile:
832             pfile = partfile[nc]
833             tdb_copy(os.path.join(private_dir, pfile),
834                      os.path.join(dns_dir, pfile))
835     except:
836         logger.error(
837             "Failed to setup database for BIND, AD based DNS cannot be used")
838         raise
839
840     # Give bind read/write permissions dns partitions
841     if paths.bind_gid is not None:
842         try:
843             os.chown(samldb_dir, -1, paths.bind_gid)
844             os.chmod(samldb_dir, 0750)
845
846             for dirname, dirs, files in os.walk(dns_dir):
847                 for d in dirs:
848                     dpath = os.path.join(dirname, d)
849                     os.chown(dpath, -1, paths.bind_gid)
850                     os.chmod(dpath, 0770)
851                 for f in files:
852                     if f.endswith('.ldb') or f.endswith('.tdb'):
853                         fpath = os.path.join(dirname, f)
854                         os.chown(fpath, -1, paths.bind_gid)
855                         os.chmod(fpath, 0660)
856         except OSError:
857             if not os.environ.has_key('SAMBA_SELFTEST'):
858                 logger.error(
859                     "Failed to set permissions to sam.ldb* files, fix manually")
860     else:
861         if not os.environ.has_key('SAMBA_SELFTEST'):
862             logger.warning("""Unable to find group id for BIND,
863                 set permissions to sam.ldb* files manually""")
864
865
866 def create_dns_update_list(lp, logger, paths):
867     """Write out a dns_update_list file"""
868     # note that we use no variable substitution on this file
869     # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
870     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
871     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
872
873
874 def create_named_conf(paths, realm, dnsdomain, dns_backend, logger):
875     """Write out a file containing zone statements suitable for inclusion in a
876     named.conf file (including GSS-TSIG configuration).
877
878     :param paths: all paths
879     :param realm: Realm name
880     :param dnsdomain: DNS Domain name
881     :param dns_backend: DNS backend type
882     :param keytab_name: File name of DNS keytab file
883     :param logger: Logger object
884     """
885
886     # TODO: This really should have been done as a top level import.
887     # It is done here to avoid a depencency loop.  That is, we move
888     # ProvisioningError to another file, and have all the provision
889     # scripts import it from there.
890
891     from samba.provision import ProvisioningError
892
893     if dns_backend == "BIND9_FLATFILE":
894         setup_file(setup_path("named.conf"), paths.namedconf, {
895                     "DNSDOMAIN": dnsdomain,
896                     "REALM": realm,
897                     "ZONE_FILE": paths.dns,
898                     "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
899                     "NAMED_CONF": paths.namedconf,
900                     "NAMED_CONF_UPDATE": paths.namedconf_update
901                     })
902
903         setup_file(setup_path("named.conf.update"), paths.namedconf_update)
904
905     elif dns_backend == "BIND9_DLZ":
906         bind_info = subprocess.Popen(['named -V'], shell=True,
907                                      stdout=subprocess.PIPE,
908                                      stderr=subprocess.STDOUT,
909                                      cwd='.').communicate()[0]
910         bind98 = '#'
911         bind99 = '#'
912         if bind_info.upper().find('BIND 9.8') != -1:
913             bind98 = ''
914         elif bind_info.upper().find('BIND 9.9') != -1:
915             bind99 = ''
916         elif bind_info.upper().find('BIND 9.7') != -1:
917             raise ProvisioningError("DLZ option incompatible with BIND 9.7.")
918         else:
919             logger.warning("BIND version unknown, please modify %s manually." % paths.namedconf)
920         setup_file(setup_path("named.conf.dlz"), paths.namedconf, {
921                     "NAMED_CONF": paths.namedconf,
922                     "MODULESDIR" : samba.param.modules_dir(),
923                     "BIND98" : bind98,
924                     "BIND99" : bind99
925                     })
926
927
928 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
929     keytab_name):
930     """Write out a file containing zone statements suitable for inclusion in a
931     named.conf file (including GSS-TSIG configuration).
932
933     :param path: Path of the new named.conf file.
934     :param realm: Realm name
935     :param dnsdomain: DNS Domain name
936     :param private_dir: Path to private directory
937     :param keytab_name: File name of DNS keytab file
938     """
939     setup_file(setup_path("named.txt"), path, {
940             "DNSDOMAIN": dnsdomain,
941             "DNSNAME" : dnsname,
942             "REALM": realm,
943             "DNS_KEYTAB": keytab_name,
944             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
945             "PRIVATE_DIR": private_dir
946         })
947
948
949 def is_valid_dns_backend(dns_backend):
950     return dns_backend in ("BIND9_FLATFILE", "BIND9_DLZ", "SAMBA_INTERNAL", "NONE")
951
952
953 def is_valid_os_level(os_level):
954     return DS_DOMAIN_FUNCTION_2000 <= os_level <= DS_DOMAIN_FUNCTION_2008_R2
955
956
957 def create_dns_legacy(samdb, domainsid, forestdn, dnsadmins_sid):
958     # Set up MicrosoftDNS container
959     add_dns_container(samdb, forestdn, "CN=System", domainsid, dnsadmins_sid)
960     # Add root servers
961     add_rootservers(samdb, forestdn, "CN=System")
962
963
964 def fill_dns_data_legacy(samdb, domainsid, forestdn, dnsdomain, site, hostname,
965                          hostip, hostip6, dnsadmins_sid):
966     # Add domain record
967     add_domain_record(samdb, forestdn, "CN=System", dnsdomain, domainsid,
968                       dnsadmins_sid)
969
970     # Add DNS records for a DC in domain
971     add_dc_domain_records(samdb, forestdn, "CN=System", site, dnsdomain,
972                           hostname, hostip, hostip6)
973
974
975 def create_dns_partitions(samdb, domainsid, names, domaindn, forestdn,
976                           dnsadmins_sid, fill_level):
977     # Set up additional partitions (DomainDnsZones, ForstDnsZones)
978     setup_dns_partitions(samdb, domainsid, domaindn, forestdn,
979                         names.configdn, names.serverdn, fill_level)
980
981     # Set up MicrosoftDNS containers
982     add_dns_container(samdb, domaindn, "DC=DomainDnsZones", domainsid,
983                       dnsadmins_sid)
984     if fill_level != FILL_SUBDOMAIN:
985         add_dns_container(samdb, forestdn, "DC=ForestDnsZones", domainsid,
986                           dnsadmins_sid, forest=True)
987
988
989 def fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn,
990                              dnsdomain, dnsforest, hostname, hostip, hostip6,
991                              domainguid, ntdsguid, dnsadmins_sid, autofill=True,
992                              fill_level=FILL_FULL):
993     """Fill data in various AD partitions
994
995     :param samdb: LDB object connected to sam.ldb file
996     :param domainsid: Domain SID (as dom_sid object)
997     :param site: Site name to create hostnames in
998     :param domaindn: DN of the domain
999     :param forestdn: DN of the forest
1000     :param dnsdomain: DNS name of the domain
1001     :param dnsforest: DNS name of the forest
1002     :param hostname: Host name of this DC
1003     :param hostip: IPv4 addresses
1004     :param hostip6: IPv6 addresses
1005     :param domainguid: Domain GUID
1006     :param ntdsguid: NTDS GUID
1007     :param dnsadmins_sid: SID for DnsAdmins group
1008     :param autofill: Create DNS records (using fixed template)
1009     """
1010
1011     ##### Set up DC=DomainDnsZones,<DOMAINDN>
1012     # Add rootserver records
1013     add_rootservers(samdb, domaindn, "DC=DomainDnsZones")
1014
1015     # Add domain record
1016     add_domain_record(samdb, domaindn, "DC=DomainDnsZones", dnsdomain,
1017                       domainsid, dnsadmins_sid)
1018
1019     # Add DNS records for a DC in domain
1020     if autofill:
1021         add_dc_domain_records(samdb, domaindn, "DC=DomainDnsZones", site,
1022                               dnsdomain, hostname, hostip, hostip6)
1023
1024     if fill_level != FILL_SUBDOMAIN:
1025         ##### Set up DC=ForestDnsZones,<FORESTDN>
1026         # Add _msdcs record
1027         add_msdcs_record(samdb, forestdn, "DC=ForestDnsZones", dnsforest)
1028
1029         # Add DNS records for a DC in forest
1030         if autofill:
1031             add_dc_msdcs_records(samdb, forestdn, "DC=ForestDnsZones", site,
1032                                  dnsforest, hostname, hostip, hostip6,
1033                                  domainguid, ntdsguid)
1034
1035
1036 def setup_ad_dns(samdb, secretsdb, domainsid, names, paths, lp, logger,
1037         dns_backend, os_level, dnspass=None, hostip=None, hostip6=None,
1038         targetdir=None, fill_level=FILL_FULL):
1039     """Provision DNS information (assuming GC role)
1040
1041     :param samdb: LDB object connected to sam.ldb file
1042     :param secretsdb: LDB object connected to secrets.ldb file
1043     :param domainsid: Domain SID (as dom_sid object)
1044     :param names: Names shortcut
1045     :param paths: Paths shortcut
1046     :param lp: Loadparm object
1047     :param logger: Logger object
1048     :param dns_backend: Type of DNS backend
1049     :param os_level: Functional level (treated as os level)
1050     :param dnspass: Password for bind's DNS account
1051     :param hostip: IPv4 address
1052     :param hostip6: IPv6 address
1053     :param targetdir: Target directory for creating DNS-related files for BIND9
1054     """
1055
1056     if not is_valid_dns_backend(dns_backend):
1057         raise Exception("Invalid dns backend: %r" % dns_backend)
1058
1059     if not is_valid_os_level(os_level):
1060         raise Exception("Invalid os level: %r" % os_level)
1061
1062     if dns_backend == "NONE":
1063         logger.info("No DNS backend set, not configuring DNS")
1064         return
1065
1066     # Add dns accounts (DnsAdmins, DnsUpdateProxy) in domain
1067     logger.info("Adding DNS accounts")
1068     add_dns_accounts(samdb, names.domaindn)
1069
1070     # If dns_backend is BIND9_FLATFILE
1071     #   Populate only CN=MicrosoftDNS,CN=System,<DOMAINDN>
1072     #
1073     # If dns_backend is SAMBA_INTERNAL or BIND9_DLZ
1074     #   Populate DNS partitions
1075
1076     # If os_level < 2003 (DS_DOMAIN_FUNCTION_2000)
1077     #   All dns records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1078     #
1079     # If os_level >= 2003 (DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008,
1080     #                        DS_DOMAIN_FUNCTION_2008_R2)
1081     #   Root server records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1082     #   Domain records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1083     #   Domain records are in CN=MicrosoftDNS,DC=DomainDnsZones,<DOMAINDN>
1084     #   Forest records are in CN=MicrosoftDNS,DC=ForestDnsZones,<FORESTDN>
1085     domaindn = names.domaindn
1086     forestdn = samdb.get_root_basedn().get_linearized()
1087
1088     dnsdomain = names.dnsdomain.lower()
1089     dnsforest = dnsdomain
1090
1091     site = names.sitename
1092
1093     hostname = names.netbiosname.lower()
1094
1095     dnsadmins_sid = get_dnsadmins_sid(samdb, domaindn)
1096     domainguid = get_domainguid(samdb, domaindn)
1097
1098     # Create CN=System
1099     logger.info("Creating CN=MicrosoftDNS,CN=System,%s" % domaindn)
1100     create_dns_legacy(samdb, domainsid, domaindn, dnsadmins_sid)
1101
1102     if os_level == DS_DOMAIN_FUNCTION_2000:
1103         # Populating legacy dns
1104         logger.info("Populating CN=MicrosoftDNS,CN=System,%s" % domaindn)
1105         fill_dns_data_legacy(samdb, domainsid, domaindn, dnsdomain, site,
1106                              hostname, hostip, hostip6, dnsadmins_sid)
1107
1108     elif dns_backend in ("SAMBA_INTERNAL", "BIND9_DLZ") and \
1109             os_level >= DS_DOMAIN_FUNCTION_2003:
1110
1111         # Create DNS partitions
1112         logger.info("Creating DomainDnsZones and ForestDnsZones partitions")
1113         create_dns_partitions(samdb, domainsid, names, domaindn, forestdn,
1114                               dnsadmins_sid, fill_level)
1115
1116         # Populating dns partitions
1117         logger.info("Populating DomainDnsZones and ForestDnsZones partitions")
1118         fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn,
1119                                  dnsdomain, dnsforest, hostname, hostip, hostip6,
1120                                  domainguid, names.ntdsguid, dnsadmins_sid,
1121                                  fill_level=fill_level)
1122
1123     if dns_backend.startswith("BIND9_"):
1124         setup_bind9_dns(samdb, secretsdb, domainsid, names, paths, lp, logger,
1125                         dns_backend, os_level, site=site, dnspass=dnspass, hostip=hostip,
1126                         hostip6=hostip6, targetdir=targetdir)
1127
1128
1129 def setup_bind9_dns(samdb, secretsdb, domainsid, names, paths, lp, logger,
1130         dns_backend, os_level, site=None, dnspass=None, hostip=None,
1131         hostip6=None, targetdir=None, key_version_number=None):
1132     """Provision DNS information (assuming BIND9 backend in DC role)
1133
1134     :param samdb: LDB object connected to sam.ldb file
1135     :param secretsdb: LDB object connected to secrets.ldb file
1136     :param domainsid: Domain SID (as dom_sid object)
1137     :param names: Names shortcut
1138     :param paths: Paths shortcut
1139     :param lp: Loadparm object
1140     :param logger: Logger object
1141     :param dns_backend: Type of DNS backend
1142     :param os_level: Functional level (treated as os level)
1143     :param site: Site to create hostnames in
1144     :param dnspass: Password for bind's DNS account
1145     :param hostip: IPv4 address
1146     :param hostip6: IPv6 address
1147     :param targetdir: Target directory for creating DNS-related files for BIND9
1148     """
1149
1150     if (not is_valid_dns_backend(dns_backend) or
1151         not dns_backend.startswith("BIND9_")):
1152         raise Exception("Invalid dns backend: %r" % dns_backend)
1153
1154     if not is_valid_os_level(os_level):
1155         raise Exception("Invalid os level: %r" % os_level)
1156
1157     domaindn = names.domaindn
1158
1159     domainguid = get_domainguid(samdb, domaindn)
1160
1161     secretsdb_setup_dns(secretsdb, names,
1162                         paths.private_dir, realm=names.realm,
1163                         dnsdomain=names.dnsdomain,
1164                         dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1165                         key_version_number=key_version_number)
1166
1167     create_dns_dir(logger, paths)
1168
1169     if dns_backend == "BIND9_FLATFILE":
1170         create_zone_file(lp, logger, paths, targetdir, site=site,
1171                          dnsdomain=names.dnsdomain, hostip=hostip,
1172                          hostip6=hostip6, hostname=names.hostname,
1173                          realm=names.realm, domainguid=domainguid,
1174                          ntdsguid=names.ntdsguid)
1175
1176     if dns_backend == "BIND9_DLZ" and os_level >= DS_DOMAIN_FUNCTION_2003:
1177         create_samdb_copy(samdb, logger, paths, names, domainsid, domainguid)
1178
1179     create_named_conf(paths, realm=names.realm,
1180                       dnsdomain=names.dnsdomain, dns_backend=dns_backend,
1181                       logger=logger)
1182
1183     create_named_txt(paths.namedtxt,
1184                      realm=names.realm, dnsdomain=names.dnsdomain,
1185                      dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1186                      private_dir=paths.private_dir,
1187                      keytab_name=paths.dns_keytab)
1188     logger.info("See %s for an example configuration include file for BIND",
1189                 paths.namedconf)
1190     logger.info("and %s for further documentation required for secure DNS "
1191                 "updates", paths.namedtxt)