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