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