provision: Correctly provision the SOA record minimum TTL
[samba.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     forestzone_guid = get_domainguid(samdb, forestzone_dn)
256
257     domainzone_guid = str(uuid.uuid4())
258     forestzone_guid = str(uuid.uuid4())
259
260     domainzone_dns = ldb.Dn(samdb, domainzone_dn).canonical_ex_str().strip()
261     forestzone_dns = ldb.Dn(samdb, forestzone_dn).canonical_ex_str().strip()
262
263     protected1_desc = get_domain_delete_protected1_descriptor(domainsid)
264     protected2_desc = get_domain_delete_protected2_descriptor(domainsid)
265     setup_add_ldif(samdb, setup_path("provision_dnszones_add.ldif"), {
266         "ZONE_DN": domainzone_dn,
267         "ZONE_GUID": domainzone_guid,
268         "ZONE_DNS": domainzone_dns,
269         "CONFIGDN": configdn,
270         "SERVERDN": serverdn,
271         "LOSTANDFOUND_DESCRIPTOR": b64encode(protected2_desc),
272         "INFRASTRUCTURE_DESCRIPTOR": b64encode(protected1_desc),
273         })
274     setup_modify_ldif(samdb, setup_path("provision_dnszones_modify.ldif"), {
275         "CONFIGDN": configdn,
276         "SERVERDN": serverdn,
277         "ZONE_DN": domainzone_dn,
278     })
279
280     if fill_level != FILL_SUBDOMAIN:
281         setup_add_ldif(samdb, setup_path("provision_dnszones_add.ldif"), {
282             "ZONE_DN": forestzone_dn,
283             "ZONE_GUID": forestzone_guid,
284             "ZONE_DNS": forestzone_dns,
285             "CONFIGDN": configdn,
286             "SERVERDN": serverdn,
287             "LOSTANDFOUND_DESCRIPTOR": b64encode(protected2_desc),
288             "INFRASTRUCTURE_DESCRIPTOR": b64encode(protected1_desc),
289         })
290         setup_modify_ldif(samdb, setup_path("provision_dnszones_modify.ldif"), {
291             "CONFIGDN": configdn,
292             "SERVERDN": serverdn,
293             "ZONE_DN": forestzone_dn,
294         })
295
296
297 def add_dns_accounts(samdb, domaindn):
298     setup_add_ldif(samdb, setup_path("provision_dns_accounts_add.ldif"), {
299         "DOMAINDN": domaindn,
300         })
301
302
303 def add_dns_container(samdb, domaindn, prefix, domain_sid, dnsadmins_sid, forest=False):
304     name_map = {'DnsAdmins': str(dnsadmins_sid)}
305     if forest is True:
306         sd_val = get_dns_forest_microsoft_dns_descriptor(domain_sid,
307                                                          name_map=name_map)
308     else:
309         sd_val = get_dns_domain_microsoft_dns_descriptor(domain_sid,
310                                                          name_map=name_map)
311     # CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
312     msg = ldb.Message(ldb.Dn(samdb, "CN=MicrosoftDNS,%s,%s" % (prefix, domaindn)))
313     msg["objectClass"] = ["top", "container"]
314     msg["nTSecurityDescriptor"] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_ADD,
315         "nTSecurityDescriptor")
316     samdb.add(msg)
317
318
319 def add_rootservers(samdb, domaindn, prefix):
320     rootservers = {}
321     rootservers["a.root-servers.net"] = "198.41.0.4"
322     rootservers["b.root-servers.net"] = "192.228.79.201"
323     rootservers["c.root-servers.net"] = "192.33.4.12"
324     rootservers["d.root-servers.net"] = "128.8.10.90"
325     rootservers["e.root-servers.net"] = "192.203.230.10"
326     rootservers["f.root-servers.net"] = "192.5.5.241"
327     rootservers["g.root-servers.net"] = "192.112.36.4"
328     rootservers["h.root-servers.net"] = "128.63.2.53"
329     rootservers["i.root-servers.net"] = "192.36.148.17"
330     rootservers["j.root-servers.net"] = "192.58.128.30"
331     rootservers["k.root-servers.net"] = "193.0.14.129"
332     rootservers["l.root-servers.net"] = "199.7.83.42"
333     rootservers["m.root-servers.net"] = "202.12.27.33"
334
335     rootservers_v6 = {}
336     rootservers_v6["a.root-servers.net"] = "2001:503:ba3e::2:30"
337     rootservers_v6["f.root-servers.net"] = "2001:500:2f::f"
338     rootservers_v6["h.root-servers.net"] = "2001:500:1::803f:235"
339     rootservers_v6["j.root-servers.net"] = "2001:503:c27::2:30"
340     rootservers_v6["k.root-servers.net"] = "2001:7fd::1"
341     rootservers_v6["m.root-servers.net"] = "2001:dc3::35"
342
343     container_dn = "DC=RootDNSServers,CN=MicrosoftDNS,%s,%s" % (prefix, domaindn)
344
345     # Add DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
346     msg = ldb.Message(ldb.Dn(samdb, container_dn))
347     props = []
348     props.append(ndr_pack(TypeProperty(zone_type=dnsp.DNS_ZONE_TYPE_CACHE)))
349     props.append(ndr_pack(AllowUpdateProperty(allow_update=dnsp.DNS_ZONE_UPDATE_OFF)))
350     props.append(ndr_pack(SecureTimeProperty()))
351     props.append(ndr_pack(NorefreshIntervalProperty()))
352     props.append(ndr_pack(RefreshIntervalProperty()))
353     props.append(ndr_pack(AgingStateProperty()))
354     props.append(ndr_pack(AgingEnabledTimeProperty()))
355     msg["objectClass"] = ["top", "dnsZone"]
356     msg["cn"] = ldb.MessageElement("Zone", ldb.FLAG_MOD_ADD, "cn")
357     msg["dNSProperty"] = ldb.MessageElement(props, ldb.FLAG_MOD_ADD, "dNSProperty")
358     samdb.add(msg)
359
360     # Add DC=@,DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
361     record = []
362     for rserver in rootservers:
363         record.append(ndr_pack(NSRecord(rserver, serial=0, ttl=0, rank=dnsp.DNS_RANK_ROOT_HINT)))
364
365     msg = ldb.Message(ldb.Dn(samdb, "DC=@,%s" % container_dn))
366     msg["objectClass"] = ["top", "dnsNode"]
367     msg["dnsRecord"] = ldb.MessageElement(record, ldb.FLAG_MOD_ADD, "dnsRecord")
368     samdb.add(msg)
369
370     # Add DC=<rootserver>,DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
371     for rserver in rootservers:
372         record = [ndr_pack(ARecord(rootservers[rserver], serial=0, ttl=0, rank=dnsp.DNS_RANK_ROOT_HINT))]
373         # Add AAAA record as well (How does W2K* add IPv6 records?)
374         #if rserver in rootservers_v6:
375         #    record.append(ndr_pack(AAAARecord(rootservers_v6[rserver], serial=0, ttl=0)))
376         msg = ldb.Message(ldb.Dn(samdb, "DC=%s,%s" % (rserver, container_dn)))
377         msg["objectClass"] = ["top", "dnsNode"]
378         msg["dnsRecord"] = ldb.MessageElement(record, ldb.FLAG_MOD_ADD, "dnsRecord")
379         samdb.add(msg)
380
381 def add_at_record(samdb, container_dn, prefix, hostname, dnsdomain, hostip, hostip6):
382
383     fqdn_hostname = "%s.%s" % (hostname, dnsdomain)
384
385     at_records = []
386
387     # SOA record
388     at_soa_record = SOARecord(fqdn_hostname, "hostmaster.%s" % dnsdomain)
389     at_records.append(ndr_pack(at_soa_record))
390
391     # NS record
392     at_ns_record = NSRecord(fqdn_hostname)
393     at_records.append(ndr_pack(at_ns_record))
394
395     if hostip is not None:
396         # A record
397         at_a_record = ARecord(hostip)
398         at_records.append(ndr_pack(at_a_record))
399
400     if hostip6 is not None:
401         # AAAA record
402         at_aaaa_record = AAAARecord(hostip6)
403         at_records.append(ndr_pack(at_aaaa_record))
404
405     msg = ldb.Message(ldb.Dn(samdb, "DC=@,%s" % container_dn))
406     msg["objectClass"] = ["top", "dnsNode"]
407     msg["dnsRecord"] = ldb.MessageElement(at_records, ldb.FLAG_MOD_ADD, "dnsRecord")
408     samdb.add(msg)
409
410
411 def add_srv_record(samdb, container_dn, prefix, host, port):
412     srv_record = SRVRecord(host, port)
413     msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
414     msg["objectClass"] = ["top", "dnsNode"]
415     msg["dnsRecord"] = ldb.MessageElement(ndr_pack(srv_record), ldb.FLAG_MOD_ADD, "dnsRecord")
416     samdb.add(msg)
417
418
419 def add_ns_record(samdb, container_dn, prefix, host):
420     ns_record = NSRecord(host)
421     msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
422     msg["objectClass"] = ["top", "dnsNode"]
423     msg["dnsRecord"] = ldb.MessageElement(ndr_pack(ns_record), ldb.FLAG_MOD_ADD, "dnsRecord")
424     samdb.add(msg)
425
426
427 def add_ns_glue_record(samdb, container_dn, prefix, host):
428     ns_record = NSRecord(host, rank=dnsp.DNS_RANK_NS_GLUE)
429     msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
430     msg["objectClass"] = ["top", "dnsNode"]
431     msg["dnsRecord"] = ldb.MessageElement(ndr_pack(ns_record), ldb.FLAG_MOD_ADD, "dnsRecord")
432     samdb.add(msg)
433
434
435 def add_cname_record(samdb, container_dn, prefix, host):
436     cname_record = CNameRecord(host)
437     msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
438     msg["objectClass"] = ["top", "dnsNode"]
439     msg["dnsRecord"] = ldb.MessageElement(ndr_pack(cname_record), ldb.FLAG_MOD_ADD, "dnsRecord")
440     samdb.add(msg)
441
442
443 def add_host_record(samdb, container_dn, prefix, hostip, hostip6):
444     host_records = []
445     if hostip:
446         a_record = ARecord(hostip)
447         host_records.append(ndr_pack(a_record))
448     if hostip6:
449         aaaa_record = AAAARecord(hostip6)
450         host_records.append(ndr_pack(aaaa_record))
451     if host_records:
452         msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
453         msg["objectClass"] = ["top", "dnsNode"]
454         msg["dnsRecord"] = ldb.MessageElement(host_records, ldb.FLAG_MOD_ADD, "dnsRecord")
455         samdb.add(msg)
456
457
458 def add_domain_record(samdb, domaindn, prefix, dnsdomain, domainsid, dnsadmins_sid):
459     # DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
460     sddl = "O:SYG:BAD:AI" \
461     "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)" \
462     "(A;;CC;;;AU)" \
463     "(A;;RPLCLORC;;;WD)" \
464     "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
465     "(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)" \
466     "(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;%s)" \
467     "(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)" \
468     "(OA;CIID;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
469     "(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
470     "(A;CIID;LC;;;RU)" \
471     "(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
472     "S:AI" % dnsadmins_sid
473     sec = security.descriptor.from_sddl(sddl, domainsid)
474     props = []
475     props.append(ndr_pack(TypeProperty()))
476     props.append(ndr_pack(AllowUpdateProperty()))
477     props.append(ndr_pack(SecureTimeProperty()))
478     props.append(ndr_pack(NorefreshIntervalProperty(norefresh_interval=168)))
479     props.append(ndr_pack(RefreshIntervalProperty(refresh_interval=168)))
480     props.append(ndr_pack(AgingStateProperty()))
481     props.append(ndr_pack(AgingEnabledTimeProperty()))
482     msg = ldb.Message(ldb.Dn(samdb, "DC=%s,CN=MicrosoftDNS,%s,%s" % (dnsdomain, prefix, domaindn)))
483     msg["objectClass"] = ["top", "dnsZone"]
484     msg["ntSecurityDescriptor"] = ldb.MessageElement(ndr_pack(sec), ldb.FLAG_MOD_ADD,
485         "nTSecurityDescriptor")
486     msg["dNSProperty"] = ldb.MessageElement(props, ldb.FLAG_MOD_ADD, "dNSProperty")
487     samdb.add(msg)
488
489
490 def add_msdcs_record(samdb, forestdn, prefix, dnsforest):
491     # DC=_msdcs.<DNSFOREST>,CN=MicrosoftDNS,<PREFIX>,<FORESTDN>
492     msg = ldb.Message(ldb.Dn(samdb, "DC=_msdcs.%s,CN=MicrosoftDNS,%s,%s" %
493                                     (dnsforest, prefix, forestdn)))
494     msg["objectClass"] = ["top", "dnsZone"]
495     samdb.add(msg)
496
497
498 def add_dc_domain_records(samdb, domaindn, prefix, site, dnsdomain, hostname,
499         hostip, hostip6):
500
501     fqdn_hostname = "%s.%s" % (hostname, dnsdomain)
502
503     # Set up domain container - DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
504     domain_container_dn = ldb.Dn(samdb, "DC=%s,CN=MicrosoftDNS,%s,%s" %
505                                     (dnsdomain, prefix, domaindn))
506
507     # DC=@ record
508     add_at_record(samdb, domain_container_dn, "DC=@", hostname, dnsdomain,
509             hostip, hostip6)
510
511     # DC=<HOSTNAME> record
512     add_host_record(samdb, domain_container_dn, "DC=%s" % hostname, hostip,
513             hostip6)
514
515     # DC=_kerberos._tcp record
516     add_srv_record(samdb, domain_container_dn, "DC=_kerberos._tcp",
517             fqdn_hostname, 88)
518
519     # DC=_kerberos._tcp.<SITENAME>._sites record
520     add_srv_record(samdb, domain_container_dn, "DC=_kerberos._tcp.%s._sites" %
521             site, fqdn_hostname, 88)
522
523     # DC=_kerberos._udp record
524     add_srv_record(samdb, domain_container_dn, "DC=_kerberos._udp",
525             fqdn_hostname, 88)
526
527     # DC=_kpasswd._tcp record
528     add_srv_record(samdb, domain_container_dn, "DC=_kpasswd._tcp",
529             fqdn_hostname, 464)
530
531     # DC=_kpasswd._udp record
532     add_srv_record(samdb, domain_container_dn, "DC=_kpasswd._udp",
533             fqdn_hostname, 464)
534
535     # DC=_ldap._tcp record
536     add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp", fqdn_hostname,
537             389)
538
539     # DC=_ldap._tcp.<SITENAME>._sites record
540     add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp.%s._sites" %
541             site, fqdn_hostname, 389)
542
543     # FIXME: The number of SRV records depend on the various roles this DC has.
544     #        _gc and _msdcs records are added if the we are the forest dc and not subdomain dc
545     #
546     # Assumption: current DC is GC and add all the entries
547
548     # DC=_gc._tcp record
549     add_srv_record(samdb, domain_container_dn, "DC=_gc._tcp", fqdn_hostname,
550             3268)
551
552     # DC=_gc._tcp.<SITENAME>,_sites record
553     add_srv_record(samdb, domain_container_dn, "DC=_gc._tcp.%s._sites" % site,
554             fqdn_hostname, 3268)
555
556     # DC=_msdcs record
557     add_ns_glue_record(samdb, domain_container_dn, "DC=_msdcs", fqdn_hostname)
558
559     # FIXME: Following entries are added only if DomainDnsZones and ForestDnsZones partitions
560     #        are created
561     #
562     # Assumption: Additional entries won't hurt on os_level = 2000
563
564     # DC=_ldap._tcp.<SITENAME>._sites.DomainDnsZones
565     add_srv_record(samdb, domain_container_dn,
566             "DC=_ldap._tcp.%s._sites.DomainDnsZones" % site, fqdn_hostname,
567             389)
568
569     # DC=_ldap._tcp.<SITENAME>._sites.ForestDnsZones
570     add_srv_record(samdb, domain_container_dn,
571             "DC=_ldap._tcp.%s._sites.ForestDnsZones" % site, fqdn_hostname,
572             389)
573
574     # DC=_ldap._tcp.DomainDnsZones
575     add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp.DomainDnsZones",
576                     fqdn_hostname, 389)
577
578     # DC=_ldap._tcp.ForestDnsZones
579     add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp.ForestDnsZones",
580                     fqdn_hostname, 389)
581
582     # DC=DomainDnsZones
583     add_host_record(samdb, domain_container_dn, "DC=DomainDnsZones", hostip,
584             hostip6)
585
586     # DC=ForestDnsZones
587     add_host_record(samdb, domain_container_dn, "DC=ForestDnsZones", hostip,
588             hostip6)
589
590
591 def add_dc_msdcs_records(samdb, forestdn, prefix, site, dnsforest, hostname,
592                             hostip, hostip6, domainguid, ntdsguid):
593
594     fqdn_hostname = "%s.%s" % (hostname, dnsforest)
595
596     # Set up forest container - DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
597     forest_container_dn = ldb.Dn(samdb, "DC=_msdcs.%s,CN=MicrosoftDNS,%s,%s" %
598                                     (dnsforest, prefix, forestdn))
599
600     # DC=@ record
601     add_at_record(samdb, forest_container_dn, "DC=@", hostname, dnsforest,
602             None, None)
603
604     # DC=_kerberos._tcp.dc record
605     add_srv_record(samdb, forest_container_dn, "DC=_kerberos._tcp.dc",
606             fqdn_hostname, 88)
607
608     # DC=_kerberos._tcp.<SITENAME>._sites.dc record
609     add_srv_record(samdb, forest_container_dn,
610             "DC=_kerberos._tcp.%s._sites.dc" % site, fqdn_hostname, 88)
611
612     # DC=_ldap._tcp.dc record
613     add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.dc",
614             fqdn_hostname, 389)
615
616     # DC=_ldap._tcp.<SITENAME>._sites.dc record
617     add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.%s._sites.dc" %
618             site, fqdn_hostname, 389)
619
620     # DC=_ldap._tcp.<SITENAME>._sites.gc record
621     add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.%s._sites.gc" %
622             site, fqdn_hostname, 3268)
623
624     # DC=_ldap._tcp.gc record
625     add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.gc",
626             fqdn_hostname, 3268)
627
628     # DC=_ldap._tcp.pdc record
629     add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.pdc",
630             fqdn_hostname, 389)
631
632     # DC=gc record
633     add_host_record(samdb, forest_container_dn, "DC=gc", hostip, hostip6)
634
635     # DC=_ldap._tcp.<DOMAINGUID>.domains record
636     add_srv_record(samdb, forest_container_dn,
637             "DC=_ldap._tcp.%s.domains" % domainguid, fqdn_hostname, 389)
638
639     # DC=<NTDSGUID>
640     add_cname_record(samdb, forest_container_dn, "DC=%s" % ntdsguid,
641             fqdn_hostname)
642
643
644 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
645                         dnsdomain, dns_keytab_path, dnspass, key_version_number):
646     """Add DNS specific bits to a secrets database.
647
648     :param secretsdb: Ldb Handle to the secrets database
649     :param names: Names shortcut
650     :param machinepass: Machine password
651     """
652     try:
653         os.unlink(os.path.join(private_dir, dns_keytab_path))
654     except OSError:
655         pass
656
657     if key_version_number is None:
658         key_version_number = 1
659
660     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
661             "REALM": realm,
662             "DNSDOMAIN": dnsdomain,
663             "DNS_KEYTAB": dns_keytab_path,
664             "DNSPASS_B64": b64encode(dnspass),
665             "KEY_VERSION_NUMBER": str(key_version_number),
666             "HOSTNAME": names.hostname,
667             "DNSNAME" : '%s.%s' % (
668                 names.netbiosname.lower(), names.dnsdomain.lower())
669             })
670
671
672 def create_dns_dir(logger, paths):
673     """Write out a DNS zone file, from the info in the current database.
674
675     :param logger: Logger object
676     :param paths: paths object
677     """
678     dns_dir = os.path.dirname(paths.dns)
679
680     try:
681         shutil.rmtree(dns_dir, True)
682     except OSError:
683         pass
684
685     os.mkdir(dns_dir, 0770)
686
687     if paths.bind_gid is not None:
688         try:
689             os.chown(dns_dir, -1, paths.bind_gid)
690             # chmod needed to cope with umask
691             os.chmod(dns_dir, 0770)
692         except OSError:
693             if not os.environ.has_key('SAMBA_SELFTEST'):
694                 logger.error("Failed to chown %s to bind gid %u" % (
695                     dns_dir, paths.bind_gid))
696
697
698 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
699                      hostip, hostip6, hostname, realm, domainguid,
700                      ntdsguid, site):
701     """Write out a DNS zone file, from the info in the current database.
702
703     :param paths: paths object
704     :param dnsdomain: DNS Domain name
705     :param domaindn: DN of the Domain
706     :param hostip: Local IPv4 IP
707     :param hostip6: Local IPv6 IP
708     :param hostname: Local hostname
709     :param realm: Realm name
710     :param domainguid: GUID of the domain.
711     :param ntdsguid: GUID of the hosts nTDSDSA record.
712     """
713     assert isinstance(domainguid, str)
714
715     if hostip6 is not None:
716         hostip6_base_line = "            IN AAAA    " + hostip6
717         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
718         gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
719     else:
720         hostip6_base_line = ""
721         hostip6_host_line = ""
722         gc_msdcs_ip6_line = ""
723
724     if hostip is not None:
725         hostip_base_line = "            IN A    " + hostip
726         hostip_host_line = hostname + "        IN A    " + hostip
727         gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
728     else:
729         hostip_base_line = ""
730         hostip_host_line = ""
731         gc_msdcs_ip_line = ""
732
733     # we need to freeze the zone while we update the contents
734     if targetdir is None:
735         rndc = ' '.join(lp.get("rndc command"))
736         os.system(rndc + " freeze " + lp.get("realm"))
737
738     setup_file(setup_path("provision.zone"), paths.dns, {
739             "HOSTNAME": hostname,
740             "DNSDOMAIN": dnsdomain,
741             "REALM": realm,
742             "HOSTIP_BASE_LINE": hostip_base_line,
743             "HOSTIP_HOST_LINE": hostip_host_line,
744             "DOMAINGUID": domainguid,
745             "DATESTRING": time.strftime("%Y%m%d%H"),
746             "DEFAULTSITE": site,
747             "NTDSGUID": ntdsguid,
748             "HOSTIP6_BASE_LINE": hostip6_base_line,
749             "HOSTIP6_HOST_LINE": hostip6_host_line,
750             "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
751             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
752         })
753
754     if paths.bind_gid is not None:
755         try:
756             os.chown(paths.dns, -1, paths.bind_gid)
757             # chmod needed to cope with umask
758             os.chmod(paths.dns, 0664)
759         except OSError:
760             if not os.environ.has_key('SAMBA_SELFTEST'):
761                 logger.error("Failed to chown %s to bind gid %u" % (
762                     paths.dns, paths.bind_gid))
763
764     if targetdir is None:
765         os.system(rndc + " unfreeze " + lp.get("realm"))
766
767
768 def create_samdb_copy(samdb, logger, paths, names, domainsid, domainguid):
769     """Create a copy of samdb and give write permissions to named for dns partitions
770     """
771     private_dir = paths.private_dir
772     samldb_dir = os.path.join(private_dir, "sam.ldb.d")
773     dns_dir = os.path.dirname(paths.dns)
774     dns_samldb_dir = os.path.join(dns_dir, "sam.ldb.d")
775
776     # Find the partitions and corresponding filenames
777     partfile = {}
778     res = samdb.search(base="@PARTITION", scope=ldb.SCOPE_BASE, attrs=["partition"])
779     for tmp in res[0]["partition"]:
780         (nc, fname) = tmp.split(':')
781         partfile[nc.upper()] = fname
782
783     # Create empty domain partition
784     domaindn = names.domaindn.upper()
785     domainpart_file = os.path.join(dns_dir, partfile[domaindn])
786     try:
787         os.mkdir(dns_samldb_dir)
788         file(domainpart_file, 'w').close()
789
790         # Fill the basedn and @OPTION records in domain partition
791         dom_ldb = samba.Ldb(domainpart_file)
792         domainguid_line = "objectGUID: %s\n-" % domainguid
793         descr = b64encode(get_domain_descriptor(domainsid))
794         setup_add_ldif(dom_ldb, setup_path("provision_basedn.ldif"), {
795             "DOMAINDN" : names.domaindn,
796             "DOMAINGUID" : domainguid_line,
797             "DOMAINSID" : str(domainsid),
798             "DESCRIPTOR" : descr})
799         setup_add_ldif(dom_ldb,
800             setup_path("provision_basedn_options.ldif"), None)
801     except:
802         logger.error(
803             "Failed to setup database for BIND, AD based DNS cannot be used")
804         raise
805     del partfile[domaindn]
806
807     # Link dns partitions and metadata
808     domainzonedn = "DC=DOMAINDNSZONES,%s" % names.domaindn.upper()
809     forestzonedn = "DC=FORESTDNSZONES,%s" % names.rootdn.upper()
810     domainzone_file = partfile[domainzonedn]
811     forestzone_file = partfile[forestzonedn]
812     metadata_file = "metadata.tdb"
813     try:
814         os.link(os.path.join(samldb_dir, metadata_file),
815             os.path.join(dns_samldb_dir, metadata_file))
816         os.link(os.path.join(private_dir, domainzone_file),
817             os.path.join(dns_dir, domainzone_file))
818         os.link(os.path.join(private_dir, forestzone_file),
819             os.path.join(dns_dir, forestzone_file))
820     except OSError:
821         logger.error(
822             "Failed to setup database for BIND, AD based DNS cannot be used")
823         raise
824     del partfile[domainzonedn]
825     del partfile[forestzonedn]
826
827     # Copy root, config, schema partitions (and any other if any)
828     # Since samdb is open in the current process, copy them in a child process
829     try:
830         tdb_copy(os.path.join(private_dir, "sam.ldb"),
831                  os.path.join(dns_dir, "sam.ldb"))
832         for nc in partfile:
833             pfile = partfile[nc]
834             tdb_copy(os.path.join(private_dir, pfile),
835                      os.path.join(dns_dir, pfile))
836     except:
837         logger.error(
838             "Failed to setup database for BIND, AD based DNS cannot be used")
839         raise
840
841     # Give bind read/write permissions dns partitions
842     if paths.bind_gid is not None:
843         try:
844             os.chown(samldb_dir, -1, paths.bind_gid)
845             os.chmod(samldb_dir, 0750)
846
847             for dirname, dirs, files in os.walk(dns_dir):
848                 for d in dirs:
849                     dpath = os.path.join(dirname, d)
850                     os.chown(dpath, -1, paths.bind_gid)
851                     os.chmod(dpath, 0770)
852                 for f in files:
853                     if f.endswith('.ldb') or f.endswith('.tdb'):
854                         fpath = os.path.join(dirname, f)
855                         os.chown(fpath, -1, paths.bind_gid)
856                         os.chmod(fpath, 0660)
857         except OSError:
858             if not os.environ.has_key('SAMBA_SELFTEST'):
859                 logger.error(
860                     "Failed to set permissions to sam.ldb* files, fix manually")
861     else:
862         if not os.environ.has_key('SAMBA_SELFTEST'):
863             logger.warning("""Unable to find group id for BIND,
864                 set permissions to sam.ldb* files manually""")
865
866
867 def create_dns_update_list(lp, logger, paths):
868     """Write out a dns_update_list file"""
869     # note that we use no variable substitution on this file
870     # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
871     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
872     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
873
874
875 def create_named_conf(paths, realm, dnsdomain, dns_backend, logger):
876     """Write out a file containing zone statements suitable for inclusion in a
877     named.conf file (including GSS-TSIG configuration).
878
879     :param paths: all paths
880     :param realm: Realm name
881     :param dnsdomain: DNS Domain name
882     :param dns_backend: DNS backend type
883     :param keytab_name: File name of DNS keytab file
884     :param logger: Logger object
885     """
886
887     # TODO: This really should have been done as a top level import.
888     # It is done here to avoid a depencency loop.  That is, we move
889     # ProvisioningError to another file, and have all the provision
890     # scripts import it from there.
891
892     from samba.provision import ProvisioningError
893
894     if dns_backend == "BIND9_FLATFILE":
895         setup_file(setup_path("named.conf"), paths.namedconf, {
896                     "DNSDOMAIN": dnsdomain,
897                     "REALM": realm,
898                     "ZONE_FILE": paths.dns,
899                     "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
900                     "NAMED_CONF": paths.namedconf,
901                     "NAMED_CONF_UPDATE": paths.namedconf_update
902                     })
903
904         setup_file(setup_path("named.conf.update"), paths.namedconf_update)
905
906     elif dns_backend == "BIND9_DLZ":
907         bind_info = subprocess.Popen(['named -V'], shell=True,
908                                      stdout=subprocess.PIPE,
909                                      stderr=subprocess.STDOUT,
910                                      cwd='.').communicate()[0]
911         bind98 = '#'
912         bind99 = '#'
913         if bind_info.upper().find('BIND 9.8') != -1:
914             bind98 = ''
915         elif bind_info.upper().find('BIND 9.9') != -1:
916             bind99 = ''
917         elif bind_info.upper().find('BIND 9.7') != -1:
918             raise ProvisioningError("DLZ option incompatible with BIND 9.7.")
919         else:
920             logger.warning("BIND version unknown, please modify %s manually." % paths.namedconf)
921         setup_file(setup_path("named.conf.dlz"), paths.namedconf, {
922                     "NAMED_CONF": paths.namedconf,
923                     "MODULESDIR" : samba.param.modules_dir(),
924                     "BIND98" : bind98,
925                     "BIND99" : bind99
926                     })
927
928
929 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
930     keytab_name):
931     """Write out a file containing zone statements suitable for inclusion in a
932     named.conf file (including GSS-TSIG configuration).
933
934     :param path: Path of the new named.conf file.
935     :param realm: Realm name
936     :param dnsdomain: DNS Domain name
937     :param private_dir: Path to private directory
938     :param keytab_name: File name of DNS keytab file
939     """
940     setup_file(setup_path("named.txt"), path, {
941             "DNSDOMAIN": dnsdomain,
942             "DNSNAME" : dnsname,
943             "REALM": realm,
944             "DNS_KEYTAB": keytab_name,
945             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
946             "PRIVATE_DIR": private_dir
947         })
948
949
950 def is_valid_dns_backend(dns_backend):
951     return dns_backend in ("BIND9_FLATFILE", "BIND9_DLZ", "SAMBA_INTERNAL", "NONE")
952
953
954 def is_valid_os_level(os_level):
955     return DS_DOMAIN_FUNCTION_2000 <= os_level <= DS_DOMAIN_FUNCTION_2008_R2
956
957
958 def create_dns_legacy(samdb, domainsid, forestdn, dnsadmins_sid):
959     # Set up MicrosoftDNS container
960     add_dns_container(samdb, forestdn, "CN=System", domainsid, dnsadmins_sid)
961     # Add root servers
962     add_rootservers(samdb, forestdn, "CN=System")
963
964
965 def fill_dns_data_legacy(samdb, domainsid, forestdn, dnsdomain, site, hostname,
966                          hostip, hostip6, dnsadmins_sid):
967     # Add domain record
968     add_domain_record(samdb, forestdn, "CN=System", dnsdomain, domainsid,
969                       dnsadmins_sid)
970
971     # Add DNS records for a DC in domain
972     add_dc_domain_records(samdb, forestdn, "CN=System", site, dnsdomain,
973                           hostname, hostip, hostip6)
974
975
976 def create_dns_partitions(samdb, domainsid, names, domaindn, forestdn,
977                           dnsadmins_sid, fill_level):
978     # Set up additional partitions (DomainDnsZones, ForstDnsZones)
979     setup_dns_partitions(samdb, domainsid, domaindn, forestdn,
980                         names.configdn, names.serverdn, fill_level)
981
982     # Set up MicrosoftDNS containers
983     add_dns_container(samdb, domaindn, "DC=DomainDnsZones", domainsid,
984                       dnsadmins_sid)
985     if fill_level != FILL_SUBDOMAIN:
986         add_dns_container(samdb, forestdn, "DC=ForestDnsZones", domainsid,
987                           dnsadmins_sid, forest=True)
988
989
990 def fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn,
991                              dnsdomain, dnsforest, hostname, hostip, hostip6,
992                              domainguid, ntdsguid, dnsadmins_sid, autofill=True,
993                              fill_level=FILL_FULL):
994     """Fill data in various AD partitions
995
996     :param samdb: LDB object connected to sam.ldb file
997     :param domainsid: Domain SID (as dom_sid object)
998     :param site: Site name to create hostnames in
999     :param domaindn: DN of the domain
1000     :param forestdn: DN of the forest
1001     :param dnsdomain: DNS name of the domain
1002     :param dnsforest: DNS name of the forest
1003     :param hostname: Host name of this DC
1004     :param hostip: IPv4 addresses
1005     :param hostip6: IPv6 addresses
1006     :param domainguid: Domain GUID
1007     :param ntdsguid: NTDS GUID
1008     :param dnsadmins_sid: SID for DnsAdmins group
1009     :param autofill: Create DNS records (using fixed template)
1010     """
1011
1012     ##### Set up DC=DomainDnsZones,<DOMAINDN>
1013     # Add rootserver records
1014     add_rootservers(samdb, domaindn, "DC=DomainDnsZones")
1015
1016     # Add domain record
1017     add_domain_record(samdb, domaindn, "DC=DomainDnsZones", dnsdomain,
1018                       domainsid, dnsadmins_sid)
1019
1020     # Add DNS records for a DC in domain
1021     if autofill:
1022         add_dc_domain_records(samdb, domaindn, "DC=DomainDnsZones", site,
1023                               dnsdomain, hostname, hostip, hostip6)
1024
1025     if fill_level != FILL_SUBDOMAIN:
1026         ##### Set up DC=ForestDnsZones,<FORESTDN>
1027         # Add _msdcs record
1028         add_msdcs_record(samdb, forestdn, "DC=ForestDnsZones", dnsforest)
1029
1030         # Add DNS records for a DC in forest
1031         if autofill:
1032             add_dc_msdcs_records(samdb, forestdn, "DC=ForestDnsZones", site,
1033                                  dnsforest, hostname, hostip, hostip6,
1034                                  domainguid, ntdsguid)
1035
1036
1037 def setup_ad_dns(samdb, secretsdb, domainsid, names, paths, lp, logger,
1038         dns_backend, os_level, site, dnspass=None, hostip=None, hostip6=None,
1039         targetdir=None, fill_level=FILL_FULL):
1040     """Provision DNS information (assuming GC role)
1041
1042     :param samdb: LDB object connected to sam.ldb file
1043     :param secretsdb: LDB object connected to secrets.ldb file
1044     :param domainsid: Domain SID (as dom_sid object)
1045     :param names: Names shortcut
1046     :param paths: Paths shortcut
1047     :param lp: Loadparm object
1048     :param logger: Logger object
1049     :param dns_backend: Type of DNS backend
1050     :param os_level: Functional level (treated as os level)
1051     :param site: Site to create hostnames in
1052     :param dnspass: Password for bind's DNS account
1053     :param hostip: IPv4 address
1054     :param hostip6: IPv6 address
1055     :param targetdir: Target directory for creating DNS-related files for BIND9
1056     """
1057
1058     if not is_valid_dns_backend(dns_backend):
1059         raise Exception("Invalid dns backend: %r" % dns_backend)
1060
1061     if not is_valid_os_level(os_level):
1062         raise Exception("Invalid os level: %r" % os_level)
1063
1064     if dns_backend == "NONE":
1065         logger.info("No DNS backend set, not configuring DNS")
1066         return
1067
1068     # Add dns accounts (DnsAdmins, DnsUpdateProxy) in domain
1069     logger.info("Adding DNS accounts")
1070     add_dns_accounts(samdb, names.domaindn)
1071
1072     # If dns_backend is BIND9_FLATFILE
1073     #   Populate only CN=MicrosoftDNS,CN=System,<DOMAINDN>
1074     #
1075     # If dns_backend is SAMBA_INTERNAL or BIND9_DLZ
1076     #   Populate DNS partitions
1077
1078     # If os_level < 2003 (DS_DOMAIN_FUNCTION_2000)
1079     #   All dns records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1080     #
1081     # If os_level >= 2003 (DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008,
1082     #                        DS_DOMAIN_FUNCTION_2008_R2)
1083     #   Root server records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1084     #   Domain records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1085     #   Domain records are in CN=MicrosoftDNS,DC=DomainDnsZones,<DOMAINDN>
1086     #   Forest records are in CN=MicrosoftDNS,DC=ForestDnsZones,<FORESTDN>
1087     domaindn = names.domaindn
1088     forestdn = samdb.get_root_basedn().get_linearized()
1089
1090     dnsdomain = names.dnsdomain.lower()
1091     dnsforest = dnsdomain
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)