python:samba: Remove code to change group
[nivanova/samba-autobuild/.git] / python / samba / provision / sambadns.py
1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning DNS for a Samba4 server
3 #
4 # Copyright (C) Kai Blin <kai@samba.org> 2011
5 # Copyright (C) Amitay Isaacs <amitay@gmail.com> 2011
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20
21 """DNS-related provisioning"""
22
23 import os
24 import uuid
25 import shutil
26 import time
27 import ldb
28 from base64 import b64encode
29 import subprocess
30 import samba
31 from samba.tdb_util import tdb_copy
32 from samba.ndr import ndr_pack, ndr_unpack
33 from samba import setup_file
34 from samba.dcerpc import dnsp, misc, security
35 from samba.dsdb import (
36     DS_DOMAIN_FUNCTION_2000,
37     DS_DOMAIN_FUNCTION_2003,
38     DS_DOMAIN_FUNCTION_2008_R2,
39     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, 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     except OSError:
663         pass
664
665     if key_version_number is None:
666         key_version_number = 1
667
668     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
669             "REALM": realm,
670             "DNSDOMAIN": dnsdomain,
671             "DNS_KEYTAB": dns_keytab_path,
672             "DNSPASS_B64": b64encode(dnspass.encode('utf-8')),
673             "KEY_VERSION_NUMBER": str(key_version_number),
674             "HOSTNAME": names.hostname,
675             "DNSNAME" : '%s.%s' % (
676                 names.netbiosname.lower(), names.dnsdomain.lower())
677             })
678
679
680 def create_dns_dir(logger, paths):
681     """Write out a DNS zone file, from the info in the current database.
682
683     :param logger: Logger object
684     :param paths: paths object
685     """
686     dns_dir = os.path.dirname(paths.dns)
687
688     try:
689         shutil.rmtree(dns_dir, True)
690     except OSError:
691         pass
692
693     os.mkdir(dns_dir, 0770)
694
695     if paths.bind_gid is not None:
696         try:
697             os.chown(dns_dir, -1, paths.bind_gid)
698             # chmod needed to cope with umask
699             os.chmod(dns_dir, 0770)
700         except OSError:
701             if not os.environ.has_key('SAMBA_SELFTEST'):
702                 logger.error("Failed to chown %s to bind gid %u" % (
703                     dns_dir, paths.bind_gid))
704
705
706 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
707                      hostip, hostip6, hostname, realm, domainguid,
708                      ntdsguid, site):
709     """Write out a DNS zone file, from the info in the current database.
710
711     :param paths: paths object
712     :param dnsdomain: DNS Domain name
713     :param domaindn: DN of the Domain
714     :param hostip: Local IPv4 IP
715     :param hostip6: Local IPv6 IP
716     :param hostname: Local hostname
717     :param realm: Realm name
718     :param domainguid: GUID of the domain.
719     :param ntdsguid: GUID of the hosts nTDSDSA record.
720     """
721     assert isinstance(domainguid, str)
722
723     if hostip6 is not None:
724         hostip6_base_line = "            IN AAAA    " + hostip6
725         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
726         gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
727     else:
728         hostip6_base_line = ""
729         hostip6_host_line = ""
730         gc_msdcs_ip6_line = ""
731
732     if hostip is not None:
733         hostip_base_line = "            IN A    " + hostip
734         hostip_host_line = hostname + "        IN A    " + hostip
735         gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
736     else:
737         hostip_base_line = ""
738         hostip_host_line = ""
739         gc_msdcs_ip_line = ""
740
741     # we need to freeze the zone while we update the contents
742     if targetdir is None:
743         rndc = ' '.join(lp.get("rndc command"))
744         os.system(rndc + " freeze " + lp.get("realm"))
745
746     setup_file(setup_path("provision.zone"), paths.dns, {
747             "HOSTNAME": hostname,
748             "DNSDOMAIN": dnsdomain,
749             "REALM": realm,
750             "HOSTIP_BASE_LINE": hostip_base_line,
751             "HOSTIP_HOST_LINE": hostip_host_line,
752             "DOMAINGUID": domainguid,
753             "DATESTRING": time.strftime("%Y%m%d%H"),
754             "DEFAULTSITE": site,
755             "NTDSGUID": ntdsguid,
756             "HOSTIP6_BASE_LINE": hostip6_base_line,
757             "HOSTIP6_HOST_LINE": hostip6_host_line,
758             "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
759             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
760         })
761
762     if paths.bind_gid is not None:
763         try:
764             os.chown(paths.dns, -1, paths.bind_gid)
765             # chmod needed to cope with umask
766             os.chmod(paths.dns, 0664)
767         except OSError:
768             if not os.environ.has_key('SAMBA_SELFTEST'):
769                 logger.error("Failed to chown %s to bind gid %u" % (
770                     paths.dns, paths.bind_gid))
771
772     if targetdir is None:
773         os.system(rndc + " unfreeze " + lp.get("realm"))
774
775
776 def create_samdb_copy(samdb, logger, paths, names, domainsid, domainguid):
777     """Create a copy of samdb and give write permissions to named for dns partitions
778     """
779     private_dir = paths.private_dir
780     samldb_dir = os.path.join(private_dir, "sam.ldb.d")
781     dns_dir = os.path.dirname(paths.dns)
782     dns_samldb_dir = os.path.join(dns_dir, "sam.ldb.d")
783
784     # Find the partitions and corresponding filenames
785     partfile = {}
786     res = samdb.search(base="@PARTITION", scope=ldb.SCOPE_BASE, attrs=["partition"])
787     for tmp in res[0]["partition"]:
788         (nc, fname) = tmp.split(':')
789         partfile[nc.upper()] = fname
790
791     # Create empty domain partition
792     domaindn = names.domaindn.upper()
793     domainpart_file = os.path.join(dns_dir, partfile[domaindn])
794     try:
795         os.mkdir(dns_samldb_dir)
796         file(domainpart_file, 'w').close()
797
798         # Fill the basedn and @OPTION records in domain partition
799         dom_ldb = samba.Ldb(domainpart_file)
800         domainguid_line = "objectGUID: %s\n-" % domainguid
801         descr = b64encode(get_domain_descriptor(domainsid))
802         setup_add_ldif(dom_ldb, setup_path("provision_basedn.ldif"), {
803             "DOMAINDN" : names.domaindn,
804             "DOMAINGUID" : domainguid_line,
805             "DOMAINSID" : str(domainsid),
806             "DESCRIPTOR" : descr})
807         setup_add_ldif(dom_ldb,
808             setup_path("provision_basedn_options.ldif"), None)
809     except:
810         logger.error(
811             "Failed to setup database for BIND, AD based DNS cannot be used")
812         raise
813
814     # This line is critical to the security of the whole scheme.
815     # We assume there is no secret data in the (to be left out of
816     # date and essentially read-only) config, schema and metadata partitions.
817     #
818     # Only the stub of the domain partition is created above.
819     #
820     # That way, things like the krbtgt key do not leak.
821     del partfile[domaindn]
822
823     # Link dns partitions and metadata
824     domainzonedn = "DC=DOMAINDNSZONES,%s" % names.domaindn.upper()
825     forestzonedn = "DC=FORESTDNSZONES,%s" % names.rootdn.upper()
826
827     domainzone_file = partfile[domainzonedn]
828     forestzone_file = partfile.get(forestzonedn)
829
830     metadata_file = "metadata.tdb"
831     try:
832         os.link(os.path.join(samldb_dir, metadata_file),
833             os.path.join(dns_samldb_dir, metadata_file))
834         os.link(os.path.join(private_dir, domainzone_file),
835             os.path.join(dns_dir, domainzone_file))
836         if forestzone_file:
837             os.link(os.path.join(private_dir, forestzone_file),
838                     os.path.join(dns_dir, forestzone_file))
839     except OSError:
840         logger.error(
841             "Failed to setup database for BIND, AD based DNS cannot be used")
842         raise
843     del partfile[domainzonedn]
844     if forestzone_file:
845         del partfile[forestzonedn]
846
847     # Copy root, config, schema partitions (and any other if any)
848     # Since samdb is open in the current process, copy them in a child process
849     try:
850         tdb_copy(os.path.join(private_dir, "sam.ldb"),
851                  os.path.join(dns_dir, "sam.ldb"))
852         for nc in partfile:
853             pfile = partfile[nc]
854             tdb_copy(os.path.join(private_dir, pfile),
855                      os.path.join(dns_dir, pfile))
856     except:
857         logger.error(
858             "Failed to setup database for BIND, AD based DNS cannot be used")
859         raise
860
861     # Give bind read/write permissions dns partitions
862     if paths.bind_gid is not None:
863         try:
864             os.chown(samldb_dir, -1, paths.bind_gid)
865             os.chmod(samldb_dir, 0750)
866
867             for dirname, dirs, files in os.walk(dns_dir):
868                 for d in dirs:
869                     dpath = os.path.join(dirname, d)
870                     os.chown(dpath, -1, paths.bind_gid)
871                     os.chmod(dpath, 0770)
872                 for f in files:
873                     if f.endswith('.ldb') or f.endswith('.tdb'):
874                         fpath = os.path.join(dirname, f)
875                         os.chown(fpath, -1, paths.bind_gid)
876                         os.chmod(fpath, 0660)
877         except OSError:
878             if not os.environ.has_key('SAMBA_SELFTEST'):
879                 logger.error(
880                     "Failed to set permissions to sam.ldb* files, fix manually")
881     else:
882         if not os.environ.has_key('SAMBA_SELFTEST'):
883             logger.warning("""Unable to find group id for BIND,
884                 set permissions to sam.ldb* files manually""")
885
886
887 def create_dns_update_list(lp, logger, paths):
888     """Write out a dns_update_list file"""
889     # note that we use no variable substitution on this file
890     # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
891     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
892     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
893
894
895 def create_named_conf(paths, realm, dnsdomain, dns_backend, logger):
896     """Write out a file containing zone statements suitable for inclusion in a
897     named.conf file (including GSS-TSIG configuration).
898
899     :param paths: all paths
900     :param realm: Realm name
901     :param dnsdomain: DNS Domain name
902     :param dns_backend: DNS backend type
903     :param keytab_name: File name of DNS keytab file
904     :param logger: Logger object
905     """
906
907     # TODO: This really should have been done as a top level import.
908     # It is done here to avoid a depencency loop.  That is, we move
909     # ProvisioningError to another file, and have all the provision
910     # scripts import it from there.
911
912     from samba.provision import ProvisioningError
913
914     if dns_backend == "BIND9_FLATFILE":
915         setup_file(setup_path("named.conf"), paths.namedconf, {
916                     "DNSDOMAIN": dnsdomain,
917                     "REALM": realm,
918                     "ZONE_FILE": paths.dns,
919                     "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
920                     "NAMED_CONF": paths.namedconf,
921                     "NAMED_CONF_UPDATE": paths.namedconf_update
922                     })
923
924         setup_file(setup_path("named.conf.update"), paths.namedconf_update)
925
926     elif dns_backend == "BIND9_DLZ":
927         bind_info = subprocess.Popen(['named -V'], shell=True,
928                                      stdout=subprocess.PIPE,
929                                      stderr=subprocess.STDOUT,
930                                      cwd='.').communicate()[0]
931         bind9_8 = '#'
932         bind9_9 = '#'
933         bind9_10 = '#'
934         bind9_11 = '#'
935         if bind_info.upper().find('BIND 9.8') != -1:
936             bind9_8 = ''
937         elif bind_info.upper().find('BIND 9.9') != -1:
938             bind9_9 = ''
939         elif bind_info.upper().find('BIND 9.10') != -1:
940             bind9_10 = ''
941         elif bind_info.upper().find('BIND 9.11') != -1:
942             bind9_11 = ''
943         elif bind_info.upper().find('BIND 9.7') != -1:
944             raise ProvisioningError("DLZ option incompatible with BIND 9.7.")
945         else:
946             logger.warning("BIND version unknown, please modify %s manually." % paths.namedconf)
947         setup_file(setup_path("named.conf.dlz"), paths.namedconf, {
948                     "NAMED_CONF": paths.namedconf,
949                     "MODULESDIR" : samba.param.modules_dir(),
950                     "BIND9_8" : bind9_8,
951                     "BIND9_9" : bind9_9,
952                     "BIND9_10" : bind9_10,
953                     "BIND9_11" : bind9_11
954                     })
955
956
957 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
958     keytab_name):
959     """Write out a file containing zone statements suitable for inclusion in a
960     named.conf file (including GSS-TSIG configuration).
961
962     :param path: Path of the new named.conf file.
963     :param realm: Realm name
964     :param dnsdomain: DNS Domain name
965     :param private_dir: Path to private directory
966     :param keytab_name: File name of DNS keytab file
967     """
968     setup_file(setup_path("named.txt"), path, {
969             "DNSDOMAIN": dnsdomain,
970             "DNSNAME" : dnsname,
971             "REALM": realm,
972             "DNS_KEYTAB": keytab_name,
973             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
974             "PRIVATE_DIR": private_dir
975         })
976
977
978 def is_valid_dns_backend(dns_backend):
979     return dns_backend in ("BIND9_FLATFILE", "BIND9_DLZ", "SAMBA_INTERNAL", "NONE")
980
981
982 def is_valid_os_level(os_level):
983     return DS_DOMAIN_FUNCTION_2000 <= os_level <= DS_DOMAIN_FUNCTION_2012_R2
984
985
986 def create_dns_legacy(samdb, domainsid, forestdn, dnsadmins_sid):
987     # Set up MicrosoftDNS container
988     add_dns_container(samdb, forestdn, "CN=System", domainsid, dnsadmins_sid)
989     # Add root servers
990     add_rootservers(samdb, forestdn, "CN=System")
991
992
993 def fill_dns_data_legacy(samdb, domainsid, forestdn, dnsdomain, site, hostname,
994                          hostip, hostip6, dnsadmins_sid):
995     # Add domain record
996     add_domain_record(samdb, forestdn, "CN=System", dnsdomain, domainsid,
997                       dnsadmins_sid)
998
999     # Add DNS records for a DC in domain
1000     add_dc_domain_records(samdb, forestdn, "CN=System", site, dnsdomain,
1001                           hostname, hostip, hostip6)
1002
1003
1004 def create_dns_partitions(samdb, domainsid, names, domaindn, forestdn,
1005                           dnsadmins_sid, fill_level):
1006     # Set up additional partitions (DomainDnsZones, ForstDnsZones)
1007     setup_dns_partitions(samdb, domainsid, domaindn, forestdn,
1008                         names.configdn, names.serverdn, fill_level)
1009
1010     # Set up MicrosoftDNS containers
1011     add_dns_container(samdb, domaindn, "DC=DomainDnsZones", domainsid,
1012                       dnsadmins_sid)
1013     if fill_level != FILL_SUBDOMAIN:
1014         add_dns_container(samdb, forestdn, "DC=ForestDnsZones", domainsid,
1015                           dnsadmins_sid, forest=True)
1016
1017
1018 def fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn,
1019                              dnsdomain, dnsforest, hostname, hostip, hostip6,
1020                              domainguid, ntdsguid, dnsadmins_sid, autofill=True,
1021                              fill_level=FILL_FULL):
1022     """Fill data in various AD partitions
1023
1024     :param samdb: LDB object connected to sam.ldb file
1025     :param domainsid: Domain SID (as dom_sid object)
1026     :param site: Site name to create hostnames in
1027     :param domaindn: DN of the domain
1028     :param forestdn: DN of the forest
1029     :param dnsdomain: DNS name of the domain
1030     :param dnsforest: DNS name of the forest
1031     :param hostname: Host name of this DC
1032     :param hostip: IPv4 addresses
1033     :param hostip6: IPv6 addresses
1034     :param domainguid: Domain GUID
1035     :param ntdsguid: NTDS GUID
1036     :param dnsadmins_sid: SID for DnsAdmins group
1037     :param autofill: Create DNS records (using fixed template)
1038     """
1039
1040     ##### Set up DC=DomainDnsZones,<DOMAINDN>
1041     # Add rootserver records
1042     add_rootservers(samdb, domaindn, "DC=DomainDnsZones")
1043
1044     # Add domain record
1045     add_domain_record(samdb, domaindn, "DC=DomainDnsZones", dnsdomain,
1046                       domainsid, dnsadmins_sid)
1047
1048     # Add DNS records for a DC in domain
1049     if autofill:
1050         add_dc_domain_records(samdb, domaindn, "DC=DomainDnsZones", site,
1051                               dnsdomain, hostname, hostip, hostip6)
1052
1053     if fill_level != FILL_SUBDOMAIN:
1054         ##### Set up DC=ForestDnsZones,<FORESTDN>
1055         # Add _msdcs record
1056         add_msdcs_record(samdb, forestdn, "DC=ForestDnsZones", dnsforest)
1057
1058         # Add DNS records for a DC in forest
1059         if autofill:
1060             add_dc_msdcs_records(samdb, forestdn, "DC=ForestDnsZones", site,
1061                                  dnsforest, hostname, hostip, hostip6,
1062                                  domainguid, ntdsguid)
1063
1064
1065 def setup_ad_dns(samdb, secretsdb, names, paths, lp, logger,
1066         dns_backend, os_level, dnspass=None, hostip=None, hostip6=None,
1067         targetdir=None, fill_level=FILL_FULL):
1068     """Provision DNS information (assuming GC role)
1069
1070     :param samdb: LDB object connected to sam.ldb file
1071     :param secretsdb: LDB object connected to secrets.ldb file
1072     :param names: Names shortcut
1073     :param paths: Paths shortcut
1074     :param lp: Loadparm object
1075     :param logger: Logger object
1076     :param dns_backend: Type of DNS backend
1077     :param os_level: Functional level (treated as os level)
1078     :param dnspass: Password for bind's DNS account
1079     :param hostip: IPv4 address
1080     :param hostip6: IPv6 address
1081     :param targetdir: Target directory for creating DNS-related files for BIND9
1082     """
1083
1084     if not is_valid_dns_backend(dns_backend):
1085         raise Exception("Invalid dns backend: %r" % dns_backend)
1086
1087     if not is_valid_os_level(os_level):
1088         raise Exception("Invalid os level: %r" % os_level)
1089
1090     if dns_backend == "NONE":
1091         logger.info("No DNS backend set, not configuring DNS")
1092         return
1093
1094     # Add dns accounts (DnsAdmins, DnsUpdateProxy) in domain
1095     logger.info("Adding DNS accounts")
1096     add_dns_accounts(samdb, names.domaindn)
1097
1098     # If dns_backend is BIND9_FLATFILE
1099     #   Populate only CN=MicrosoftDNS,CN=System,<DOMAINDN>
1100     #
1101     # If dns_backend is SAMBA_INTERNAL or BIND9_DLZ
1102     #   Populate DNS partitions
1103
1104     # If os_level < 2003 (DS_DOMAIN_FUNCTION_2000)
1105     #   All dns records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1106     #
1107     # If os_level >= 2003 (DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008,
1108     #                        DS_DOMAIN_FUNCTION_2008_R2)
1109     #   Root server records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1110     #   Domain records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1111     #   Domain records are in CN=MicrosoftDNS,DC=DomainDnsZones,<DOMAINDN>
1112     #   Forest records are in CN=MicrosoftDNS,DC=ForestDnsZones,<FORESTDN>
1113     domaindn = names.domaindn
1114     forestdn = samdb.get_root_basedn().get_linearized()
1115
1116     dnsdomain = names.dnsdomain.lower()
1117     dnsforest = dnsdomain
1118
1119     site = names.sitename
1120
1121     hostname = names.netbiosname.lower()
1122
1123     dnsadmins_sid = get_dnsadmins_sid(samdb, domaindn)
1124     domainguid = get_domainguid(samdb, domaindn)
1125
1126     samdb.transaction_start()
1127     try:
1128         # Create CN=System
1129         logger.info("Creating CN=MicrosoftDNS,CN=System,%s" % domaindn)
1130         create_dns_legacy(samdb, names.domainsid, domaindn, dnsadmins_sid)
1131
1132         if os_level == DS_DOMAIN_FUNCTION_2000:
1133             # Populating legacy dns
1134             logger.info("Populating CN=MicrosoftDNS,CN=System,%s" % domaindn)
1135             fill_dns_data_legacy(samdb, names.domainsid, domaindn, dnsdomain, site,
1136                                  hostname, hostip, hostip6, dnsadmins_sid)
1137
1138         elif dns_backend in ("SAMBA_INTERNAL", "BIND9_DLZ") and \
1139                 os_level >= DS_DOMAIN_FUNCTION_2003:
1140
1141             # Create DNS partitions
1142             logger.info("Creating DomainDnsZones and ForestDnsZones partitions")
1143             create_dns_partitions(samdb, names.domainsid, names, domaindn, forestdn,
1144                                   dnsadmins_sid, fill_level)
1145
1146             # Populating dns partitions
1147             logger.info("Populating DomainDnsZones and ForestDnsZones partitions")
1148             fill_dns_data_partitions(samdb, names.domainsid, site, domaindn, forestdn,
1149                                      dnsdomain, dnsforest, hostname, hostip, hostip6,
1150                                      domainguid, names.ntdsguid, dnsadmins_sid,
1151                                      fill_level=fill_level)
1152
1153     except:
1154         samdb.transaction_cancel()
1155         raise
1156     else:
1157         samdb.transaction_commit()
1158
1159     if dns_backend.startswith("BIND9_"):
1160         setup_bind9_dns(samdb, secretsdb, names, paths, lp, logger,
1161                         dns_backend, os_level, site=site, dnspass=dnspass, hostip=hostip,
1162                         hostip6=hostip6, targetdir=targetdir)
1163
1164
1165 def setup_bind9_dns(samdb, secretsdb, names, paths, lp, logger,
1166         dns_backend, os_level, site=None, dnspass=None, hostip=None,
1167         hostip6=None, targetdir=None, key_version_number=None):
1168     """Provision DNS information (assuming BIND9 backend in DC role)
1169
1170     :param samdb: LDB object connected to sam.ldb file
1171     :param secretsdb: LDB object connected to secrets.ldb file
1172     :param names: Names shortcut
1173     :param paths: Paths shortcut
1174     :param lp: Loadparm object
1175     :param logger: Logger object
1176     :param dns_backend: Type of DNS backend
1177     :param os_level: Functional level (treated as os level)
1178     :param site: Site to create hostnames in
1179     :param dnspass: Password for bind's DNS account
1180     :param hostip: IPv4 address
1181     :param hostip6: IPv6 address
1182     :param targetdir: Target directory for creating DNS-related files for BIND9
1183     """
1184
1185     if (not is_valid_dns_backend(dns_backend) or
1186         not dns_backend.startswith("BIND9_")):
1187         raise Exception("Invalid dns backend: %r" % dns_backend)
1188
1189     if not is_valid_os_level(os_level):
1190         raise Exception("Invalid os level: %r" % os_level)
1191
1192     domaindn = names.domaindn
1193
1194     domainguid = get_domainguid(samdb, domaindn)
1195
1196     secretsdb_setup_dns(secretsdb, names,
1197                         paths.private_dir, realm=names.realm,
1198                         dnsdomain=names.dnsdomain,
1199                         dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1200                         key_version_number=key_version_number)
1201
1202     create_dns_dir(logger, paths)
1203
1204     if dns_backend == "BIND9_FLATFILE":
1205         create_zone_file(lp, logger, paths, targetdir, site=site,
1206                          dnsdomain=names.dnsdomain, hostip=hostip,
1207                          hostip6=hostip6, hostname=names.hostname,
1208                          realm=names.realm, domainguid=domainguid,
1209                          ntdsguid=names.ntdsguid)
1210
1211     if dns_backend == "BIND9_DLZ" and os_level >= DS_DOMAIN_FUNCTION_2003:
1212         create_samdb_copy(samdb, logger, paths, names, names.domainsid, domainguid)
1213
1214     create_named_conf(paths, realm=names.realm,
1215                       dnsdomain=names.dnsdomain, dns_backend=dns_backend,
1216                       logger=logger)
1217
1218     create_named_txt(paths.namedtxt,
1219                      realm=names.realm, dnsdomain=names.dnsdomain,
1220                      dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1221                      private_dir=paths.private_dir,
1222                      keytab_name=paths.dns_keytab)
1223     logger.info("See %s for an example configuration include file for BIND",
1224                 paths.namedconf)
1225     logger.info("and %s for further documentation required for secure DNS "
1226                 "updates", paths.namedtxt)