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