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