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