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