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