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