libds:common Remove DS_DC_* domain functionality flags
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision.py
blob1b7cfca64d4d84f84919c3d25846ebb53afb5b80
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
29 import os
30 import re
31 import pwd
32 import grp
33 import logging
34 import time
35 import uuid
36 import socket
37 import urllib
38 import shutil
40 import ldb
42 from samba.auth import system_session, admin_session
43 import samba
44 from samba import version, Ldb, substitute_var, valid_netbios_name
45 from samba import check_all_substituted, read_and_sub_file, setup_file
46 from samba.dsdb import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008
47 from samba.dcerpc import security
48 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
49 from samba.idmap import IDmapDB
50 from samba.ms_display_specifiers import read_ms_ldif
51 from samba.ntacls import setntacl, dsacl2fsacl
52 from samba.ndr import ndr_pack,ndr_unpack
53 from samba.provisionbackend import (
54 ExistingBackend,
55 FDSBackend,
56 LDBBackend,
57 OpenLDAPBackend,
59 import samba.param
60 import samba.registry
61 from samba.schema import Schema
62 from samba.samdb import SamDB
64 __docformat__ = "restructuredText"
66 def find_setup_dir():
67 """Find the setup directory used by provision."""
68 import sys
69 for prefix in [sys.prefix,
70 os.path.join(os.path.dirname(__file__), "../../../..")]:
71 for suffix in ["share/setup", "share/samba/setup", "setup"]:
72 ret = os.path.join(prefix, suffix)
73 if os.path.isdir(ret):
74 return ret
75 # In source tree
76 dirname = os.path.dirname(__file__)
77 ret = os.path.join(dirname, "../../../setup")
78 if os.path.isdir(ret):
79 return ret
80 raise Exception("Unable to find setup directory.")
82 # descriptors of the naming contexts
83 # hard coded at this point, but will probably be changed when
84 # we enable different fsmo roles
87 def get_config_descriptor(domain_sid):
88 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
89 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
90 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
91 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
92 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
93 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
94 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
95 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
96 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
97 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
98 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
99 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
100 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
101 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
102 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
103 sec = security.descriptor.from_sddl(sddl, domain_sid)
104 return ndr_pack(sec)
106 def get_domain_descriptor(domain_sid):
107 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
108 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
109 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
110 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
111 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
112 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
113 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
114 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
115 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
116 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
117 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
118 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
119 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
120 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
121 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
122 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
123 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
124 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
125 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
126 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
127 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
128 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
129 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
130 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
131 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
132 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
133 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
134 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
135 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
136 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
137 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
138 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
139 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
140 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
141 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
142 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
143 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
144 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
145 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
146 "(A;;RPRC;;;RU)" \
147 "(A;CI;LC;;;RU)" \
148 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
149 "(A;;RP;;;WD)" \
150 "(A;;RPLCLORC;;;ED)" \
151 "(A;;RPLCLORC;;;AU)" \
152 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
153 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
154 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
155 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
156 sec = security.descriptor.from_sddl(sddl, domain_sid)
157 return ndr_pack(sec)
159 DEFAULTSITE = "Default-First-Site-Name"
160 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
162 class ProvisionPaths(object):
164 def __init__(self):
165 self.shareconf = None
166 self.hklm = None
167 self.hkcu = None
168 self.hkcr = None
169 self.hku = None
170 self.hkpd = None
171 self.hkpt = None
172 self.samdb = None
173 self.idmapdb = None
174 self.secrets = None
175 self.keytab = None
176 self.dns_keytab = None
177 self.dns = None
178 self.winsdb = None
179 self.private_dir = None
182 class ProvisionNames(object):
184 def __init__(self):
185 self.rootdn = None
186 self.domaindn = None
187 self.configdn = None
188 self.schemadn = None
189 self.ldapmanagerdn = None
190 self.dnsdomain = None
191 self.realm = None
192 self.netbiosname = None
193 self.domain = None
194 self.hostname = None
195 self.sitename = None
196 self.smbconf = None
199 def update_provision_usn(samdb, low, high, replace=False):
200 """Update the field provisionUSN in sam.ldb
202 This field is used to track range of USN modified by provision and
203 upgradeprovision.
204 This value is used afterward by next provision to figure out if
205 the field have been modified since last provision.
207 :param samdb: An LDB object connect to sam.ldb
208 :param low: The lowest USN modified by this upgrade
209 :param high: The highest USN modified by this upgrade
210 :param replace: A boolean indicating if the range should replace any
211 existing one or appended (default)
214 tab = []
215 if not replace:
216 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" % \
217 LAST_PROVISION_USN_ATTRIBUTE, base="",
218 scope=ldb.SCOPE_SUBTREE,
219 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
220 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
221 tab.append(str(e))
223 tab.append("%s-%s" % (low, high))
224 delta = ldb.Message()
225 delta.dn = ldb.Dn(samdb, "@PROVISION")
226 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
227 ldb.FLAG_MOD_REPLACE,
228 LAST_PROVISION_USN_ATTRIBUTE)
229 samdb.modify(delta)
232 def set_provision_usn(samdb, low, high):
233 """Set the field provisionUSN in sam.ldb
234 This field is used to track range of USN modified by provision and
235 upgradeprovision.
236 This value is used afterward by next provision to figure out if
237 the field have been modified since last provision.
239 :param samdb: An LDB object connect to sam.ldb
240 :param low: The lowest USN modified by this upgrade
241 :param high: The highest USN modified by this upgrade"""
242 tab = []
243 tab.append("%s-%s" % (low, high))
244 delta = ldb.Message()
245 delta.dn = ldb.Dn(samdb, "@PROVISION")
246 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
247 ldb.FLAG_MOD_ADD,
248 LAST_PROVISION_USN_ATTRIBUTE)
249 samdb.add(delta)
252 def get_max_usn(samdb,basedn):
253 """ This function return the biggest USN present in the provision
255 :param samdb: A LDB object pointing to the sam.ldb
256 :param basedn: A string containing the base DN of the provision
257 (ie. DC=foo, DC=bar)
258 :return: The biggest USN in the provision"""
260 res = samdb.search(expression="objectClass=*",base=basedn,
261 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
262 controls=["search_options:1:2",
263 "server_sort:1:1:uSNChanged",
264 "paged_results:1:1"])
265 return res[0]["uSNChanged"]
267 def get_last_provision_usn(sam):
268 """Get the lastest USN modified by a provision or an upgradeprovision
270 :param sam: An LDB object pointing to the sam.ldb
271 :return an integer corresponding to the highest USN modified by
272 (upgrade)provision, 0 is this value is unknown"""
274 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" % \
275 LAST_PROVISION_USN_ATTRIBUTE,
276 base="", scope=ldb.SCOPE_SUBTREE,
277 attrs=[LAST_PROVISION_USN_ATTRIBUTE])
278 if len(entry):
279 range = []
280 idx = 0
281 p = re.compile(r'-')
282 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
283 tab = p.split(str(r))
284 range.append(tab[0])
285 range.append(tab[1])
286 idx = idx + 1
287 return range
288 else:
289 return None
291 class ProvisionResult(object):
293 def __init__(self):
294 self.paths = None
295 self.domaindn = None
296 self.lp = None
297 self.samdb = None
300 def check_install(lp, session_info, credentials):
301 """Check whether the current install seems ok.
303 :param lp: Loadparm context
304 :param session_info: Session information
305 :param credentials: Credentials
307 if lp.get("realm") == "":
308 raise Exception("Realm empty")
309 samdb = Ldb(lp.get("sam database"), session_info=session_info,
310 credentials=credentials, lp=lp)
311 if len(samdb.search("(cn=Administrator)")) != 1:
312 raise ProvisioningError("No administrator account found")
315 def findnss(nssfn, names):
316 """Find a user or group from a list of possibilities.
318 :param nssfn: NSS Function to try (should raise KeyError if not found)
319 :param names: Names to check.
320 :return: Value return by first names list.
322 for name in names:
323 try:
324 return nssfn(name)
325 except KeyError:
326 pass
327 raise KeyError("Unable to find user/group in %r" % names)
330 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
331 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
334 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
335 """Setup a ldb in the private dir.
337 :param ldb: LDB file to import data into
338 :param ldif_path: Path of the LDIF file to load
339 :param subst_vars: Optional variables to subsitute in LDIF.
340 :param nocontrols: Optional list of controls, can be None for no controls
342 assert isinstance(ldif_path, str)
343 data = read_and_sub_file(ldif_path, subst_vars)
344 ldb.add_ldif(data, controls)
347 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
348 """Modify a ldb in the private dir.
350 :param ldb: LDB object.
351 :param ldif_path: LDIF file path.
352 :param subst_vars: Optional dictionary with substitution variables.
354 data = read_and_sub_file(ldif_path, subst_vars)
355 ldb.modify_ldif(data)
358 def setup_ldb(ldb, ldif_path, subst_vars):
359 """Import a LDIF a file into a LDB handle, optionally substituting variables.
361 :note: Either all LDIF data will be added or none (using transactions).
363 :param ldb: LDB file to import into.
364 :param ldif_path: Path to the LDIF file.
365 :param subst_vars: Dictionary with substitution variables.
367 assert ldb is not None
368 ldb.transaction_start()
369 try:
370 setup_add_ldif(ldb, ldif_path, subst_vars)
371 except:
372 ldb.transaction_cancel()
373 raise
374 else:
375 ldb.transaction_commit()
378 def provision_paths_from_lp(lp, dnsdomain):
379 """Set the default paths for provisioning.
381 :param lp: Loadparm context.
382 :param dnsdomain: DNS Domain name
384 paths = ProvisionPaths()
385 paths.private_dir = lp.get("private dir")
387 # This is stored without path prefix for the "privateKeytab" attribute in
388 # "secrets_dns.ldif".
389 paths.dns_keytab = "dns.keytab"
391 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
392 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
393 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
394 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
395 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
396 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
397 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
398 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
399 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
400 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
401 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
402 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
403 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
404 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
405 paths.phpldapadminconfig = os.path.join(paths.private_dir,
406 "phpldapadmin-config.php")
407 paths.hklm = "hklm.ldb"
408 paths.hkcr = "hkcr.ldb"
409 paths.hkcu = "hkcu.ldb"
410 paths.hku = "hku.ldb"
411 paths.hkpd = "hkpd.ldb"
412 paths.hkpt = "hkpt.ldb"
413 paths.sysvol = lp.get("path", "sysvol")
414 paths.netlogon = lp.get("path", "netlogon")
415 paths.smbconf = lp.configfile
416 return paths
419 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
420 serverrole=None, rootdn=None, domaindn=None, configdn=None,
421 schemadn=None, serverdn=None, sitename=None):
422 """Guess configuration settings to use."""
424 if hostname is None:
425 hostname = socket.gethostname().split(".")[0]
427 netbiosname = lp.get("netbios name")
428 if netbiosname is None:
429 netbiosname = hostname
430 assert netbiosname is not None
431 netbiosname = netbiosname.upper()
432 if not valid_netbios_name(netbiosname):
433 raise InvalidNetbiosName(netbiosname)
435 if dnsdomain is None:
436 dnsdomain = lp.get("realm")
437 if dnsdomain is None or dnsdomain == "":
438 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
440 dnsdomain = dnsdomain.lower()
442 if serverrole is None:
443 serverrole = lp.get("server role")
444 if serverrole is None:
445 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
447 serverrole = serverrole.lower()
449 realm = dnsdomain.upper()
451 if lp.get("realm") == "":
452 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
454 if lp.get("realm").upper() != realm:
455 raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
457 if lp.get("server role").lower() != serverrole:
458 raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role").upper(), serverrole, lp.configfile))
460 if serverrole == "domain controller":
461 if domain is None:
462 # This will, for better or worse, default to 'WORKGROUP'
463 domain = lp.get("workgroup")
464 domain = domain.upper()
466 if lp.get("workgroup").upper() != domain:
467 raise ProvisioningError("guess_names: Workgroup '%s' in %s must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
469 if domaindn is None:
470 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
471 else:
472 domain = netbiosname
473 if domaindn is None:
474 domaindn = "DC=" + netbiosname
476 if not valid_netbios_name(domain):
477 raise InvalidNetbiosName(domain)
479 if hostname.upper() == realm:
480 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
481 if netbiosname == realm:
482 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
483 if domain == realm:
484 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
486 if rootdn is None:
487 rootdn = domaindn
489 if configdn is None:
490 configdn = "CN=Configuration," + rootdn
491 if schemadn is None:
492 schemadn = "CN=Schema," + configdn
494 if sitename is None:
495 sitename=DEFAULTSITE
497 names = ProvisionNames()
498 names.rootdn = rootdn
499 names.domaindn = domaindn
500 names.configdn = configdn
501 names.schemadn = schemadn
502 names.ldapmanagerdn = "CN=Manager," + rootdn
503 names.dnsdomain = dnsdomain
504 names.domain = domain
505 names.realm = realm
506 names.netbiosname = netbiosname
507 names.hostname = hostname
508 names.sitename = sitename
509 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
511 return names
514 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
515 targetdir, sid_generator="internal", eadb=False):
516 """Create a new smb.conf file based on a couple of basic settings.
518 assert smbconf is not None
519 if hostname is None:
520 hostname = socket.gethostname().split(".")[0]
521 netbiosname = hostname.upper()
523 if serverrole is None:
524 serverrole = "standalone"
526 assert serverrole in ("domain controller", "member server", "standalone")
527 if serverrole == "domain controller":
528 smbconfsuffix = "dc"
529 elif serverrole == "member server":
530 smbconfsuffix = "member"
531 elif serverrole == "standalone":
532 smbconfsuffix = "standalone"
534 if sid_generator is None:
535 sid_generator = "internal"
537 assert domain is not None
538 domain = domain.upper()
540 assert realm is not None
541 realm = realm.upper()
543 default_lp = samba.param.LoadParm()
544 #Load non-existant file
545 if os.path.exists(smbconf):
546 default_lp.load(smbconf)
547 if eadb:
548 if targetdir is not None:
549 privdir = os.path.join(targetdir, "private")
550 else:
551 privdir = default_lp.get("private dir")
552 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir, "eadb.tdb"))
553 else:
554 posixeadb_line = ""
556 if targetdir is not None:
557 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
558 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
560 default_lp.set("lock dir", os.path.abspath(targetdir))
561 else:
562 privatedir_line = ""
563 lockdir_line = ""
565 if sid_generator == "internal":
566 sid_generator_line = ""
567 else:
568 sid_generator_line = "sid generator = " + sid_generator
570 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
571 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
573 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
574 smbconf, {
575 "NETBIOS_NAME": netbiosname,
576 "DOMAIN": domain,
577 "REALM": realm,
578 "SERVERROLE": serverrole,
579 "NETLOGONPATH": netlogon,
580 "SYSVOLPATH": sysvol,
581 "SIDGENERATOR_LINE": sid_generator_line,
582 "PRIVATEDIR_LINE": privatedir_line,
583 "LOCKDIR_LINE": lockdir_line,
584 "POSIXEADB_LINE": posixeadb_line
588 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
589 users_gid, wheel_gid):
590 """setup reasonable name mappings for sam names to unix names.
592 :param samdb: SamDB object.
593 :param idmap: IDmap db object.
594 :param sid: The domain sid.
595 :param domaindn: The domain DN.
596 :param root_uid: uid of the UNIX root user.
597 :param nobody_uid: uid of the UNIX nobody user.
598 :param users_gid: gid of the UNIX users group.
599 :param wheel_gid: gid of the UNIX wheel group."""
600 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
601 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
603 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
604 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
607 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
608 provision_backend, names, schema, serverrole,
609 erase=False):
610 """Setup the partitions for the SAM database.
612 Alternatively, provision() may call this, and then populate the database.
614 :note: This will wipe the Sam Database!
616 :note: This function always removes the local SAM LDB file. The erase
617 parameter controls whether to erase the existing data, which
618 may not be stored locally but in LDAP.
621 assert session_info is not None
623 # We use options=["modules:"] to stop the modules loading - we
624 # just want to wipe and re-initialise the database, not start it up
626 try:
627 os.unlink(samdb_path)
628 except OSError:
629 pass
631 samdb = Ldb(url=samdb_path, session_info=session_info,
632 lp=lp, options=["modules:"])
634 ldap_backend_line = "# No LDAP backend"
635 if provision_backend.type is not "ldb":
636 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
638 samdb.transaction_start()
639 try:
640 logger.info("Setting up sam.ldb partitions and settings")
641 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
642 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
643 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
644 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
645 "LDAP_BACKEND_LINE": ldap_backend_line,
649 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
650 "BACKEND_TYPE": provision_backend.type,
651 "SERVER_ROLE": serverrole
654 logger.info("Setting up sam.ldb rootDSE")
655 setup_samdb_rootdse(samdb, setup_path, names)
656 except:
657 samdb.transaction_cancel()
658 raise
659 else:
660 samdb.transaction_commit()
663 def secretsdb_self_join(secretsdb, domain,
664 netbiosname, machinepass, domainsid=None,
665 realm=None, dnsdomain=None,
666 keytab_path=None,
667 key_version_number=1,
668 secure_channel_type=SEC_CHAN_WKSTA):
669 """Add domain join-specific bits to a secrets database.
671 :param secretsdb: Ldb Handle to the secrets database
672 :param machinepass: Machine password
674 attrs=["whenChanged",
675 "secret",
676 "priorSecret",
677 "priorChanged",
678 "krb5Keytab",
679 "privateKeytab"]
682 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
683 msg["secureChannelType"] = str(secure_channel_type)
684 msg["flatname"] = [domain]
685 msg["objectClass"] = ["top", "primaryDomain"]
686 if realm is not None:
687 if dnsdomain is None:
688 dnsdomain = realm.lower()
689 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
690 msg["realm"] = realm
691 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
692 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
693 msg["privateKeytab"] = ["secrets.keytab"]
696 msg["secret"] = [machinepass]
697 msg["samAccountName"] = ["%s$" % netbiosname]
698 msg["secureChannelType"] = [str(secure_channel_type)]
699 if domainsid is not None:
700 msg["objectSid"] = [ndr_pack(domainsid)]
702 res = secretsdb.search(base="cn=Primary Domains",
703 attrs=attrs,
704 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
705 scope=ldb.SCOPE_ONELEVEL)
707 for del_msg in res:
708 if del_msg.dn is not msg.dn:
709 secretsdb.delete(del_msg.dn)
711 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
713 if len(res) == 1:
714 msg["priorSecret"] = res[0]["secret"]
715 msg["priorWhenChanged"] = res[0]["whenChanged"]
717 if res["privateKeytab"] is not None:
718 msg["privateKeytab"] = res[0]["privateKeytab"]
720 if res["krb5Keytab"] is not None:
721 msg["krb5Keytab"] = res[0]["krb5Keytab"]
723 for el in msg:
724 el.set_flags(ldb.FLAG_MOD_REPLACE)
725 secretsdb.modify(msg)
726 else:
727 secretsdb.add(msg)
730 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
731 realm, dnsdomain,
732 dns_keytab_path, dnspass):
733 """Add DNS specific bits to a secrets database.
735 :param secretsdb: Ldb Handle to the secrets database
736 :param setup_path: Setup path function
737 :param machinepass: Machine password
739 try:
740 os.unlink(os.path.join(private_dir, dns_keytab_path))
741 except OSError:
742 pass
744 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
745 "REALM": realm,
746 "DNSDOMAIN": dnsdomain,
747 "DNS_KEYTAB": dns_keytab_path,
748 "DNSPASS_B64": b64encode(dnspass),
752 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
753 """Setup the secrets database.
755 :note: This function does not handle exceptions and transaction on purpose,
756 it's up to the caller to do this job.
758 :param path: Path to the secrets database.
759 :param setup_path: Get the path to a setup file.
760 :param session_info: Session info.
761 :param credentials: Credentials
762 :param lp: Loadparm context
763 :return: LDB handle for the created secrets database
765 if os.path.exists(path):
766 os.unlink(path)
767 secrets_ldb = Ldb(path, session_info=session_info,
768 lp=lp)
769 secrets_ldb.erase()
770 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
771 secrets_ldb = Ldb(path, session_info=session_info,
772 lp=lp)
773 secrets_ldb.transaction_start()
774 try:
775 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
777 if backend_credentials is not None and backend_credentials.authentication_requested():
778 if backend_credentials.get_bind_dn() is not None:
779 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
780 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
781 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
783 else:
784 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
785 "LDAPADMINUSER": backend_credentials.get_username(),
786 "LDAPADMINREALM": backend_credentials.get_realm(),
787 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
790 return secrets_ldb
791 except:
792 secrets_ldb.transaction_cancel()
793 raise
795 def setup_privileges(path, setup_path, session_info, lp):
796 """Setup the privileges database.
798 :param path: Path to the privileges database.
799 :param setup_path: Get the path to a setup file.
800 :param session_info: Session info.
801 :param credentials: Credentials
802 :param lp: Loadparm context
803 :return: LDB handle for the created secrets database
805 if os.path.exists(path):
806 os.unlink(path)
807 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
808 privilege_ldb.erase()
809 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
812 def setup_registry(path, setup_path, session_info, lp):
813 """Setup the registry.
815 :param path: Path to the registry database
816 :param setup_path: Function that returns the path to a setup.
817 :param session_info: Session information
818 :param credentials: Credentials
819 :param lp: Loadparm context
821 reg = samba.registry.Registry()
822 hive = samba.registry.open_ldb(path, session_info=session_info,
823 lp_ctx=lp)
824 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
825 provision_reg = setup_path("provision.reg")
826 assert os.path.exists(provision_reg)
827 reg.diff_apply(provision_reg)
830 def setup_idmapdb(path, setup_path, session_info, lp):
831 """Setup the idmap database.
833 :param path: path to the idmap database
834 :param setup_path: Function that returns a path to a setup file
835 :param session_info: Session information
836 :param credentials: Credentials
837 :param lp: Loadparm context
839 if os.path.exists(path):
840 os.unlink(path)
842 idmap_ldb = IDmapDB(path, session_info=session_info,
843 lp=lp)
845 idmap_ldb.erase()
846 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
847 return idmap_ldb
850 def setup_samdb_rootdse(samdb, setup_path, names):
851 """Setup the SamDB rootdse.
853 :param samdb: Sam Database handle
854 :param setup_path: Obtain setup path
856 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
857 "SCHEMADN": names.schemadn,
858 "NETBIOSNAME": names.netbiosname,
859 "DNSDOMAIN": names.dnsdomain,
860 "REALM": names.realm,
861 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
862 "DOMAINDN": names.domaindn,
863 "ROOTDN": names.rootdn,
864 "CONFIGDN": names.configdn,
865 "SERVERDN": names.serverdn,
869 def setup_self_join(samdb, names,
870 machinepass, dnspass,
871 domainsid, invocationid, setup_path,
872 policyguid, policyguid_dc, domainControllerFunctionality,
873 ntdsguid):
874 """Join a host to its own domain."""
875 assert isinstance(invocationid, str)
876 if ntdsguid is not None:
877 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
878 else:
879 ntdsguid_line = ""
880 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
881 "CONFIGDN": names.configdn,
882 "SCHEMADN": names.schemadn,
883 "DOMAINDN": names.domaindn,
884 "SERVERDN": names.serverdn,
885 "INVOCATIONID": invocationid,
886 "NETBIOSNAME": names.netbiosname,
887 "DEFAULTSITE": names.sitename,
888 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
889 "MACHINEPASS_B64": b64encode(machinepass),
890 "REALM": names.realm,
891 "DOMAIN": names.domain,
892 "DOMAINSID": str(domainsid),
893 "DNSDOMAIN": names.dnsdomain,
894 "SAMBA_VERSION_STRING": version,
895 "NTDSGUID": ntdsguid_line,
896 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
898 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
899 "POLICYGUID": policyguid,
900 "POLICYGUID_DC": policyguid_dc,
901 "DNSDOMAIN": names.dnsdomain,
902 "DOMAINSID": str(domainsid),
903 "DOMAINDN": names.domaindn})
905 # add the NTDSGUID based SPNs
906 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=%s,CN=Sites,CN=Configuration,%s" % (names.hostname, names.sitename, names.domaindn)
907 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
908 expression="", scope=ldb.SCOPE_BASE)
909 assert isinstance(names.ntdsguid, str)
911 # Setup fSMORoleOwner entries to point at the newly created DC entry
912 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
913 "DOMAIN": names.domain,
914 "DNSDOMAIN": names.dnsdomain,
915 "DOMAINDN": names.domaindn,
916 "CONFIGDN": names.configdn,
917 "SCHEMADN": names.schemadn,
918 "DEFAULTSITE": names.sitename,
919 "SERVERDN": names.serverdn,
920 "NETBIOSNAME": names.netbiosname,
921 "NTDSGUID": names.ntdsguid,
922 "DNSPASS_B64": b64encode(dnspass),
925 def getpolicypath(sysvolpath, dnsdomain, guid):
926 if guid[0] != "{":
927 guid = "{%s}" % guid
928 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
929 return policy_path
931 def create_gpo_struct(policy_path):
932 os.makedirs(policy_path, 0755)
933 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
934 "[General]\r\nVersion=65543")
935 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
936 os.makedirs(os.path.join(policy_path, "USER"), 0755)
939 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
940 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
941 create_gpo_struct(policy_path)
943 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
944 create_gpo_struct(policy_path)
947 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
948 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
949 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
950 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None):
951 """Setup a complete SAM Database.
953 :note: This will wipe the main SAM database file!
956 # ATTENTION: Do NOT change these default values without discussion with the
957 # team and/or release manager. They have a big impact on the whole program!
958 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008
960 if dom_for_fun_level is None:
961 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
963 if dom_for_fun_level > domainControllerFunctionality:
964 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008). This won't work!")
966 domainFunctionality = dom_for_fun_level
967 forestFunctionality = dom_for_fun_level
969 # Also wipes the database
970 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
971 provision_backend=provision_backend, session_info=session_info,
972 names=names, serverrole=serverrole, schema=schema)
974 if schema is None:
975 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
976 am_rodc=am_rodc)
978 # Load the database, but don's load the global schema and don't connect quite yet
979 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
980 credentials=provision_backend.credentials, lp=lp, global_schema=False,
981 am_rodc=am_rodc)
983 logger.info("Pre-loading the Samba 4 and AD schema")
985 # Load the schema from the one we computed earlier
986 samdb.set_schema(schema)
988 # And now we can connect to the DB - the schema won't be loaded from the DB
989 samdb.connect(path)
991 if fill == FILL_DRS:
992 return samdb
994 samdb.transaction_start()
995 try:
996 # Set the domain functionality levels onto the database.
997 # Various module (the password_hash module in particular) need
998 # to know what level of AD we are emulating.
1000 # These will be fixed into the database via the database
1001 # modifictions below, but we need them set from the start.
1002 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1003 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1004 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1006 samdb.set_domain_sid(str(domainsid))
1007 samdb.set_invocation_id(invocationid)
1008 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1010 logger.info("Adding DomainDN: %s" % names.domaindn)
1012 #impersonate domain admin
1013 admin_session_info = admin_session(lp, str(domainsid))
1014 samdb.set_session_info(admin_session_info)
1015 if domainguid is not None:
1016 domainguid_line = "objectGUID: %s\n-" % domainguid
1017 else:
1018 domainguid_line = ""
1020 descr = b64encode(get_domain_descriptor(domainsid))
1021 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1022 "DOMAINDN": names.domaindn,
1023 "DOMAINGUID": domainguid_line,
1024 "DESCRIPTOR": descr
1028 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1029 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1030 "DOMAINSID": str(domainsid),
1031 "SCHEMADN": names.schemadn,
1032 "NETBIOSNAME": names.netbiosname,
1033 "DEFAULTSITE": names.sitename,
1034 "CONFIGDN": names.configdn,
1035 "SERVERDN": names.serverdn,
1036 "POLICYGUID": policyguid,
1037 "DOMAINDN": names.domaindn,
1038 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1039 "SAMBA_VERSION_STRING": version
1042 logger.info("Adding configuration container")
1043 descr = b64encode(get_config_descriptor(domainsid))
1044 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1045 "CONFIGDN": names.configdn,
1046 "DESCRIPTOR": descr,
1049 # The LDIF here was created when the Schema object was constructed
1050 logger.info("Setting up sam.ldb schema")
1051 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1052 samdb.modify_ldif(schema.schema_dn_modify)
1053 samdb.write_prefixes_from_schema()
1054 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1055 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1056 {"SCHEMADN": names.schemadn})
1058 logger.info("Reopening sam.ldb with new schema")
1059 except:
1060 samdb.transaction_cancel()
1061 raise
1062 else:
1063 samdb.transaction_commit()
1065 samdb = SamDB(session_info=admin_session_info,
1066 credentials=provision_backend.credentials, lp=lp,
1067 global_schema=False, am_rodc=am_rodc)
1068 samdb.connect(path)
1069 samdb.transaction_start()
1070 try:
1071 samdb.invocation_id = invocationid
1073 logger.info("Setting up sam.ldb configuration data")
1074 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1075 "CONFIGDN": names.configdn,
1076 "NETBIOSNAME": names.netbiosname,
1077 "DEFAULTSITE": names.sitename,
1078 "DNSDOMAIN": names.dnsdomain,
1079 "DOMAIN": names.domain,
1080 "SCHEMADN": names.schemadn,
1081 "DOMAINDN": names.domaindn,
1082 "SERVERDN": names.serverdn,
1083 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1084 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
1087 logger.info("Setting up display specifiers")
1088 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1089 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1090 check_all_substituted(display_specifiers_ldif)
1091 samdb.add_ldif(display_specifiers_ldif)
1093 logger.info("Adding users container")
1094 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1095 "DOMAINDN": names.domaindn})
1096 logger.info("Modifying users container")
1097 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1098 "DOMAINDN": names.domaindn})
1099 logger.info("Adding computers container")
1100 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1101 "DOMAINDN": names.domaindn})
1102 logger.info("Modifying computers container")
1103 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1104 "DOMAINDN": names.domaindn})
1105 logger.info("Setting up sam.ldb data")
1106 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1107 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1108 "DOMAINDN": names.domaindn,
1109 "NETBIOSNAME": names.netbiosname,
1110 "DEFAULTSITE": names.sitename,
1111 "CONFIGDN": names.configdn,
1112 "SERVERDN": names.serverdn,
1113 "POLICYGUID_DC": policyguid_dc
1116 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1117 "DOMAINDN": names.domaindn})
1119 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1120 "CONFIGDN": names.configdn,
1121 "SCHEMADN": names.schemadn})
1122 if fill == FILL_FULL:
1123 logger.info("Setting up sam.ldb users and groups")
1124 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1125 "DOMAINDN": names.domaindn,
1126 "DOMAINSID": str(domainsid),
1127 "CONFIGDN": names.configdn,
1128 "ADMINPASS_B64": b64encode(adminpass),
1129 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1132 logger.info("Setting up self join")
1133 setup_self_join(samdb, names=names, invocationid=invocationid,
1134 dnspass=dnspass,
1135 machinepass=machinepass,
1136 domainsid=domainsid, policyguid=policyguid,
1137 policyguid_dc=policyguid_dc,
1138 setup_path=setup_path,
1139 domainControllerFunctionality=domainControllerFunctionality,
1140 ntdsguid=ntdsguid)
1142 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=%s,CN=Sites,CN=Configuration,%s" % (names.hostname, names.sitename, names.domaindn)
1143 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1144 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1145 assert isinstance(names.ntdsguid, str)
1146 except:
1147 samdb.transaction_cancel()
1148 raise
1149 else:
1150 samdb.transaction_commit()
1151 return samdb
1154 FILL_FULL = "FULL"
1155 FILL_NT4SYNC = "NT4SYNC"
1156 FILL_DRS = "DRS"
1157 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1158 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1160 def set_dir_acl(path, acl, lp, domsid):
1161 setntacl(lp, path, acl, domsid)
1162 for root, dirs, files in os.walk(path, topdown=False):
1163 for name in files:
1164 setntacl(lp, os.path.join(root, name), acl, domsid)
1165 for name in dirs:
1166 setntacl(lp, os.path.join(root, name), acl, domsid)
1169 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1170 # Set ACL for GPO
1171 policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1172 set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
1173 lp, str(domainsid))
1174 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1175 attrs=["cn", "nTSecurityDescriptor"],
1176 expression="", scope=ldb.SCOPE_ONELEVEL)
1177 for policy in res:
1178 acl = ndr_unpack(security.descriptor,
1179 str(policy["nTSecurityDescriptor"])).as_sddl()
1180 policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1181 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1182 str(domainsid))
1184 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1185 lp):
1186 try:
1187 os.chown(sysvol,-1,gid)
1188 except:
1189 canchown = False
1190 else:
1191 canchown = True
1193 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1194 for root, dirs, files in os.walk(sysvol, topdown=False):
1195 for name in files:
1196 if canchown:
1197 os.chown(os.path.join(root, name),-1,gid)
1198 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1199 for name in dirs:
1200 if canchown:
1201 os.chown(os.path.join(root, name),-1,gid)
1202 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1203 set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1206 def provision(setup_dir, logger, session_info,
1207 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1208 realm=None,
1209 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1210 serverdn=None,
1211 domain=None, hostname=None, hostip=None, hostip6=None,
1212 domainsid=None, adminpass=None, ldapadminpass=None,
1213 krbtgtpass=None, domainguid=None,
1214 policyguid=None, policyguid_dc=None, invocationid=None,
1215 machinepass=None, ntdsguid=None,
1216 dnspass=None, root=None, nobody=None, users=None,
1217 wheel=None, backup=None, aci=None, serverrole=None,
1218 dom_for_fun_level=None,
1219 ldap_backend_extra_port=None, backend_type=None,
1220 sitename=None,
1221 ol_mmr_urls=None, ol_olc=None,
1222 setup_ds_path=None, slapd_path=None, nosync=False,
1223 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1224 """Provision samba4
1226 :note: caution, this wipes all existing data!
1229 def setup_path(file):
1230 return os.path.join(setup_dir, file)
1232 if domainsid is None:
1233 domainsid = security.random_sid()
1234 else:
1235 domainsid = security.dom_sid(domainsid)
1237 # create/adapt the group policy GUIDs
1238 if policyguid is None:
1239 policyguid = str(uuid.uuid4())
1240 policyguid = policyguid.upper()
1241 if policyguid_dc is None:
1242 policyguid_dc = str(uuid.uuid4())
1243 policyguid_dc = policyguid_dc.upper()
1245 if adminpass is None:
1246 adminpass = samba.generate_random_password(12, 32)
1247 if krbtgtpass is None:
1248 krbtgtpass = samba.generate_random_password(128, 255)
1249 if machinepass is None:
1250 machinepass = samba.generate_random_password(128, 255)
1251 if dnspass is None:
1252 dnspass = samba.generate_random_password(128, 255)
1253 if ldapadminpass is None:
1254 #Make a new, random password between Samba and it's LDAP server
1255 ldapadminpass=samba.generate_random_password(128, 255)
1257 if backend_type is None:
1258 backend_type = "ldb"
1260 sid_generator = "internal"
1261 if backend_type == "fedora-ds":
1262 sid_generator = "backend"
1264 root_uid = findnss_uid([root or "root"])
1265 nobody_uid = findnss_uid([nobody or "nobody"])
1266 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1267 if wheel is None:
1268 wheel_gid = findnss_gid(["wheel", "adm"])
1269 else:
1270 wheel_gid = findnss_gid([wheel])
1271 try:
1272 bind_gid = findnss_gid(["bind", "named"])
1273 except KeyError:
1274 bind_gid = None
1276 if targetdir is not None:
1277 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1278 elif smbconf is None:
1279 smbconf = samba.param.default_path()
1280 if not os.path.exists(os.path.dirname(smbconf)):
1281 os.makedirs(os.path.dirname(smbconf))
1283 # only install a new smb.conf if there isn't one there already
1284 if os.path.exists(smbconf):
1285 # if Samba Team members can't figure out the weird errors
1286 # loading an empty smb.conf gives, then we need to be smarter.
1287 # Pretend it just didn't exist --abartlet
1288 data = open(smbconf, 'r').read()
1289 data = data.lstrip()
1290 if data is None or data == "":
1291 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1292 serverrole, targetdir, sid_generator, useeadb)
1293 else:
1294 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1295 targetdir, sid_generator, useeadb)
1297 lp = samba.param.LoadParm()
1298 lp.load(smbconf)
1300 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1301 dnsdomain=realm, serverrole=serverrole,
1302 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1303 serverdn=serverdn, sitename=sitename)
1305 paths = provision_paths_from_lp(lp, names.dnsdomain)
1307 paths.bind_gid = bind_gid
1309 if hostip is None:
1310 hostips = samba.interface_ips(lp, False)
1311 if len(hostips) == 0:
1312 logger.warning("No external IPv4 address has been found. Using loopback.")
1313 hostip = '127.0.0.1'
1314 else:
1315 hostip = hostips[0]
1316 if len(hostips) > 1:
1317 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1319 if hostip6 is None:
1320 try:
1321 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1322 if hostip6 is None:
1323 hostip6 = ip[-1][0]
1324 if hostip6 == '::1' and ip[-1][0] != '::1':
1325 hostip6 = ip[-1][0]
1326 except socket.gaierror, (socket.EAI_NODATA, msg):
1327 hostip6 = None
1329 if serverrole is None:
1330 serverrole = lp.get("server role")
1332 assert serverrole in ("domain controller", "member server", "standalone")
1333 if invocationid is None:
1334 invocationid = str(uuid.uuid4())
1336 if not os.path.exists(paths.private_dir):
1337 os.mkdir(paths.private_dir)
1338 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1339 os.mkdir(os.path.join(paths.private_dir, "tls"))
1341 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1343 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1344 serverdn=names.serverdn, am_rodc=am_rodc)
1346 if backend_type == "ldb":
1347 provision_backend = LDBBackend(backend_type,
1348 paths=paths, setup_path=setup_path,
1349 lp=lp, credentials=credentials,
1350 names=names,
1351 logger=logger)
1352 elif backend_type == "existing":
1353 provision_backend = ExistingBackend(backend_type,
1354 paths=paths, setup_path=setup_path,
1355 lp=lp, credentials=credentials,
1356 names=names,
1357 logger=logger,
1358 ldapi_url=ldapi_url)
1359 elif backend_type == "fedora-ds":
1360 provision_backend = FDSBackend(backend_type,
1361 paths=paths, setup_path=setup_path,
1362 lp=lp, credentials=credentials,
1363 names=names,
1364 logger=logger,
1365 domainsid=domainsid,
1366 schema=schema,
1367 hostname=hostname,
1368 ldapadminpass=ldapadminpass,
1369 slapd_path=slapd_path,
1370 ldap_backend_extra_port=ldap_backend_extra_port,
1371 ldap_dryrun_mode=ldap_dryrun_mode,
1372 root=root,
1373 setup_ds_path=setup_ds_path)
1374 elif backend_type == "openldap":
1375 provision_backend = OpenLDAPBackend(backend_type,
1376 paths=paths, setup_path=setup_path,
1377 lp=lp, credentials=credentials,
1378 names=names,
1379 logger=logger,
1380 domainsid=domainsid,
1381 schema=schema,
1382 hostname=hostname,
1383 ldapadminpass=ldapadminpass,
1384 slapd_path=slapd_path,
1385 ldap_backend_extra_port=ldap_backend_extra_port,
1386 ldap_dryrun_mode=ldap_dryrun_mode,
1387 ol_mmr_urls=ol_mmr_urls,
1388 nosync=nosync)
1389 else:
1390 raise ValueError("Unknown LDAP backend type selected")
1392 provision_backend.init()
1393 provision_backend.start()
1395 # only install a new shares config db if there is none
1396 if not os.path.exists(paths.shareconf):
1397 logger.info("Setting up share.ldb")
1398 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1399 lp=lp)
1400 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1403 logger.info("Setting up secrets.ldb")
1404 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1405 session_info=session_info,
1406 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1408 try:
1409 logger.info("Setting up the registry")
1410 setup_registry(paths.hklm, setup_path, session_info,
1411 lp=lp)
1413 logger.info("Setting up the privileges database")
1414 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1416 logger.info("Setting up idmap db")
1417 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1418 lp=lp)
1420 logger.info("Setting up SAM db")
1421 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1422 provision_backend, lp, names,
1423 logger=logger,
1424 domainsid=domainsid,
1425 schema=schema, domainguid=domainguid,
1426 policyguid=policyguid, policyguid_dc=policyguid_dc,
1427 fill=samdb_fill,
1428 adminpass=adminpass, krbtgtpass=krbtgtpass,
1429 invocationid=invocationid,
1430 machinepass=machinepass, dnspass=dnspass,
1431 ntdsguid=ntdsguid, serverrole=serverrole,
1432 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc)
1434 if serverrole == "domain controller":
1435 if paths.netlogon is None:
1436 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1437 logger.info("Please either remove %s or see the template at %s" %
1438 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1439 assert paths.netlogon is not None
1441 if paths.sysvol is None:
1442 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1443 " are configuring a DC.")
1444 logger.info("Please either remove %s or see the template at %s" %
1445 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1446 assert paths.sysvol is not None
1448 if not os.path.isdir(paths.netlogon):
1449 os.makedirs(paths.netlogon, 0755)
1451 if samdb_fill == FILL_FULL:
1452 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1453 root_uid=root_uid, nobody_uid=nobody_uid,
1454 users_gid=users_gid, wheel_gid=wheel_gid)
1456 if serverrole == "domain controller":
1457 # Set up group policies (domain policy and domain controller policy)
1458 setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1459 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1460 domainsid, names.dnsdomain, names.domaindn, lp)
1462 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1463 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1465 secretsdb_self_join(secrets_ldb, domain=names.domain,
1466 realm=names.realm,
1467 dnsdomain=names.dnsdomain,
1468 netbiosname=names.netbiosname,
1469 domainsid=domainsid,
1470 machinepass=machinepass,
1471 secure_channel_type=SEC_CHAN_BDC)
1473 if serverrole == "domain controller":
1474 secretsdb_setup_dns(secrets_ldb, setup_path,
1475 paths.private_dir,
1476 realm=names.realm, dnsdomain=names.dnsdomain,
1477 dns_keytab_path=paths.dns_keytab,
1478 dnspass=dnspass)
1480 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1481 assert isinstance(domainguid, str)
1483 # Only make a zone file on the first DC, it should be replicated
1484 # with DNS replication
1485 create_zone_file(lp, logger, paths, targetdir, setup_path,
1486 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1487 hostname=names.hostname, realm=names.realm,
1488 domainguid=domainguid, ntdsguid=names.ntdsguid)
1490 create_named_conf(paths, setup_path, realm=names.realm,
1491 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1493 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1494 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1495 keytab_name=paths.dns_keytab)
1496 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1497 logger.info("and %s for further documentation required for secure DNS "
1498 "updates", paths.namedtxt)
1500 create_krb5_conf(paths.krb5conf, setup_path,
1501 dnsdomain=names.dnsdomain, hostname=names.hostname,
1502 realm=names.realm)
1503 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1504 "generated at %s", paths.krb5conf)
1506 lastProvisionUSNs = get_last_provision_usn(samdb)
1507 maxUSN = get_max_usn(samdb, str(names.rootdn))
1508 if lastProvisionUSNs is not None:
1509 update_provision_usn(samdb, 0, maxUSN, 1)
1510 else:
1511 set_provision_usn(samdb, 0, maxUSN)
1513 if serverrole == "domain controller":
1514 create_dns_update_list(lp, logger, paths, setup_path)
1516 provision_backend.post_setup()
1517 provision_backend.shutdown()
1519 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1520 ldapi_url)
1521 except:
1522 secrets_ldb.transaction_cancel()
1523 raise
1525 #Now commit the secrets.ldb to disk
1526 secrets_ldb.transaction_commit()
1528 # the commit creates the dns.keytab, now chown it
1529 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1530 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1531 try:
1532 os.chmod(dns_keytab_path, 0640)
1533 os.chown(dns_keytab_path, -1, paths.bind_gid)
1534 except OSError:
1535 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1536 paths.bind_gid)
1539 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1540 paths.phpldapadminconfig)
1542 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1543 logger.info("Server Role: %s" % serverrole)
1544 logger.info("Hostname: %s" % names.hostname)
1545 logger.info("NetBIOS Domain: %s" % names.domain)
1546 logger.info("DNS Domain: %s" % names.dnsdomain)
1547 logger.info("DOMAIN SID: %s" % str(domainsid))
1548 if samdb_fill == FILL_FULL:
1549 logger.info("Admin password: %s" % adminpass)
1550 if provision_backend.type is not "ldb":
1551 if provision_backend.credentials.get_bind_dn() is not None:
1552 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1553 else:
1554 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1556 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1558 if provision_backend.slapd_command_escaped is not None:
1559 # now display slapd_command_file.txt to show how slapd must be started next time
1560 logger.info("Use later the following commandline to start slapd, then Samba:")
1561 logger.info(provision_backend.slapd_command_escaped)
1562 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1563 provision_backend.ldapdir)
1565 result = ProvisionResult()
1566 result.domaindn = domaindn
1567 result.paths = paths
1568 result.lp = lp
1569 result.samdb = samdb
1570 return result
1573 def provision_become_dc(setup_dir=None,
1574 smbconf=None, targetdir=None, realm=None,
1575 rootdn=None, domaindn=None, schemadn=None,
1576 configdn=None, serverdn=None,
1577 domain=None, hostname=None, domainsid=None,
1578 adminpass=None, krbtgtpass=None, domainguid=None,
1579 policyguid=None, policyguid_dc=None, invocationid=None,
1580 machinepass=None,
1581 dnspass=None, root=None, nobody=None, users=None,
1582 wheel=None, backup=None, serverrole=None,
1583 ldap_backend=None, ldap_backend_type=None,
1584 sitename=None, debuglevel=1):
1586 logger = logging.getLogger("provision")
1587 samba.set_debug_level(debuglevel)
1589 return provision(setup_dir, logger, system_session(), None,
1590 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1591 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1592 configdn=configdn, serverdn=serverdn, domain=domain,
1593 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1594 machinepass=machinepass, serverrole="domain controller",
1595 sitename=sitename)
1598 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1599 """Create a PHP LDAP admin configuration file.
1601 :param path: Path to write the configuration to.
1602 :param setup_path: Function to generate setup paths.
1604 setup_file(setup_path("phpldapadmin-config.php"), path,
1605 {"S4_LDAPI_URI": ldapi_uri})
1608 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1609 hostip, hostip6, hostname, realm, domainguid,
1610 ntdsguid):
1611 """Write out a DNS zone file, from the info in the current database.
1613 :param paths: paths object
1614 :param setup_path: Setup path function.
1615 :param dnsdomain: DNS Domain name
1616 :param domaindn: DN of the Domain
1617 :param hostip: Local IPv4 IP
1618 :param hostip6: Local IPv6 IP
1619 :param hostname: Local hostname
1620 :param realm: Realm name
1621 :param domainguid: GUID of the domain.
1622 :param ntdsguid: GUID of the hosts nTDSDSA record.
1624 assert isinstance(domainguid, str)
1626 if hostip6 is not None:
1627 hostip6_base_line = " IN AAAA " + hostip6
1628 hostip6_host_line = hostname + " IN AAAA " + hostip6
1629 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1630 else:
1631 hostip6_base_line = ""
1632 hostip6_host_line = ""
1633 gc_msdcs_ip6_line = ""
1635 if hostip is not None:
1636 hostip_base_line = " IN A " + hostip
1637 hostip_host_line = hostname + " IN A " + hostip
1638 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1639 else:
1640 hostip_base_line = ""
1641 hostip_host_line = ""
1642 gc_msdcs_ip_line = ""
1644 dns_dir = os.path.dirname(paths.dns)
1646 try:
1647 shutil.rmtree(dns_dir, True)
1648 except OSError:
1649 pass
1651 os.mkdir(dns_dir, 0775)
1653 # we need to freeze the zone while we update the contents
1654 if targetdir is None:
1655 rndc = ' '.join(lp.get("rndc command"))
1656 os.system(rndc + " freeze " + lp.get("realm"))
1658 setup_file(setup_path("provision.zone"), paths.dns, {
1659 "HOSTNAME": hostname,
1660 "DNSDOMAIN": dnsdomain,
1661 "REALM": realm,
1662 "HOSTIP_BASE_LINE": hostip_base_line,
1663 "HOSTIP_HOST_LINE": hostip_host_line,
1664 "DOMAINGUID": domainguid,
1665 "DATESTRING": time.strftime("%Y%m%d%H"),
1666 "DEFAULTSITE": DEFAULTSITE,
1667 "NTDSGUID": ntdsguid,
1668 "HOSTIP6_BASE_LINE": hostip6_base_line,
1669 "HOSTIP6_HOST_LINE": hostip6_host_line,
1670 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1671 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1674 # note that we use no variable substitution on this file
1675 # the substitution is done at runtime by samba_dnsupdate
1676 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1678 # and the SPN update list
1679 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1681 if paths.bind_gid is not None:
1682 try:
1683 os.chown(dns_dir, -1, paths.bind_gid)
1684 os.chown(paths.dns, -1, paths.bind_gid)
1685 # chmod needed to cope with umask
1686 os.chmod(dns_dir, 0775)
1687 os.chmod(paths.dns, 0664)
1688 except OSError:
1689 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1691 if targetdir is None:
1692 os.system(rndc + " unfreeze " + lp.get("realm"))
1695 def create_dns_update_list(lp, logger, paths, setup_path):
1696 """Write out a dns_update_list file"""
1697 # note that we use no variable substitution on this file
1698 # the substitution is done at runtime by samba_dnsupdate
1699 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1700 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1703 def create_named_conf(paths, setup_path, realm, dnsdomain,
1704 private_dir):
1705 """Write out a file containing zone statements suitable for inclusion in a
1706 named.conf file (including GSS-TSIG configuration).
1708 :param paths: all paths
1709 :param setup_path: Setup path function.
1710 :param realm: Realm name
1711 :param dnsdomain: DNS Domain name
1712 :param private_dir: Path to private directory
1713 :param keytab_name: File name of DNS keytab file
1716 setup_file(setup_path("named.conf"), paths.namedconf, {
1717 "DNSDOMAIN": dnsdomain,
1718 "REALM": realm,
1719 "ZONE_FILE": paths.dns,
1720 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1721 "NAMED_CONF": paths.namedconf,
1722 "NAMED_CONF_UPDATE": paths.namedconf_update
1725 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1728 def create_named_txt(path, setup_path, realm, dnsdomain,
1729 private_dir, keytab_name):
1730 """Write out a file containing zone statements suitable for inclusion in a
1731 named.conf file (including GSS-TSIG configuration).
1733 :param path: Path of the new named.conf file.
1734 :param setup_path: Setup path function.
1735 :param realm: Realm name
1736 :param dnsdomain: DNS Domain name
1737 :param private_dir: Path to private directory
1738 :param keytab_name: File name of DNS keytab file
1741 setup_file(setup_path("named.txt"), path, {
1742 "DNSDOMAIN": dnsdomain,
1743 "REALM": realm,
1744 "DNS_KEYTAB": keytab_name,
1745 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1746 "PRIVATE_DIR": private_dir
1750 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1751 """Write out a file containing zone statements suitable for inclusion in a
1752 named.conf file (including GSS-TSIG configuration).
1754 :param path: Path of the new named.conf file.
1755 :param setup_path: Setup path function.
1756 :param dnsdomain: DNS Domain name
1757 :param hostname: Local hostname
1758 :param realm: Realm name
1760 setup_file(setup_path("krb5.conf"), path, {
1761 "DNSDOMAIN": dnsdomain,
1762 "HOSTNAME": hostname,
1763 "REALM": realm,
1767 class ProvisioningError(Exception):
1768 """A generic provision error."""
1770 def __init__(self, value):
1771 self.value = value
1773 def __str__(self):
1774 return "ProvisioningError: " + self.value
1777 class InvalidNetbiosName(Exception):
1778 """A specified name was not a valid NetBIOS name."""
1779 def __init__(self, name):
1780 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)