python: Remove unused parameter ‘lp’
[Samba.git] / python / samba / provision / sambadns.py
blobbdd2abce24ca5f15dba1da49daeda10523bd1adf
1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning DNS for a Samba4 server
4 # Copyright (C) Kai Blin <kai@samba.org> 2011
5 # Copyright (C) Amitay Isaacs <amitay@gmail.com> 2011
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.
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.
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/>.
21 """DNS-related provisioning"""
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.mdb_util import mdb_copy
33 from samba.ndr import ndr_pack, ndr_unpack
34 from samba import setup_file
35 from samba.dcerpc import dnsp, misc, security
36 from samba.dsdb import (
37 DS_DOMAIN_FUNCTION_2000,
38 DS_DOMAIN_FUNCTION_2003,
39 DS_DOMAIN_FUNCTION_2016,
40 DS_GUID_USERS_CONTAINER
42 from samba.descriptor import (
43 get_deletedobjects_descriptor,
44 get_domain_descriptor,
45 get_domain_delete_protected1_descriptor,
46 get_domain_delete_protected2_descriptor,
47 get_dns_partition_descriptor,
48 get_dns_forest_microsoft_dns_descriptor,
49 get_dns_domain_microsoft_dns_descriptor
51 from samba.provision.common import (
52 setup_path,
53 setup_add_ldif,
54 setup_modify_ldif,
55 setup_ldb,
56 FILL_FULL,
57 FILL_SUBDOMAIN,
60 from samba.samdb import get_default_backend_store
61 from samba.common import get_string
63 def get_domainguid(samdb, domaindn):
64 res = samdb.search(base=domaindn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"])
65 domainguid = str(ndr_unpack(misc.GUID, res[0]["objectGUID"][0]))
66 return domainguid
69 def get_dnsadmins_sid(samdb, domaindn):
70 base_dn = "CN=DnsAdmins,%s" % samdb.get_wellknown_dn(ldb.Dn(samdb,
71 domaindn), DS_GUID_USERS_CONTAINER)
72 res = samdb.search(base=base_dn, scope=ldb.SCOPE_BASE, attrs=["objectSid"])
73 dnsadmins_sid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0])
74 return dnsadmins_sid
77 # Note: these classes are not quite the same as similar looking ones
78 # in ../dnsserver.py -- those ones are based on
79 # dnsserver.DNS_RPC_RECORD ([MS-DNSP]2.2.2.2.5 "DNS_RPC_RECORD"),
80 # these are based on dnsp.DnssrvRpcRecord ([MS-DNSP] 2.3.2.2
81 # "DnsRecord").
83 # They are not interchangeable or mergeable. If you're talking over
84 # the wire you want those other ones; these are the on-disk format.
86 class ARecord(dnsp.DnssrvRpcRecord):
88 def __init__(self, ip_addr, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE):
89 super(ARecord, self).__init__()
90 self.wType = dnsp.DNS_TYPE_A
91 self.rank = rank
92 self.dwSerial = serial
93 self.dwTtlSeconds = ttl
94 self.data = ip_addr
97 class AAAARecord(dnsp.DnssrvRpcRecord):
99 def __init__(self, ip6_addr, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE):
100 super(AAAARecord, self).__init__()
101 self.wType = dnsp.DNS_TYPE_AAAA
102 self.rank = rank
103 self.dwSerial = serial
104 self.dwTtlSeconds = ttl
105 self.data = ip6_addr
108 class CNAMERecord(dnsp.DnssrvRpcRecord):
110 def __init__(self, cname, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE):
111 super().__init__()
112 self.wType = dnsp.DNS_TYPE_CNAME
113 self.rank = rank
114 self.dwSerial = serial
115 self.dwTtlSeconds = ttl
116 self.data = cname
119 class NSRecord(dnsp.DnssrvRpcRecord):
121 def __init__(self, dns_server, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE):
122 super(NSRecord, self).__init__()
123 self.wType = dnsp.DNS_TYPE_NS
124 self.rank = rank
125 self.dwSerial = serial
126 self.dwTtlSeconds = ttl
127 self.data = dns_server
130 class SOARecord(dnsp.DnssrvRpcRecord):
132 def __init__(self, mname, rname, serial=1, refresh=900, retry=600,
133 expire=86400, minimum=3600, ttl=3600, rank=dnsp.DNS_RANK_ZONE):
134 super(SOARecord, self).__init__()
135 self.wType = dnsp.DNS_TYPE_SOA
136 self.rank = rank
137 self.dwSerial = serial
138 self.dwTtlSeconds = ttl
139 soa = dnsp.soa()
140 soa.serial = serial
141 soa.refresh = refresh
142 soa.retry = retry
143 soa.expire = expire
144 soa.mname = mname
145 soa.rname = rname
146 soa.minimum = minimum
147 self.data = soa
150 class SRVRecord(dnsp.DnssrvRpcRecord):
152 def __init__(self, target, port, priority=0, weight=100, serial=1, ttl=900,
153 rank=dnsp.DNS_RANK_ZONE):
154 super(SRVRecord, self).__init__()
155 self.wType = dnsp.DNS_TYPE_SRV
156 self.rank = rank
157 self.dwSerial = serial
158 self.dwTtlSeconds = ttl
159 srv = dnsp.srv()
160 srv.nameTarget = target
161 srv.wPort = port
162 srv.wPriority = priority
163 srv.wWeight = weight
164 self.data = srv
167 class TXTRecord(dnsp.DnssrvRpcRecord):
169 def __init__(self, slist, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE):
170 super(TXTRecord, self).__init__()
171 self.wType = dnsp.DNS_TYPE_TXT
172 self.rank = rank
173 self.dwSerial = serial
174 self.dwTtlSeconds = ttl
175 stringlist = dnsp.string_list()
176 stringlist.count = len(slist)
177 stringlist.str = slist
178 self.data = stringlist
181 class TypeProperty(dnsp.DnsProperty):
183 def __init__(self, zone_type=dnsp.DNS_ZONE_TYPE_PRIMARY):
184 super(TypeProperty, self).__init__()
185 self.wDataLength = 1
186 self.version = 1
187 self.id = dnsp.DSPROPERTY_ZONE_TYPE
188 self.data = zone_type
191 class AllowUpdateProperty(dnsp.DnsProperty):
193 def __init__(self, allow_update=dnsp.DNS_ZONE_UPDATE_SECURE):
194 super(AllowUpdateProperty, self).__init__()
195 self.wDataLength = 1
196 self.version = 1
197 self.id = dnsp.DSPROPERTY_ZONE_ALLOW_UPDATE
198 self.data = allow_update
201 class SecureTimeProperty(dnsp.DnsProperty):
203 def __init__(self, secure_time=0):
204 super(SecureTimeProperty, self).__init__()
205 self.wDataLength = 1
206 self.version = 1
207 self.id = dnsp.DSPROPERTY_ZONE_SECURE_TIME
208 self.data = secure_time
211 class NorefreshIntervalProperty(dnsp.DnsProperty):
213 def __init__(self, norefresh_interval=0):
214 super(NorefreshIntervalProperty, self).__init__()
215 self.wDataLength = 1
216 self.version = 1
217 self.id = dnsp.DSPROPERTY_ZONE_NOREFRESH_INTERVAL
218 self.data = norefresh_interval
221 class RefreshIntervalProperty(dnsp.DnsProperty):
223 def __init__(self, refresh_interval=0):
224 super(RefreshIntervalProperty, self).__init__()
225 self.wDataLength = 1
226 self.version = 1
227 self.id = dnsp.DSPROPERTY_ZONE_REFRESH_INTERVAL
228 self.data = refresh_interval
231 class AgingStateProperty(dnsp.DnsProperty):
233 def __init__(self, aging_enabled=0):
234 super(AgingStateProperty, self).__init__()
235 self.wDataLength = 1
236 self.version = 1
237 self.id = dnsp.DSPROPERTY_ZONE_AGING_STATE
238 self.data = aging_enabled
241 class AgingEnabledTimeProperty(dnsp.DnsProperty):
243 def __init__(self, next_cycle_hours=0):
244 super(AgingEnabledTimeProperty, self).__init__()
245 self.wDataLength = 1
246 self.version = 1
247 self.id = dnsp.DSPROPERTY_ZONE_AGING_ENABLED_TIME
248 self.data = next_cycle_hours
251 def setup_dns_partitions(samdb, domainsid, domaindn, forestdn, configdn,
252 serverdn, fill_level):
253 domainzone_dn = "DC=DomainDnsZones,%s" % domaindn
254 forestzone_dn = "DC=ForestDnsZones,%s" % forestdn
255 descriptor = get_dns_partition_descriptor(domainsid)
256 deletedobjects_desc = get_deletedobjects_descriptor(domainsid)
258 setup_add_ldif(samdb, setup_path("provision_dnszones_partitions.ldif"), {
259 "ZONE_DN": domainzone_dn,
260 "SECDESC": b64encode(descriptor).decode('utf8')
262 if fill_level != FILL_SUBDOMAIN:
263 setup_add_ldif(samdb, setup_path("provision_dnszones_partitions.ldif"), {
264 "ZONE_DN": forestzone_dn,
265 "SECDESC": b64encode(descriptor).decode('utf8')
268 domainzone_guid = str(uuid.uuid4())
269 domainzone_dns = ldb.Dn(samdb, domainzone_dn).canonical_ex_str().strip()
271 protected1_desc = get_domain_delete_protected1_descriptor(domainsid)
272 protected2_desc = get_domain_delete_protected2_descriptor(domainsid)
273 setup_add_ldif(samdb, setup_path("provision_dnszones_add.ldif"), {
274 "ZONE_DN": domainzone_dn,
275 "ZONE_GUID": domainzone_guid,
276 "ZONE_DNS": domainzone_dns,
277 "CONFIGDN": configdn,
278 "SERVERDN": serverdn,
279 "DELETEDOBJECTS_DESCRIPTOR": b64encode(deletedobjects_desc).decode('utf8'),
280 "LOSTANDFOUND_DESCRIPTOR": b64encode(protected2_desc).decode('utf8'),
281 "INFRASTRUCTURE_DESCRIPTOR": b64encode(protected1_desc).decode('utf8'),
283 setup_modify_ldif(samdb, setup_path("provision_dnszones_modify.ldif"), {
284 "CONFIGDN": configdn,
285 "SERVERDN": serverdn,
286 "ZONE_DN": domainzone_dn,
289 if fill_level != FILL_SUBDOMAIN:
290 forestzone_guid = str(uuid.uuid4())
291 forestzone_dns = ldb.Dn(samdb, forestzone_dn).canonical_ex_str().strip()
293 setup_add_ldif(samdb, setup_path("provision_dnszones_add.ldif"), {
294 "ZONE_DN": forestzone_dn,
295 "ZONE_GUID": forestzone_guid,
296 "ZONE_DNS": forestzone_dns,
297 "CONFIGDN": configdn,
298 "SERVERDN": serverdn,
299 "DELETEDOBJECTS_DESCRIPTOR": b64encode(deletedobjects_desc).decode('utf8'),
300 "LOSTANDFOUND_DESCRIPTOR": b64encode(protected2_desc).decode('utf8'),
301 "INFRASTRUCTURE_DESCRIPTOR": b64encode(protected1_desc).decode('utf8'),
303 setup_modify_ldif(samdb, setup_path("provision_dnszones_modify.ldif"), {
304 "CONFIGDN": configdn,
305 "SERVERDN": serverdn,
306 "ZONE_DN": forestzone_dn,
310 def add_dns_accounts(samdb, domaindn):
311 setup_add_ldif(samdb, setup_path("provision_dns_accounts_add.ldif"), {
312 "DOMAINDN": domaindn,
316 def add_dns_container(samdb, domaindn, prefix, domain_sid, dnsadmins_sid, forest=False):
317 name_map = {'DnsAdmins': str(dnsadmins_sid)}
318 if forest is True:
319 sd_val = get_dns_forest_microsoft_dns_descriptor(domain_sid,
320 name_map=name_map)
321 else:
322 sd_val = get_dns_domain_microsoft_dns_descriptor(domain_sid,
323 name_map=name_map)
324 # CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
325 msg = ldb.Message(ldb.Dn(samdb, "CN=MicrosoftDNS,%s,%s" % (prefix, domaindn)))
326 msg["objectClass"] = ["top", "container"]
327 msg["nTSecurityDescriptor"] = \
328 ldb.MessageElement(sd_val, ldb.FLAG_MOD_ADD,
329 "nTSecurityDescriptor")
330 samdb.add(msg)
333 def add_rootservers(samdb, domaindn, prefix):
334 # https://www.internic.net/zones/named.root
335 rootservers = {}
336 rootservers["a.root-servers.net"] = "198.41.0.4"
337 rootservers["b.root-servers.net"] = "192.228.79.201"
338 rootservers["c.root-servers.net"] = "192.33.4.12"
339 rootservers["d.root-servers.net"] = "199.7.91.13"
340 rootservers["e.root-servers.net"] = "192.203.230.10"
341 rootservers["f.root-servers.net"] = "192.5.5.241"
342 rootservers["g.root-servers.net"] = "192.112.36.4"
343 rootservers["h.root-servers.net"] = "198.97.190.53"
344 rootservers["i.root-servers.net"] = "192.36.148.17"
345 rootservers["j.root-servers.net"] = "192.58.128.30"
346 rootservers["k.root-servers.net"] = "193.0.14.129"
347 rootservers["l.root-servers.net"] = "199.7.83.42"
348 rootservers["m.root-servers.net"] = "202.12.27.33"
350 rootservers_v6 = {}
351 rootservers_v6["a.root-servers.net"] = "2001:503:ba3e::2:30"
352 rootservers_v6["b.root-servers.net"] = "2001:500:84::b"
353 rootservers_v6["c.root-servers.net"] = "2001:500:2::c"
354 rootservers_v6["d.root-servers.net"] = "2001:500:2d::d"
355 rootservers_v6["e.root-servers.net"] = "2001:500:a8::e"
356 rootservers_v6["f.root-servers.net"] = "2001:500:2f::f"
357 rootservers_v6["g.root-servers.net"] = "2001:500:12::d0d"
358 rootservers_v6["h.root-servers.net"] = "2001:500:1::53"
359 rootservers_v6["i.root-servers.net"] = "2001:7fe::53"
360 rootservers_v6["j.root-servers.net"] = "2001:503:c27::2:30"
361 rootservers_v6["k.root-servers.net"] = "2001:7fd::1"
362 rootservers_v6["l.root-servers.net"] = "2001:500:9f::42"
363 rootservers_v6["m.root-servers.net"] = "2001:dc3::35"
365 container_dn = "DC=RootDNSServers,CN=MicrosoftDNS,%s,%s" % (prefix, domaindn)
367 # Add DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
368 msg = ldb.Message(ldb.Dn(samdb, container_dn))
369 props = []
370 props.append(ndr_pack(TypeProperty(zone_type=dnsp.DNS_ZONE_TYPE_CACHE)))
371 props.append(ndr_pack(AllowUpdateProperty(allow_update=dnsp.DNS_ZONE_UPDATE_OFF)))
372 props.append(ndr_pack(SecureTimeProperty()))
373 props.append(ndr_pack(NorefreshIntervalProperty()))
374 props.append(ndr_pack(RefreshIntervalProperty()))
375 props.append(ndr_pack(AgingStateProperty()))
376 props.append(ndr_pack(AgingEnabledTimeProperty()))
377 msg["objectClass"] = ["top", "dnsZone"]
378 msg["cn"] = ldb.MessageElement("Zone", ldb.FLAG_MOD_ADD, "cn")
379 msg["dNSProperty"] = ldb.MessageElement(props, ldb.FLAG_MOD_ADD, "dNSProperty")
380 samdb.add(msg)
382 # Add DC=@,DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
383 record = []
384 for rserver in rootservers:
385 record.append(ndr_pack(NSRecord(rserver, serial=0, ttl=0, rank=dnsp.DNS_RANK_ROOT_HINT)))
387 msg = ldb.Message(ldb.Dn(samdb, "DC=@,%s" % container_dn))
388 msg["objectClass"] = ["top", "dnsNode"]
389 msg["dnsRecord"] = ldb.MessageElement(record, ldb.FLAG_MOD_ADD, "dnsRecord")
390 samdb.add(msg)
392 # Add DC=<rootserver>,DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
393 for rserver in rootservers:
394 record = [ndr_pack(ARecord(rootservers[rserver], serial=0, ttl=0, rank=dnsp.DNS_RANK_ROOT_HINT))]
395 # Add AAAA record as well (How does W2K* add IPv6 records?)
396 # if rserver in rootservers_v6:
397 # record.append(ndr_pack(AAAARecord(rootservers_v6[rserver], serial=0, ttl=0)))
398 msg = ldb.Message(ldb.Dn(samdb, "DC=%s,%s" % (rserver, container_dn)))
399 msg["objectClass"] = ["top", "dnsNode"]
400 msg["dnsRecord"] = ldb.MessageElement(record, ldb.FLAG_MOD_ADD, "dnsRecord")
401 samdb.add(msg)
404 def add_at_record(samdb, container_dn, prefix, hostname, dnsdomain, hostip, hostip6):
406 fqdn_hostname = "%s.%s" % (hostname, dnsdomain)
408 at_records = []
410 # SOA record
411 at_soa_record = SOARecord(fqdn_hostname, "hostmaster.%s" % dnsdomain)
412 at_records.append(ndr_pack(at_soa_record))
414 # NS record
415 at_ns_record = NSRecord(fqdn_hostname)
416 at_records.append(ndr_pack(at_ns_record))
418 if hostip is not None:
419 # A record
420 at_a_record = ARecord(hostip)
421 at_records.append(ndr_pack(at_a_record))
423 if hostip6 is not None:
424 # AAAA record
425 at_aaaa_record = AAAARecord(hostip6)
426 at_records.append(ndr_pack(at_aaaa_record))
428 msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
429 msg["objectClass"] = ["top", "dnsNode"]
430 msg["dnsRecord"] = ldb.MessageElement(at_records, ldb.FLAG_MOD_ADD, "dnsRecord")
431 samdb.add(msg)
434 def add_srv_record(samdb, container_dn, prefix, host, port):
435 srv_record = SRVRecord(host, port)
436 msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
437 msg["objectClass"] = ["top", "dnsNode"]
438 msg["dnsRecord"] = ldb.MessageElement(ndr_pack(srv_record), ldb.FLAG_MOD_ADD, "dnsRecord")
439 samdb.add(msg)
442 def add_ns_record(samdb, container_dn, prefix, host):
443 ns_record = NSRecord(host)
444 msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
445 msg["objectClass"] = ["top", "dnsNode"]
446 msg["dnsRecord"] = ldb.MessageElement(ndr_pack(ns_record), ldb.FLAG_MOD_ADD, "dnsRecord")
447 samdb.add(msg)
450 def add_ns_glue_record(samdb, container_dn, prefix, host):
451 ns_record = NSRecord(host, rank=dnsp.DNS_RANK_NS_GLUE)
452 msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
453 msg["objectClass"] = ["top", "dnsNode"]
454 msg["dnsRecord"] = ldb.MessageElement(ndr_pack(ns_record), ldb.FLAG_MOD_ADD, "dnsRecord")
455 samdb.add(msg)
458 def add_cname_record(samdb, container_dn, prefix, host):
459 cname_record = CNAMERecord(host)
460 msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
461 msg["objectClass"] = ["top", "dnsNode"]
462 msg["dnsRecord"] = ldb.MessageElement(ndr_pack(cname_record), ldb.FLAG_MOD_ADD, "dnsRecord")
463 samdb.add(msg)
466 def add_host_record(samdb, container_dn, prefix, hostip, hostip6):
467 host_records = []
468 if hostip:
469 a_record = ARecord(hostip)
470 host_records.append(ndr_pack(a_record))
471 if hostip6:
472 aaaa_record = AAAARecord(hostip6)
473 host_records.append(ndr_pack(aaaa_record))
474 if host_records:
475 msg = ldb.Message(ldb.Dn(samdb, "%s,%s" % (prefix, container_dn)))
476 msg["objectClass"] = ["top", "dnsNode"]
477 msg["dnsRecord"] = ldb.MessageElement(host_records, ldb.FLAG_MOD_ADD, "dnsRecord")
478 samdb.add(msg)
481 def add_domain_record(samdb, domaindn, prefix, dnsdomain, domainsid, dnsadmins_sid):
482 # DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
483 sddl = "O:SYG:BAD:AI" \
484 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)" \
485 "(A;;CC;;;AU)" \
486 "(A;;RPLCLORC;;;WD)" \
487 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
488 "(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)" \
489 "(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;%s)" \
490 "(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)" \
491 "(OA;CIID;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
492 "(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
493 "(A;CIID;LC;;;RU)" \
494 "(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
495 "S:AI" % dnsadmins_sid
496 sec = security.descriptor.from_sddl(sddl, domainsid)
497 props = []
498 props.append(ndr_pack(TypeProperty()))
499 props.append(ndr_pack(AllowUpdateProperty()))
500 props.append(ndr_pack(SecureTimeProperty()))
501 props.append(ndr_pack(NorefreshIntervalProperty(norefresh_interval=168)))
502 props.append(ndr_pack(RefreshIntervalProperty(refresh_interval=168)))
503 props.append(ndr_pack(AgingStateProperty()))
504 props.append(ndr_pack(AgingEnabledTimeProperty()))
505 msg = ldb.Message(ldb.Dn(samdb, "DC=%s,CN=MicrosoftDNS,%s,%s" % (dnsdomain, prefix, domaindn)))
506 msg["objectClass"] = ["top", "dnsZone"]
507 msg["ntSecurityDescriptor"] = \
508 ldb.MessageElement(ndr_pack(sec),
509 ldb.FLAG_MOD_ADD,
510 "nTSecurityDescriptor")
511 msg["dNSProperty"] = ldb.MessageElement(props, ldb.FLAG_MOD_ADD, "dNSProperty")
512 samdb.add(msg)
515 def add_msdcs_record(samdb, forestdn, prefix, dnsforest):
516 # DC=_msdcs.<DNSFOREST>,CN=MicrosoftDNS,<PREFIX>,<FORESTDN>
517 msg = ldb.Message(ldb.Dn(samdb, "DC=_msdcs.%s,CN=MicrosoftDNS,%s,%s" %
518 (dnsforest, prefix, forestdn)))
519 msg["objectClass"] = ["top", "dnsZone"]
520 samdb.add(msg)
523 def add_dc_domain_records(samdb, domaindn, prefix, site, dnsdomain, hostname,
524 hostip, hostip6):
526 fqdn_hostname = "%s.%s" % (hostname, dnsdomain)
528 # Set up domain container - DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
529 domain_container_dn = ldb.Dn(samdb, "DC=%s,CN=MicrosoftDNS,%s,%s" %
530 (dnsdomain, prefix, domaindn))
532 # DC=@ record
533 add_at_record(samdb, domain_container_dn, "DC=@", hostname, dnsdomain,
534 hostip, hostip6)
536 # DC=<HOSTNAME> record
537 add_host_record(samdb, domain_container_dn, "DC=%s" % hostname, hostip,
538 hostip6)
540 # DC=_kerberos._tcp record
541 add_srv_record(samdb, domain_container_dn, "DC=_kerberos._tcp",
542 fqdn_hostname, 88)
544 # DC=_kerberos._tcp.<SITENAME>._sites record
545 add_srv_record(samdb, domain_container_dn, "DC=_kerberos._tcp.%s._sites" %
546 site, fqdn_hostname, 88)
548 # DC=_kerberos._udp record
549 add_srv_record(samdb, domain_container_dn, "DC=_kerberos._udp",
550 fqdn_hostname, 88)
552 # DC=_kpasswd._tcp record
553 add_srv_record(samdb, domain_container_dn, "DC=_kpasswd._tcp",
554 fqdn_hostname, 464)
556 # DC=_kpasswd._udp record
557 add_srv_record(samdb, domain_container_dn, "DC=_kpasswd._udp",
558 fqdn_hostname, 464)
560 # DC=_ldap._tcp record
561 add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp", fqdn_hostname,
562 389)
564 # DC=_ldap._tcp.<SITENAME>._sites record
565 add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp.%s._sites" %
566 site, fqdn_hostname, 389)
568 # FIXME: The number of SRV records depend on the various roles this DC has.
569 # _gc and _msdcs records are added if the we are the forest dc and not subdomain dc
571 # Assumption: current DC is GC and add all the entries
573 # DC=_gc._tcp record
574 add_srv_record(samdb, domain_container_dn, "DC=_gc._tcp", fqdn_hostname,
575 3268)
577 # DC=_gc._tcp.<SITENAME>,_sites record
578 add_srv_record(samdb, domain_container_dn, "DC=_gc._tcp.%s._sites" % site,
579 fqdn_hostname, 3268)
581 # DC=_msdcs record
582 add_ns_glue_record(samdb, domain_container_dn, "DC=_msdcs", fqdn_hostname)
584 # FIXME: Following entries are added only if DomainDnsZones and ForestDnsZones partitions
585 # are created
587 # Assumption: Additional entries won't hurt on os_level = 2000
589 # DC=_ldap._tcp.<SITENAME>._sites.DomainDnsZones
590 add_srv_record(samdb, domain_container_dn,
591 "DC=_ldap._tcp.%s._sites.DomainDnsZones" % site, fqdn_hostname,
592 389)
594 # DC=_ldap._tcp.<SITENAME>._sites.ForestDnsZones
595 add_srv_record(samdb, domain_container_dn,
596 "DC=_ldap._tcp.%s._sites.ForestDnsZones" % site, fqdn_hostname,
597 389)
599 # DC=_ldap._tcp.DomainDnsZones
600 add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp.DomainDnsZones",
601 fqdn_hostname, 389)
603 # DC=_ldap._tcp.ForestDnsZones
604 add_srv_record(samdb, domain_container_dn, "DC=_ldap._tcp.ForestDnsZones",
605 fqdn_hostname, 389)
607 # DC=DomainDnsZones
608 add_host_record(samdb, domain_container_dn, "DC=DomainDnsZones", hostip,
609 hostip6)
611 # DC=ForestDnsZones
612 add_host_record(samdb, domain_container_dn, "DC=ForestDnsZones", hostip,
613 hostip6)
616 def add_dc_msdcs_records(samdb, forestdn, prefix, site, dnsforest, hostname,
617 hostip, hostip6, domainguid, ntdsguid):
619 fqdn_hostname = "%s.%s" % (hostname, dnsforest)
621 # Set up forest container - DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
622 forest_container_dn = ldb.Dn(samdb, "DC=_msdcs.%s,CN=MicrosoftDNS,%s,%s" %
623 (dnsforest, prefix, forestdn))
625 # DC=@ record
626 add_at_record(samdb, forest_container_dn, "DC=@", hostname, dnsforest,
627 None, None)
629 # DC=_kerberos._tcp.dc record
630 add_srv_record(samdb, forest_container_dn, "DC=_kerberos._tcp.dc",
631 fqdn_hostname, 88)
633 # DC=_kerberos._tcp.<SITENAME>._sites.dc record
634 add_srv_record(samdb, forest_container_dn,
635 "DC=_kerberos._tcp.%s._sites.dc" % site, fqdn_hostname, 88)
637 # DC=_ldap._tcp.dc record
638 add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.dc",
639 fqdn_hostname, 389)
641 # DC=_ldap._tcp.<SITENAME>._sites.dc record
642 add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.%s._sites.dc" %
643 site, fqdn_hostname, 389)
645 # DC=_ldap._tcp.<SITENAME>._sites.gc record
646 add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.%s._sites.gc" %
647 site, fqdn_hostname, 3268)
649 # DC=_ldap._tcp.gc record
650 add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.gc",
651 fqdn_hostname, 3268)
653 # DC=_ldap._tcp.pdc record
654 add_srv_record(samdb, forest_container_dn, "DC=_ldap._tcp.pdc",
655 fqdn_hostname, 389)
657 # DC=gc record
658 add_host_record(samdb, forest_container_dn, "DC=gc", hostip, hostip6)
660 # DC=_ldap._tcp.<DOMAINGUID>.domains record
661 add_srv_record(samdb, forest_container_dn,
662 "DC=_ldap._tcp.%s.domains" % domainguid, fqdn_hostname, 389)
664 # DC=<NTDSGUID>
665 add_cname_record(samdb, forest_container_dn, "DC=%s" % ntdsguid,
666 fqdn_hostname)
669 def secretsdb_setup_dns(secretsdb, names, private_dir, binddns_dir, realm,
670 dnsdomain, dns_keytab_path, dnspass, key_version_number):
671 """Add DNS specific bits to a secrets database.
673 :param secretsdb: Ldb Handle to the secrets database
674 :param names: Names shortcut
675 :param machinepass: Machine password
677 try:
678 os.unlink(os.path.join(private_dir, dns_keytab_path))
679 os.unlink(os.path.join(binddns_dir, dns_keytab_path))
680 except OSError:
681 pass
683 if key_version_number is None:
684 key_version_number = 1
686 # This will create the dns.keytab file in the private_dir when it is
687 # committed!
688 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
689 "REALM": realm,
690 "DNSDOMAIN": dnsdomain,
691 "DNS_KEYTAB": dns_keytab_path,
692 "DNSPASS_B64": b64encode(dnspass.encode('utf-8')).decode('utf8'),
693 "KEY_VERSION_NUMBER": str(key_version_number),
694 "HOSTNAME": names.hostname,
695 "DNSNAME": '%s.%s' % (
696 names.netbiosname.lower(), names.dnsdomain.lower())
700 def create_dns_dir(logger, paths):
701 """(Re)create the DNS directory and chown it to bind.
703 :param logger: Logger object
704 :param paths: paths object
706 dns_dir = os.path.dirname(paths.dns)
708 try:
709 shutil.rmtree(dns_dir, True)
710 except OSError:
711 pass
713 os.mkdir(dns_dir, 0o770)
715 if paths.bind_gid is not None:
716 try:
717 os.chown(dns_dir, -1, paths.bind_gid)
718 # chmod needed to cope with umask
719 os.chmod(dns_dir, 0o770)
720 except OSError:
721 if 'SAMBA_SELFTEST' not in os.environ:
722 logger.error("Failed to chown %s to bind gid %u" % (
723 dns_dir, paths.bind_gid))
726 def create_dns_dir_keytab_link(logger, paths):
727 """Create link for BIND to DNS keytab
729 :param logger: Logger object
730 :param paths: paths object
732 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
733 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
735 if os.path.isfile(private_dns_keytab_path):
736 if os.path.isfile(bind_dns_keytab_path):
737 try:
738 os.unlink(bind_dns_keytab_path)
739 except OSError as e:
740 logger.error("Failed to remove %s: %s" %
741 (bind_dns_keytab_path, e.strerror))
743 # link the dns.keytab to the bind-dns directory
744 try:
745 os.link(private_dns_keytab_path, bind_dns_keytab_path)
746 except OSError as e:
747 logger.error("Failed to create link %s -> %s: %s" %
748 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
750 # chown the dns.keytab in the bind-dns directory
751 if paths.bind_gid is not None:
752 try:
753 os.chmod(paths.binddns_dir, 0o770)
754 os.chown(paths.binddns_dir, -1, paths.bind_gid)
755 except OSError:
756 if 'SAMBA_SELFTEST' not in os.environ:
757 logger.info("Failed to chown %s to bind gid %u",
758 paths.binddns_dir, paths.bind_gid)
759 try:
760 os.chmod(bind_dns_keytab_path, 0o640)
761 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
762 except OSError:
763 if 'SAMBA_SELFTEST' not in os.environ:
764 logger.info("Failed to chown %s to bind gid %u",
765 bind_dns_keytab_path, paths.bind_gid)
768 def create_zone_file(logger, paths, dnsdomain,
769 hostip, hostip6, hostname, realm, domainguid,
770 ntdsguid, site):
771 """Write out a DNS zone file, from the info in the current database.
773 :param paths: paths object
774 :param dnsdomain: DNS Domain name
775 :param domaindn: DN of the Domain
776 :param hostip: Local IPv4 IP
777 :param hostip6: Local IPv6 IP
778 :param hostname: Local hostname
779 :param realm: Realm name
780 :param domainguid: GUID of the domain.
781 :param ntdsguid: GUID of the hosts nTDSDSA record.
783 assert isinstance(domainguid, str)
785 if hostip6 is not None:
786 hostip6_base_line = " IN AAAA " + hostip6
787 hostip6_host_line = hostname + " IN AAAA " + hostip6
788 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
789 else:
790 hostip6_base_line = ""
791 hostip6_host_line = ""
792 gc_msdcs_ip6_line = ""
794 if hostip is not None:
795 hostip_base_line = " IN A " + hostip
796 hostip_host_line = hostname + " IN A " + hostip
797 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
798 else:
799 hostip_base_line = ""
800 hostip_host_line = ""
801 gc_msdcs_ip_line = ""
803 setup_file(setup_path("provision.zone"), paths.dns, {
804 "HOSTNAME": hostname,
805 "DNSDOMAIN": dnsdomain,
806 "REALM": realm,
807 "HOSTIP_BASE_LINE": hostip_base_line,
808 "HOSTIP_HOST_LINE": hostip_host_line,
809 "DOMAINGUID": domainguid,
810 "DATESTRING": time.strftime("%Y%m%d%H"),
811 "DEFAULTSITE": site,
812 "NTDSGUID": ntdsguid,
813 "HOSTIP6_BASE_LINE": hostip6_base_line,
814 "HOSTIP6_HOST_LINE": hostip6_host_line,
815 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
816 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
819 if paths.bind_gid is not None:
820 try:
821 os.chown(paths.dns, -1, paths.bind_gid)
822 # chmod needed to cope with umask
823 os.chmod(paths.dns, 0o664)
824 except OSError:
825 if 'SAMBA_SELFTEST' not in os.environ:
826 logger.error("Failed to chown %s to bind gid %u" % (
827 paths.dns, paths.bind_gid))
830 def create_samdb_copy(samdb, logger, paths, names, domainsid, domainguid):
831 """Create a copy of samdb and give write permissions to named for dns partitions
833 private_dir = paths.private_dir
834 samldb_dir = os.path.join(private_dir, "sam.ldb.d")
835 dns_dir = os.path.dirname(paths.dns)
836 dns_samldb_dir = os.path.join(dns_dir, "sam.ldb.d")
838 # Find the partitions and corresponding filenames
839 partfile = {}
840 res = samdb.search(base="@PARTITION",
841 scope=ldb.SCOPE_BASE,
842 attrs=["partition", "backendStore"])
843 for tmp in res[0]["partition"]:
844 (nc, fname) = str(tmp).split(':')
845 partfile[nc.upper()] = fname
847 backend_store = get_default_backend_store()
848 if "backendStore" in res[0]:
849 backend_store = str(res[0]["backendStore"][0])
851 # Create empty domain partition
853 domaindn = names.domaindn.upper()
854 domainpart_file = os.path.join(dns_dir, partfile[domaindn])
855 try:
856 os.mkdir(dns_samldb_dir)
857 open(domainpart_file, 'w').close()
859 # Fill the basedn and @OPTION records in domain partition
860 dom_url = "%s://%s" % (backend_store, domainpart_file)
861 dom_ldb = samba.Ldb(dom_url)
863 # We need the dummy main-domain DB to have the correct @INDEXLIST
864 index_res = samdb.search(base="@INDEXLIST", scope=ldb.SCOPE_BASE)
865 dom_ldb.add(index_res[0])
867 domainguid_line = "objectGUID: %s\n-" % domainguid
868 descr = b64encode(get_domain_descriptor(domainsid)).decode('utf8')
869 setup_add_ldif(dom_ldb, setup_path("provision_basedn.ldif"), {
870 "DOMAINDN": names.domaindn,
871 "DOMAINGUID": domainguid_line,
872 "DOMAINSID": str(domainsid),
873 "DESCRIPTOR": descr})
874 setup_add_ldif(dom_ldb,
875 setup_path("provision_basedn_options.ldif"), None)
877 except:
878 logger.error(
879 "Failed to setup database for BIND, AD based DNS cannot be used")
880 raise
882 # This line is critical to the security of the whole scheme.
883 # We assume there is no secret data in the (to be left out of
884 # date and essentially read-only) config, schema and metadata partitions.
886 # Only the stub of the domain partition is created above.
888 # That way, things like the krbtgt key do not leak.
889 del partfile[domaindn]
891 # Link dns partitions and metadata
892 domainzonedn = "DC=DOMAINDNSZONES,%s" % names.domaindn.upper()
893 forestzonedn = "DC=FORESTDNSZONES,%s" % names.rootdn.upper()
895 domainzone_file = partfile[domainzonedn]
896 forestzone_file = partfile.get(forestzonedn)
898 metadata_file = "metadata.tdb"
899 try:
900 os.link(os.path.join(samldb_dir, metadata_file),
901 os.path.join(dns_samldb_dir, metadata_file))
902 os.link(os.path.join(private_dir, domainzone_file),
903 os.path.join(dns_dir, domainzone_file))
904 if backend_store == "mdb":
905 # If the file is an lmdb data file need to link the
906 # lock file as well
907 os.link(os.path.join(private_dir, domainzone_file + "-lock"),
908 os.path.join(dns_dir, domainzone_file + "-lock"))
909 if forestzone_file:
910 os.link(os.path.join(private_dir, forestzone_file),
911 os.path.join(dns_dir, forestzone_file))
912 if backend_store == "mdb":
913 # If the database file is an lmdb data file need to link the
914 # lock file as well
915 os.link(os.path.join(private_dir, forestzone_file + "-lock"),
916 os.path.join(dns_dir, forestzone_file + "-lock"))
917 except OSError:
918 logger.error(
919 "Failed to setup database for BIND, AD based DNS cannot be used")
920 raise
921 del partfile[domainzonedn]
922 if forestzone_file:
923 del partfile[forestzonedn]
925 # Copy root, config, schema partitions (and any other if any)
926 # Since samdb is open in the current process, copy them in a child process
927 try:
928 tdb_copy(os.path.join(private_dir, "sam.ldb"),
929 os.path.join(dns_dir, "sam.ldb"))
930 for nc in partfile:
931 pfile = partfile[nc]
932 if backend_store == "mdb":
933 mdb_copy(os.path.join(private_dir, pfile),
934 os.path.join(dns_dir, pfile))
935 else:
936 tdb_copy(os.path.join(private_dir, pfile),
937 os.path.join(dns_dir, pfile))
938 except:
939 logger.error(
940 "Failed to setup database for BIND, AD based DNS cannot be used")
941 raise
943 # Give bind read/write permissions dns partitions
944 if paths.bind_gid is not None:
945 try:
946 for dirname, dirs, files in os.walk(dns_dir):
947 for d in dirs:
948 dpath = os.path.join(dirname, d)
949 os.chown(dpath, -1, paths.bind_gid)
950 os.chmod(dpath, 0o770)
951 for f in files:
952 if f.endswith(('.ldb', '.tdb', 'ldb-lock')):
953 fpath = os.path.join(dirname, f)
954 os.chown(fpath, -1, paths.bind_gid)
955 os.chmod(fpath, 0o660)
956 except OSError:
957 if 'SAMBA_SELFTEST' not in os.environ:
958 logger.error(
959 "Failed to set permissions to sam.ldb* files, fix manually")
960 else:
961 if 'SAMBA_SELFTEST' not in os.environ:
962 logger.warning("""Unable to find group id for BIND,
963 set permissions to sam.ldb* files manually""")
966 def create_dns_update_list(paths):
967 """Write out a dns_update_list file"""
968 # note that we use no variable substitution on this file
969 # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
970 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
971 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
974 def create_named_conf(paths, realm, dnsdomain, dns_backend, logger):
975 """Write out a file containing zone statements suitable for inclusion in a
976 named.conf file (including GSS-TSIG configuration).
978 :param paths: all paths
979 :param realm: Realm name
980 :param dnsdomain: DNS Domain name
981 :param dns_backend: DNS backend type
982 :param keytab_name: File name of DNS keytab file
983 :param logger: Logger object
986 # TODO: This really should have been done as a top level import.
987 # It is done here to avoid a dependency loop. That is, we move
988 # ProvisioningError to another file, and have all the provision
989 # scripts import it from there.
991 from samba.provision import ProvisioningError
993 if dns_backend == "BIND9_FLATFILE":
994 setup_file(setup_path("named.conf"), paths.namedconf, {
995 "DNSDOMAIN": dnsdomain,
996 "REALM": realm,
997 "ZONE_FILE": paths.dns,
998 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
999 "NAMED_CONF": paths.namedconf,
1000 "NAMED_CONF_UPDATE": paths.namedconf_update
1003 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1005 elif dns_backend == "BIND9_DLZ":
1006 bind_info = subprocess.Popen(['named -V'], shell=True,
1007 stdout=subprocess.PIPE,
1008 stderr=subprocess.STDOUT,
1009 cwd='.').communicate()[0]
1010 bind_info = get_string(bind_info)
1011 bind9_8 = '#'
1012 bind9_9 = '#'
1013 bind9_10 = '#'
1014 bind9_11 = '#'
1015 bind9_12 = '#'
1016 bind9_14 = '#'
1017 bind9_16 = '#'
1018 bind9_18 = '#'
1019 if bind_info.upper().find('BIND 9.8') != -1:
1020 bind9_8 = ''
1021 elif bind_info.upper().find('BIND 9.9') != -1:
1022 bind9_9 = ''
1023 elif bind_info.upper().find('BIND 9.10') != -1:
1024 bind9_10 = ''
1025 elif bind_info.upper().find('BIND 9.11') != -1:
1026 bind9_11 = ''
1027 elif bind_info.upper().find('BIND 9.12') != -1:
1028 bind9_12 = ''
1029 elif bind_info.upper().find('BIND 9.14') != -1:
1030 bind9_14 = ''
1031 elif bind_info.upper().find('BIND 9.16') != -1:
1032 bind9_16 = ''
1033 elif bind_info.upper().find('BIND 9.18') != -1:
1034 bind9_18 = ''
1035 elif bind_info.upper().find('BIND 9.7') != -1:
1036 raise ProvisioningError("DLZ option incompatible with BIND 9.7.")
1037 elif bind_info.upper().find('BIND_9.13') != -1:
1038 raise ProvisioningError("Only stable/esv releases of BIND are supported.")
1039 elif bind_info.upper().find('BIND_9.15') != -1:
1040 raise ProvisioningError("Only stable/esv releases of BIND are supported.")
1041 elif bind_info.upper().find('BIND_9.17') != -1:
1042 raise ProvisioningError("Only stable/esv releases of BIND are supported.")
1043 else:
1044 logger.warning("BIND version unknown, please modify %s manually." % paths.namedconf)
1045 setup_file(setup_path("named.conf.dlz"), paths.namedconf, {
1046 "NAMED_CONF": paths.namedconf,
1047 "MODULESDIR": samba.param.modules_dir(),
1048 "BIND9_8": bind9_8,
1049 "BIND9_9": bind9_9,
1050 "BIND9_10": bind9_10,
1051 "BIND9_11": bind9_11,
1052 "BIND9_12": bind9_12,
1053 "BIND9_14": bind9_14,
1054 "BIND9_16": bind9_16,
1055 "BIND9_18": bind9_18
1059 def create_named_txt(path, realm, dnsdomain, dnsname, binddns_dir,
1060 keytab_name):
1061 """Write out a file containing zone statements suitable for inclusion in a
1062 named.conf file (including GSS-TSIG configuration).
1064 :param path: Path of the new named.conf file.
1065 :param realm: Realm name
1066 :param dnsdomain: DNS Domain name
1067 :param binddns_dir: Path to bind dns directory
1068 :param keytab_name: File name of DNS keytab file
1070 setup_file(setup_path("named.txt"), path, {
1071 "DNSDOMAIN": dnsdomain,
1072 "DNSNAME": dnsname,
1073 "REALM": realm,
1074 "DNS_KEYTAB": keytab_name,
1075 "DNS_KEYTAB_ABS": os.path.join(binddns_dir, keytab_name),
1076 "PRIVATE_DIR": binddns_dir
1080 def is_valid_dns_backend(dns_backend):
1081 return dns_backend in ("BIND9_FLATFILE", "BIND9_DLZ", "SAMBA_INTERNAL", "NONE")
1084 def is_valid_os_level(os_level):
1085 return DS_DOMAIN_FUNCTION_2000 <= os_level <= DS_DOMAIN_FUNCTION_2016
1088 def create_dns_legacy(samdb, domainsid, forestdn, dnsadmins_sid):
1089 # Set up MicrosoftDNS container
1090 add_dns_container(samdb, forestdn, "CN=System", domainsid, dnsadmins_sid)
1091 # Add root servers
1092 add_rootservers(samdb, forestdn, "CN=System")
1095 def fill_dns_data_legacy(samdb, domainsid, forestdn, dnsdomain, site, hostname,
1096 hostip, hostip6, dnsadmins_sid):
1097 # Add domain record
1098 add_domain_record(samdb, forestdn, "CN=System", dnsdomain, domainsid,
1099 dnsadmins_sid)
1101 # Add DNS records for a DC in domain
1102 add_dc_domain_records(samdb, forestdn, "CN=System", site, dnsdomain,
1103 hostname, hostip, hostip6)
1106 def create_dns_partitions(samdb, domainsid, names, domaindn, forestdn,
1107 dnsadmins_sid, fill_level):
1108 # Set up additional partitions (DomainDnsZones, ForstDnsZones)
1109 setup_dns_partitions(samdb, domainsid, domaindn, forestdn,
1110 names.configdn, names.serverdn, fill_level)
1112 # Set up MicrosoftDNS containers
1113 add_dns_container(samdb, domaindn, "DC=DomainDnsZones", domainsid,
1114 dnsadmins_sid)
1115 if fill_level != FILL_SUBDOMAIN:
1116 add_dns_container(samdb, forestdn, "DC=ForestDnsZones", domainsid,
1117 dnsadmins_sid, forest=True)
1120 def fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn,
1121 dnsdomain, dnsforest, hostname, hostip, hostip6,
1122 domainguid, ntdsguid, dnsadmins_sid, autofill=True,
1123 fill_level=FILL_FULL, add_root=True):
1124 """Fill data in various AD partitions
1126 :param samdb: LDB object connected to sam.ldb file
1127 :param domainsid: Domain SID (as dom_sid object)
1128 :param site: Site name to create hostnames in
1129 :param domaindn: DN of the domain
1130 :param forestdn: DN of the forest
1131 :param dnsdomain: DNS name of the domain
1132 :param dnsforest: DNS name of the forest
1133 :param hostname: Host name of this DC
1134 :param hostip: IPv4 addresses
1135 :param hostip6: IPv6 addresses
1136 :param domainguid: Domain GUID
1137 :param ntdsguid: NTDS GUID
1138 :param dnsadmins_sid: SID for DnsAdmins group
1139 :param autofill: Create DNS records (using fixed template)
1142 # Set up DC=DomainDnsZones,<DOMAINDN>
1143 # Add rootserver records
1144 if add_root:
1145 add_rootservers(samdb, domaindn, "DC=DomainDnsZones")
1147 # Add domain record
1148 add_domain_record(samdb, domaindn, "DC=DomainDnsZones", dnsdomain,
1149 domainsid, dnsadmins_sid)
1151 # Add DNS records for a DC in domain
1152 if autofill:
1153 add_dc_domain_records(samdb, domaindn, "DC=DomainDnsZones", site,
1154 dnsdomain, hostname, hostip, hostip6)
1156 if fill_level != FILL_SUBDOMAIN:
1157 # Set up DC=ForestDnsZones,<FORESTDN>
1158 # Add _msdcs record
1159 add_msdcs_record(samdb, forestdn, "DC=ForestDnsZones", dnsforest)
1161 # Add DNS records for a DC in forest
1162 if autofill:
1163 add_dc_msdcs_records(samdb, forestdn, "DC=ForestDnsZones", site,
1164 dnsforest, hostname, hostip, hostip6,
1165 domainguid, ntdsguid)
1168 def setup_ad_dns(samdb, secretsdb, names, paths, lp, logger,
1169 dns_backend, os_level, dnspass=None, hostip=None, hostip6=None,
1170 fill_level=FILL_FULL, backend_store=None):
1171 """Provision DNS information (assuming GC role)
1173 :param samdb: LDB object connected to sam.ldb file
1174 :param secretsdb: LDB object connected to secrets.ldb file
1175 :param names: Names shortcut
1176 :param paths: Paths shortcut
1177 :param lp: Loadparm object
1178 :param logger: Logger object
1179 :param dns_backend: Type of DNS backend
1180 :param os_level: Functional level (treated as os level)
1181 :param dnspass: Password for bind's DNS account
1182 :param hostip: IPv4 address
1183 :param hostip6: IPv6 address
1186 if not is_valid_dns_backend(dns_backend):
1187 raise Exception("Invalid dns backend: %r" % dns_backend)
1189 if not is_valid_os_level(os_level):
1190 raise Exception("Invalid os level: %r" % os_level)
1192 if dns_backend == "NONE":
1193 logger.info("No DNS backend set, not configuring DNS")
1194 return
1196 # Add dns accounts (DnsAdmins, DnsUpdateProxy) in domain
1197 logger.info("Adding DNS accounts")
1198 add_dns_accounts(samdb, names.domaindn)
1200 # If dns_backend is BIND9_FLATFILE
1201 # Populate only CN=MicrosoftDNS,CN=System,<DOMAINDN>
1203 # If dns_backend is SAMBA_INTERNAL or BIND9_DLZ
1204 # Populate DNS partitions
1206 # If os_level < 2003 (DS_DOMAIN_FUNCTION_2000)
1207 # All dns records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1209 # If os_level >= 2003 (DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008,
1210 # DS_DOMAIN_FUNCTION_2008_R2)
1211 # Root server records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1212 # Domain records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1213 # Domain records are in CN=MicrosoftDNS,DC=DomainDnsZones,<DOMAINDN>
1214 # Forest records are in CN=MicrosoftDNS,DC=ForestDnsZones,<FORESTDN>
1215 domaindn = names.domaindn
1216 forestdn = samdb.get_root_basedn().get_linearized()
1218 dnsdomain = names.dnsdomain.lower()
1219 dnsforest = dnsdomain
1221 site = names.sitename
1223 hostname = names.netbiosname.lower()
1225 dnsadmins_sid = get_dnsadmins_sid(samdb, domaindn)
1226 domainguid = get_domainguid(samdb, domaindn)
1228 samdb.transaction_start()
1229 try:
1230 # Create CN=System
1231 logger.info("Creating CN=MicrosoftDNS,CN=System,%s" % domaindn)
1232 create_dns_legacy(samdb, names.domainsid, domaindn, dnsadmins_sid)
1234 if os_level == DS_DOMAIN_FUNCTION_2000:
1235 # Populating legacy dns
1236 logger.info("Populating CN=MicrosoftDNS,CN=System,%s" % domaindn)
1237 fill_dns_data_legacy(samdb, names.domainsid, domaindn, dnsdomain, site,
1238 hostname, hostip, hostip6, dnsadmins_sid)
1240 elif dns_backend in ("SAMBA_INTERNAL", "BIND9_DLZ") and \
1241 os_level >= DS_DOMAIN_FUNCTION_2003:
1243 # Create DNS partitions
1244 logger.info("Creating DomainDnsZones and ForestDnsZones partitions")
1245 create_dns_partitions(samdb, names.domainsid, names, domaindn, forestdn,
1246 dnsadmins_sid, fill_level)
1248 # Populating dns partitions
1249 logger.info("Populating DomainDnsZones and ForestDnsZones partitions")
1250 fill_dns_data_partitions(samdb, names.domainsid, site, domaindn, forestdn,
1251 dnsdomain, dnsforest, hostname, hostip, hostip6,
1252 domainguid, names.ntdsguid, dnsadmins_sid,
1253 fill_level=fill_level)
1255 except:
1256 samdb.transaction_cancel()
1257 raise
1258 else:
1259 samdb.transaction_commit()
1261 if dns_backend.startswith("BIND9_"):
1262 setup_bind9_dns(samdb, secretsdb, names, paths, logger,
1263 dns_backend, os_level, site=site, dnspass=dnspass, hostip=hostip,
1264 hostip6=hostip6)
1267 def setup_bind9_dns(samdb, secretsdb, names, paths, logger,
1268 dns_backend, os_level, site=None, dnspass=None, hostip=None,
1269 hostip6=None, key_version_number=None):
1270 """Provision DNS information (assuming BIND9 backend in DC role)
1272 :param samdb: LDB object connected to sam.ldb file
1273 :param secretsdb: LDB object connected to secrets.ldb file
1274 :param names: Names shortcut
1275 :param paths: Paths shortcut
1276 :param logger: Logger object
1277 :param dns_backend: Type of DNS backend
1278 :param os_level: Functional level (treated as os level)
1279 :param site: Site to create hostnames in
1280 :param dnspass: Password for bind's DNS account
1281 :param hostip: IPv4 address
1282 :param hostip6: IPv6 address
1285 if (not is_valid_dns_backend(dns_backend) or
1286 not dns_backend.startswith("BIND9_")):
1287 raise Exception("Invalid dns backend: %r" % dns_backend)
1289 if not is_valid_os_level(os_level):
1290 raise Exception("Invalid os level: %r" % os_level)
1292 domaindn = names.domaindn
1294 domainguid = get_domainguid(samdb, domaindn)
1296 secretsdb_setup_dns(secretsdb, names,
1297 paths.private_dir,
1298 paths.binddns_dir,
1299 realm=names.realm,
1300 dnsdomain=names.dnsdomain,
1301 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1302 key_version_number=key_version_number)
1304 create_dns_dir(logger, paths)
1305 create_dns_dir_keytab_link(logger, paths)
1307 if dns_backend == "BIND9_FLATFILE":
1308 create_zone_file(logger, paths, site=site,
1309 dnsdomain=names.dnsdomain, hostip=hostip,
1310 hostip6=hostip6, hostname=names.hostname,
1311 realm=names.realm, domainguid=domainguid,
1312 ntdsguid=names.ntdsguid)
1314 if dns_backend == "BIND9_DLZ" and os_level >= DS_DOMAIN_FUNCTION_2003:
1315 create_samdb_copy(samdb, logger, paths,
1316 names, names.domainsid, domainguid)
1318 create_named_conf(paths, realm=names.realm,
1319 dnsdomain=names.dnsdomain, dns_backend=dns_backend,
1320 logger=logger)
1322 create_named_txt(paths.namedtxt,
1323 realm=names.realm, dnsdomain=names.dnsdomain,
1324 dnsname="%s.%s" % (names.hostname, names.dnsdomain),
1325 binddns_dir=paths.binddns_dir,
1326 keytab_name=paths.dns_keytab)
1327 logger.info("See %s for an example configuration include file for BIND",
1328 paths.namedconf)
1329 logger.info("and %s for further documentation required for secure DNS "
1330 "updates", paths.namedtxt)