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