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