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
42 from samba.auth import system_session, admin_session
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_R2
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 (
61 from samba.schema import Schema
62 from samba.samdb import SamDB
64 __docformat__ = "restructuredText"
67 """Find the setup directory used by provision."""
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):
76 dirname = os.path.dirname(__file__)
77 ret = os.path.join(dirname, "../../../setup")
78 if os.path.isdir(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)
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)" \
148 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
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)
159 DEFAULTSITE = "Default-First-Site-Name"
160 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
162 class ProvisionPaths(object):
165 self.shareconf = None
176 self.dns_keytab = None
179 self.private_dir = None
182 class ProvisionNames(object):
189 self.ldapmanagerdn = None
190 self.dnsdomain = None
192 self.netbiosname = 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
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)
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]:
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)
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
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"""
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,
248 LAST_PROVISION_USN_ATTRIBUTE)
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
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])
282 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
283 tab = p.split(str(r))
291 class ProvisionResult(object):
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.
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,controls=["relax:0"]):
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, controls)
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()
370 setup_add_ldif(ldb, ldif_path, subst_vars)
372 ldb.transaction_cancel()
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
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."""
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":
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))
470 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
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))
484 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
490 configdn = "CN=Configuration," + rootdn
492 schemadn = "CN=Schema," + configdn
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
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)
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
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":
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)
548 if targetdir is not None:
549 privdir = os.path.join(targetdir, "private")
551 privdir = default_lp.get("private dir")
552 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir, "eadb.tdb"))
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))
565 if sid_generator == "internal":
566 sid_generator_line = ""
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),
575 "NETBIOS_NAME": netbiosname,
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,
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
627 os.unlink(samdb_path)
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()
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)
657 samdb.transaction_cancel()
660 samdb.transaction_commit()
663 def secretsdb_self_join(secretsdb, domain,
664 netbiosname, machinepass, domainsid=None,
665 realm=None, dnsdomain=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",
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"]
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",
704 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
705 scope=ldb.SCOPE_ONELEVEL)
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)
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"]
724 el.set_flags(ldb.FLAG_MOD_REPLACE)
725 secretsdb.modify(msg)
730 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
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
740 os.unlink(os.path.join(private_dir, dns_keytab_path))
744 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
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):
767 secrets_ldb = Ldb(path, session_info=session_info,
770 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
771 secrets_ldb = Ldb(path, session_info=session_info,
773 secrets_ldb.transaction_start()
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())
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())
792 secrets_ldb.transaction_cancel()
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):
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,
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):
842 idmap_ldb = IDmapDB(path, session_info=session_info,
846 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
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, next_rid, invocationid, setup_path,
872 policyguid, policyguid_dc, domainControllerFunctionality,
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
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 "DCRID": str(next_rid),
894 "DNSDOMAIN": names.dnsdomain,
895 "SAMBA_VERSION_STRING": version,
896 "NTDSGUID": ntdsguid_line,
897 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
899 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
900 "POLICYGUID": policyguid,
901 "POLICYGUID_DC": policyguid_dc,
902 "DNSDOMAIN": names.dnsdomain,
903 "DOMAINSID": str(domainsid),
904 "DOMAINDN": names.domaindn})
906 # add the NTDSGUID based SPNs
907 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=%s,CN=Sites,CN=Configuration,%s" % (names.hostname, names.sitename, names.domaindn)
908 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
909 expression="", scope=ldb.SCOPE_BASE)
910 assert isinstance(names.ntdsguid, str)
912 # Setup fSMORoleOwner entries to point at the newly created DC entry
913 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
914 "DOMAIN": names.domain,
915 "DNSDOMAIN": names.dnsdomain,
916 "DOMAINDN": names.domaindn,
917 "CONFIGDN": names.configdn,
918 "SCHEMADN": names.schemadn,
919 "DEFAULTSITE": names.sitename,
920 "SERVERDN": names.serverdn,
921 "NETBIOSNAME": names.netbiosname,
922 "NTDSGUID": names.ntdsguid,
923 "DNSPASS_B64": b64encode(dnspass),
924 "RIDALLOCATIONSTART": str(next_rid + 100),
925 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
928 def getpolicypath(sysvolpath, dnsdomain, guid):
931 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
934 def create_gpo_struct(policy_path):
935 os.makedirs(policy_path, 0755)
936 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
937 "[General]\r\nVersion=65543")
938 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
939 os.makedirs(os.path.join(policy_path, "USER"), 0755)
942 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
943 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
944 create_gpo_struct(policy_path)
946 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
947 create_gpo_struct(policy_path)
950 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
951 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
952 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
953 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
955 """Setup a complete SAM Database.
957 :note: This will wipe the main SAM database file!
960 # ATTENTION: Do NOT change these default values without discussion with the
961 # team and/or release manager. They have a big impact on the whole program!
962 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
964 if dom_for_fun_level is None:
965 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
967 if dom_for_fun_level > domainControllerFunctionality:
968 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_R2). This won't work!")
970 domainFunctionality = dom_for_fun_level
971 forestFunctionality = dom_for_fun_level
973 # Also wipes the database
974 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
975 provision_backend=provision_backend, session_info=session_info,
976 names=names, serverrole=serverrole, schema=schema)
979 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
981 # Load the database, but don's load the global schema and don't connect quite yet
982 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
983 credentials=provision_backend.credentials, lp=lp, global_schema=False,
986 logger.info("Pre-loading the Samba 4 and AD schema")
988 # Load the schema from the one we computed earlier
989 samdb.set_schema(schema)
991 # And now we can connect to the DB - the schema won't be loaded from the DB
997 samdb.transaction_start()
999 # Set the domain functionality levels onto the database.
1000 # Various module (the password_hash module in particular) need
1001 # to know what level of AD we are emulating.
1003 # These will be fixed into the database via the database
1004 # modifictions below, but we need them set from the start.
1005 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1006 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1007 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1009 samdb.set_domain_sid(str(domainsid))
1010 samdb.set_invocation_id(invocationid)
1011 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1013 logger.info("Adding DomainDN: %s" % names.domaindn)
1015 #impersonate domain admin
1016 admin_session_info = admin_session(lp, str(domainsid))
1017 samdb.set_session_info(admin_session_info)
1018 if domainguid is not None:
1019 domainguid_line = "objectGUID: %s\n-" % domainguid
1021 domainguid_line = ""
1023 descr = b64encode(get_domain_descriptor(domainsid))
1024 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1025 "DOMAINDN": names.domaindn,
1026 "DOMAINGUID": domainguid_line,
1031 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1032 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1033 "DOMAINSID": str(domainsid),
1034 "NEXTRID": str(next_rid),
1035 "SCHEMADN": names.schemadn,
1036 "NETBIOSNAME": names.netbiosname,
1037 "DEFAULTSITE": names.sitename,
1038 "CONFIGDN": names.configdn,
1039 "SERVERDN": names.serverdn,
1040 "POLICYGUID": policyguid,
1041 "DOMAINDN": names.domaindn,
1042 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1043 "SAMBA_VERSION_STRING": version
1046 logger.info("Adding configuration container")
1047 descr = b64encode(get_config_descriptor(domainsid))
1048 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1049 "CONFIGDN": names.configdn,
1050 "DESCRIPTOR": descr,
1053 # The LDIF here was created when the Schema object was constructed
1054 logger.info("Setting up sam.ldb schema")
1055 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1056 samdb.modify_ldif(schema.schema_dn_modify)
1057 samdb.write_prefixes_from_schema()
1058 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1059 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1060 {"SCHEMADN": names.schemadn})
1062 logger.info("Reopening sam.ldb with new schema")
1064 samdb.transaction_cancel()
1067 samdb.transaction_commit()
1069 samdb = SamDB(session_info=admin_session_info,
1070 credentials=provision_backend.credentials, lp=lp,
1071 global_schema=False, am_rodc=am_rodc)
1073 samdb.transaction_start()
1075 samdb.invocation_id = invocationid
1077 logger.info("Setting up sam.ldb configuration data")
1078 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1079 "CONFIGDN": names.configdn,
1080 "NETBIOSNAME": names.netbiosname,
1081 "DEFAULTSITE": names.sitename,
1082 "DNSDOMAIN": names.dnsdomain,
1083 "DOMAIN": names.domain,
1084 "SCHEMADN": names.schemadn,
1085 "DOMAINDN": names.domaindn,
1086 "SERVERDN": names.serverdn,
1087 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1088 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
1091 logger.info("Setting up display specifiers")
1092 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1093 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1094 check_all_substituted(display_specifiers_ldif)
1095 samdb.add_ldif(display_specifiers_ldif)
1097 logger.info("Adding users container")
1098 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1099 "DOMAINDN": names.domaindn})
1100 logger.info("Modifying users container")
1101 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1102 "DOMAINDN": names.domaindn})
1103 logger.info("Adding computers container")
1104 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1105 "DOMAINDN": names.domaindn})
1106 logger.info("Modifying computers container")
1107 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1108 "DOMAINDN": names.domaindn})
1109 logger.info("Setting up sam.ldb data")
1110 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1111 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1112 "DOMAINDN": names.domaindn,
1113 "NETBIOSNAME": names.netbiosname,
1114 "DEFAULTSITE": names.sitename,
1115 "CONFIGDN": names.configdn,
1116 "SERVERDN": names.serverdn,
1117 "RIDAVAILABLESTART": str(next_rid + 600),
1118 "POLICYGUID_DC": policyguid_dc
1121 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1122 "DOMAINDN": names.domaindn})
1124 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1125 "CONFIGDN": names.configdn,
1126 "SCHEMADN": names.schemadn})
1127 if fill == FILL_FULL:
1128 logger.info("Setting up sam.ldb users and groups")
1129 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1130 "DOMAINDN": names.domaindn,
1131 "DOMAINSID": str(domainsid),
1132 "CONFIGDN": names.configdn,
1133 "ADMINPASS_B64": b64encode(adminpass),
1134 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1137 logger.info("Setting up self join")
1138 setup_self_join(samdb, names=names, invocationid=invocationid,
1140 machinepass=machinepass,
1141 domainsid=domainsid,
1143 policyguid=policyguid,
1144 policyguid_dc=policyguid_dc,
1145 setup_path=setup_path,
1146 domainControllerFunctionality=domainControllerFunctionality,
1149 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=%s,CN=Sites,CN=Configuration,%s" % (names.hostname, names.sitename, names.domaindn)
1150 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1151 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1152 assert isinstance(names.ntdsguid, str)
1154 samdb.transaction_cancel()
1157 samdb.transaction_commit()
1162 FILL_NT4SYNC = "NT4SYNC"
1164 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1165 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)"
1167 def set_dir_acl(path, acl, lp, domsid):
1168 setntacl(lp, path, acl, domsid)
1169 for root, dirs, files in os.walk(path, topdown=False):
1171 setntacl(lp, os.path.join(root, name), acl, domsid)
1173 setntacl(lp, os.path.join(root, name), acl, domsid)
1176 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1178 policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1179 set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
1181 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1182 attrs=["cn", "nTSecurityDescriptor"],
1183 expression="", scope=ldb.SCOPE_ONELEVEL)
1185 acl = ndr_unpack(security.descriptor,
1186 str(policy["nTSecurityDescriptor"])).as_sddl()
1187 policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1188 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1191 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1194 os.chown(sysvol,-1,gid)
1200 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1201 for root, dirs, files in os.walk(sysvol, topdown=False):
1204 os.chown(os.path.join(root, name),-1,gid)
1205 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1208 os.chown(os.path.join(root, name),-1,gid)
1209 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1210 set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1213 def provision(setup_dir, logger, session_info,
1214 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1216 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1218 domain=None, hostname=None, hostip=None, hostip6=None,
1219 domainsid=None, adminpass=None, ldapadminpass=None,
1220 krbtgtpass=None, domainguid=None,
1221 policyguid=None, policyguid_dc=None, invocationid=None,
1222 machinepass=None, ntdsguid=None,
1223 dnspass=None, root=None, nobody=None, users=None,
1224 wheel=None, backup=None, aci=None, serverrole=None,
1225 dom_for_fun_level=None,
1226 ldap_backend_extra_port=None, backend_type=None,
1228 ol_mmr_urls=None, ol_olc=None,
1229 setup_ds_path=None, slapd_path=None, nosync=False,
1230 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1233 :note: caution, this wipes all existing data!
1236 def setup_path(file):
1237 return os.path.join(setup_dir, file)
1239 if domainsid is None:
1240 domainsid = security.random_sid()
1242 domainsid = security.dom_sid(domainsid)
1244 # create/adapt the group policy GUIDs
1245 if policyguid is None:
1246 policyguid = str(uuid.uuid4())
1247 policyguid = policyguid.upper()
1248 if policyguid_dc is None:
1249 policyguid_dc = str(uuid.uuid4())
1250 policyguid_dc = policyguid_dc.upper()
1252 if adminpass is None:
1253 adminpass = samba.generate_random_password(12, 32)
1254 if krbtgtpass is None:
1255 krbtgtpass = samba.generate_random_password(128, 255)
1256 if machinepass is None:
1257 machinepass = samba.generate_random_password(128, 255)
1259 dnspass = samba.generate_random_password(128, 255)
1260 if ldapadminpass is None:
1261 #Make a new, random password between Samba and it's LDAP server
1262 ldapadminpass=samba.generate_random_password(128, 255)
1264 if backend_type is None:
1265 backend_type = "ldb"
1267 sid_generator = "internal"
1268 if backend_type == "fedora-ds":
1269 sid_generator = "backend"
1271 root_uid = findnss_uid([root or "root"])
1272 nobody_uid = findnss_uid([nobody or "nobody"])
1273 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1275 wheel_gid = findnss_gid(["wheel", "adm"])
1277 wheel_gid = findnss_gid([wheel])
1279 bind_gid = findnss_gid(["bind", "named"])
1283 if targetdir is not None:
1284 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1285 elif smbconf is None:
1286 smbconf = samba.param.default_path()
1287 if not os.path.exists(os.path.dirname(smbconf)):
1288 os.makedirs(os.path.dirname(smbconf))
1290 # only install a new smb.conf if there isn't one there already
1291 if os.path.exists(smbconf):
1292 # if Samba Team members can't figure out the weird errors
1293 # loading an empty smb.conf gives, then we need to be smarter.
1294 # Pretend it just didn't exist --abartlet
1295 data = open(smbconf, 'r').read()
1296 data = data.lstrip()
1297 if data is None or data == "":
1298 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1299 serverrole, targetdir, sid_generator, useeadb)
1301 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1302 targetdir, sid_generator, useeadb)
1304 lp = samba.param.LoadParm()
1307 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1308 dnsdomain=realm, serverrole=serverrole,
1309 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1310 serverdn=serverdn, sitename=sitename)
1312 paths = provision_paths_from_lp(lp, names.dnsdomain)
1314 paths.bind_gid = bind_gid
1317 hostips = samba.interface_ips(lp, False)
1318 if len(hostips) == 0:
1319 logger.warning("No external IPv4 address has been found. Using loopback.")
1320 hostip = '127.0.0.1'
1323 if len(hostips) > 1:
1324 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1328 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1331 if hostip6 == '::1' and ip[-1][0] != '::1':
1333 except socket.gaierror, (socket.EAI_NODATA, msg):
1336 if serverrole is None:
1337 serverrole = lp.get("server role")
1339 assert serverrole in ("domain controller", "member server", "standalone")
1340 if invocationid is None:
1341 invocationid = str(uuid.uuid4())
1343 if not os.path.exists(paths.private_dir):
1344 os.mkdir(paths.private_dir)
1345 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1346 os.mkdir(os.path.join(paths.private_dir, "tls"))
1348 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1350 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1351 serverdn=names.serverdn)
1353 if backend_type == "ldb":
1354 provision_backend = LDBBackend(backend_type,
1355 paths=paths, setup_path=setup_path,
1356 lp=lp, credentials=credentials,
1359 elif backend_type == "existing":
1360 provision_backend = ExistingBackend(backend_type,
1361 paths=paths, setup_path=setup_path,
1362 lp=lp, credentials=credentials,
1365 ldapi_url=ldapi_url)
1366 elif backend_type == "fedora-ds":
1367 provision_backend = FDSBackend(backend_type,
1368 paths=paths, setup_path=setup_path,
1369 lp=lp, credentials=credentials,
1372 domainsid=domainsid,
1375 ldapadminpass=ldapadminpass,
1376 slapd_path=slapd_path,
1377 ldap_backend_extra_port=ldap_backend_extra_port,
1378 ldap_dryrun_mode=ldap_dryrun_mode,
1380 setup_ds_path=setup_ds_path)
1381 elif backend_type == "openldap":
1382 provision_backend = OpenLDAPBackend(backend_type,
1383 paths=paths, setup_path=setup_path,
1384 lp=lp, credentials=credentials,
1387 domainsid=domainsid,
1390 ldapadminpass=ldapadminpass,
1391 slapd_path=slapd_path,
1392 ldap_backend_extra_port=ldap_backend_extra_port,
1393 ldap_dryrun_mode=ldap_dryrun_mode,
1394 ol_mmr_urls=ol_mmr_urls,
1397 raise ValueError("Unknown LDAP backend type selected")
1399 provision_backend.init()
1400 provision_backend.start()
1402 # only install a new shares config db if there is none
1403 if not os.path.exists(paths.shareconf):
1404 logger.info("Setting up share.ldb")
1405 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1407 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1410 logger.info("Setting up secrets.ldb")
1411 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1412 session_info=session_info,
1413 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1416 logger.info("Setting up the registry")
1417 setup_registry(paths.hklm, setup_path, session_info,
1420 logger.info("Setting up the privileges database")
1421 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1423 logger.info("Setting up idmap db")
1424 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1427 logger.info("Setting up SAM db")
1428 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1429 provision_backend, lp, names,
1431 domainsid=domainsid,
1432 schema=schema, domainguid=domainguid,
1433 policyguid=policyguid, policyguid_dc=policyguid_dc,
1435 adminpass=adminpass, krbtgtpass=krbtgtpass,
1436 invocationid=invocationid,
1437 machinepass=machinepass, dnspass=dnspass,
1438 ntdsguid=ntdsguid, serverrole=serverrole,
1439 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc)
1441 if serverrole == "domain controller":
1442 if paths.netlogon is None:
1443 logger.info("Existing smb.conf does not have a [netlogon] share, but you 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.netlogon is not None
1448 if paths.sysvol is None:
1449 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1450 " are configuring a DC.")
1451 logger.info("Please either remove %s or see the template at %s" %
1452 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1453 assert paths.sysvol is not None
1455 if not os.path.isdir(paths.netlogon):
1456 os.makedirs(paths.netlogon, 0755)
1458 if samdb_fill == FILL_FULL:
1459 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1460 root_uid=root_uid, nobody_uid=nobody_uid,
1461 users_gid=users_gid, wheel_gid=wheel_gid)
1463 if serverrole == "domain controller":
1464 # Set up group policies (domain policy and domain controller policy)
1465 setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1466 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1467 domainsid, names.dnsdomain, names.domaindn, lp)
1469 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1470 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1472 secretsdb_self_join(secrets_ldb, domain=names.domain,
1474 dnsdomain=names.dnsdomain,
1475 netbiosname=names.netbiosname,
1476 domainsid=domainsid,
1477 machinepass=machinepass,
1478 secure_channel_type=SEC_CHAN_BDC)
1480 if serverrole == "domain controller":
1481 secretsdb_setup_dns(secrets_ldb, setup_path,
1483 realm=names.realm, dnsdomain=names.dnsdomain,
1484 dns_keytab_path=paths.dns_keytab,
1487 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1488 assert isinstance(domainguid, str)
1490 # Only make a zone file on the first DC, it should be replicated
1491 # with DNS replication
1492 create_zone_file(lp, logger, paths, targetdir, setup_path,
1493 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1494 hostname=names.hostname, realm=names.realm,
1495 domainguid=domainguid, ntdsguid=names.ntdsguid)
1497 create_named_conf(paths, setup_path, realm=names.realm,
1498 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1500 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1501 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1502 keytab_name=paths.dns_keytab)
1503 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1504 logger.info("and %s for further documentation required for secure DNS "
1505 "updates", paths.namedtxt)
1507 create_krb5_conf(paths.krb5conf, setup_path,
1508 dnsdomain=names.dnsdomain, hostname=names.hostname,
1510 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1511 "generated at %s", paths.krb5conf)
1513 lastProvisionUSNs = get_last_provision_usn(samdb)
1514 maxUSN = get_max_usn(samdb, str(names.rootdn))
1515 if lastProvisionUSNs is not None:
1516 update_provision_usn(samdb, 0, maxUSN, 1)
1518 set_provision_usn(samdb, 0, maxUSN)
1520 if serverrole == "domain controller":
1521 create_dns_update_list(lp, logger, paths, setup_path)
1523 provision_backend.post_setup()
1524 provision_backend.shutdown()
1526 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1529 secrets_ldb.transaction_cancel()
1532 #Now commit the secrets.ldb to disk
1533 secrets_ldb.transaction_commit()
1535 # the commit creates the dns.keytab, now chown it
1536 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1537 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1539 os.chmod(dns_keytab_path, 0640)
1540 os.chown(dns_keytab_path, -1, paths.bind_gid)
1542 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1546 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1547 paths.phpldapadminconfig)
1549 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1550 logger.info("Server Role: %s" % serverrole)
1551 logger.info("Hostname: %s" % names.hostname)
1552 logger.info("NetBIOS Domain: %s" % names.domain)
1553 logger.info("DNS Domain: %s" % names.dnsdomain)
1554 logger.info("DOMAIN SID: %s" % str(domainsid))
1555 if samdb_fill == FILL_FULL:
1556 logger.info("Admin password: %s" % adminpass)
1557 if provision_backend.type is not "ldb":
1558 if provision_backend.credentials.get_bind_dn() is not None:
1559 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1561 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1563 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1565 if provision_backend.slapd_command_escaped is not None:
1566 # now display slapd_command_file.txt to show how slapd must be started next time
1567 logger.info("Use later the following commandline to start slapd, then Samba:")
1568 logger.info(provision_backend.slapd_command_escaped)
1569 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1570 provision_backend.ldapdir)
1572 result = ProvisionResult()
1573 result.domaindn = domaindn
1574 result.paths = paths
1576 result.samdb = samdb
1580 def provision_become_dc(setup_dir=None,
1581 smbconf=None, targetdir=None, realm=None,
1582 rootdn=None, domaindn=None, schemadn=None,
1583 configdn=None, serverdn=None,
1584 domain=None, hostname=None, domainsid=None,
1585 adminpass=None, krbtgtpass=None, domainguid=None,
1586 policyguid=None, policyguid_dc=None, invocationid=None,
1588 dnspass=None, root=None, nobody=None, users=None,
1589 wheel=None, backup=None, serverrole=None,
1590 ldap_backend=None, ldap_backend_type=None,
1591 sitename=None, debuglevel=1):
1593 logger = logging.getLogger("provision")
1594 samba.set_debug_level(debuglevel)
1596 return provision(setup_dir, logger, system_session(), None,
1597 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1598 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1599 configdn=configdn, serverdn=serverdn, domain=domain,
1600 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1601 machinepass=machinepass, serverrole="domain controller",
1605 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1606 """Create a PHP LDAP admin configuration file.
1608 :param path: Path to write the configuration to.
1609 :param setup_path: Function to generate setup paths.
1611 setup_file(setup_path("phpldapadmin-config.php"), path,
1612 {"S4_LDAPI_URI": ldapi_uri})
1615 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1616 hostip, hostip6, hostname, realm, domainguid,
1618 """Write out a DNS zone file, from the info in the current database.
1620 :param paths: paths object
1621 :param setup_path: Setup path function.
1622 :param dnsdomain: DNS Domain name
1623 :param domaindn: DN of the Domain
1624 :param hostip: Local IPv4 IP
1625 :param hostip6: Local IPv6 IP
1626 :param hostname: Local hostname
1627 :param realm: Realm name
1628 :param domainguid: GUID of the domain.
1629 :param ntdsguid: GUID of the hosts nTDSDSA record.
1631 assert isinstance(domainguid, str)
1633 if hostip6 is not None:
1634 hostip6_base_line = " IN AAAA " + hostip6
1635 hostip6_host_line = hostname + " IN AAAA " + hostip6
1636 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1638 hostip6_base_line = ""
1639 hostip6_host_line = ""
1640 gc_msdcs_ip6_line = ""
1642 if hostip is not None:
1643 hostip_base_line = " IN A " + hostip
1644 hostip_host_line = hostname + " IN A " + hostip
1645 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1647 hostip_base_line = ""
1648 hostip_host_line = ""
1649 gc_msdcs_ip_line = ""
1651 dns_dir = os.path.dirname(paths.dns)
1654 shutil.rmtree(dns_dir, True)
1658 os.mkdir(dns_dir, 0775)
1660 # we need to freeze the zone while we update the contents
1661 if targetdir is None:
1662 rndc = ' '.join(lp.get("rndc command"))
1663 os.system(rndc + " freeze " + lp.get("realm"))
1665 setup_file(setup_path("provision.zone"), paths.dns, {
1666 "HOSTNAME": hostname,
1667 "DNSDOMAIN": dnsdomain,
1669 "HOSTIP_BASE_LINE": hostip_base_line,
1670 "HOSTIP_HOST_LINE": hostip_host_line,
1671 "DOMAINGUID": domainguid,
1672 "DATESTRING": time.strftime("%Y%m%d%H"),
1673 "DEFAULTSITE": DEFAULTSITE,
1674 "NTDSGUID": ntdsguid,
1675 "HOSTIP6_BASE_LINE": hostip6_base_line,
1676 "HOSTIP6_HOST_LINE": hostip6_host_line,
1677 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1678 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1681 # note that we use no variable substitution on this file
1682 # the substitution is done at runtime by samba_dnsupdate
1683 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1685 # and the SPN update list
1686 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1688 if paths.bind_gid is not None:
1690 os.chown(dns_dir, -1, paths.bind_gid)
1691 os.chown(paths.dns, -1, paths.bind_gid)
1692 # chmod needed to cope with umask
1693 os.chmod(dns_dir, 0775)
1694 os.chmod(paths.dns, 0664)
1696 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1698 if targetdir is None:
1699 os.system(rndc + " unfreeze " + lp.get("realm"))
1702 def create_dns_update_list(lp, logger, paths, setup_path):
1703 """Write out a dns_update_list file"""
1704 # note that we use no variable substitution on this file
1705 # the substitution is done at runtime by samba_dnsupdate
1706 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1707 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1710 def create_named_conf(paths, setup_path, realm, dnsdomain,
1712 """Write out a file containing zone statements suitable for inclusion in a
1713 named.conf file (including GSS-TSIG configuration).
1715 :param paths: all paths
1716 :param setup_path: Setup path function.
1717 :param realm: Realm name
1718 :param dnsdomain: DNS Domain name
1719 :param private_dir: Path to private directory
1720 :param keytab_name: File name of DNS keytab file
1723 setup_file(setup_path("named.conf"), paths.namedconf, {
1724 "DNSDOMAIN": dnsdomain,
1726 "ZONE_FILE": paths.dns,
1727 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1728 "NAMED_CONF": paths.namedconf,
1729 "NAMED_CONF_UPDATE": paths.namedconf_update
1732 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1735 def create_named_txt(path, setup_path, realm, dnsdomain,
1736 private_dir, keytab_name):
1737 """Write out a file containing zone statements suitable for inclusion in a
1738 named.conf file (including GSS-TSIG configuration).
1740 :param path: Path of the new named.conf file.
1741 :param setup_path: Setup path function.
1742 :param realm: Realm name
1743 :param dnsdomain: DNS Domain name
1744 :param private_dir: Path to private directory
1745 :param keytab_name: File name of DNS keytab file
1748 setup_file(setup_path("named.txt"), path, {
1749 "DNSDOMAIN": dnsdomain,
1751 "DNS_KEYTAB": keytab_name,
1752 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1753 "PRIVATE_DIR": private_dir
1757 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1758 """Write out a file containing zone statements suitable for inclusion in a
1759 named.conf file (including GSS-TSIG configuration).
1761 :param path: Path of the new named.conf file.
1762 :param setup_path: Setup path function.
1763 :param dnsdomain: DNS Domain name
1764 :param hostname: Local hostname
1765 :param realm: Realm name
1767 setup_file(setup_path("krb5.conf"), path, {
1768 "DNSDOMAIN": dnsdomain,
1769 "HOSTNAME": hostname,
1774 class ProvisioningError(Exception):
1775 """A generic provision error."""
1777 def __init__(self, value):
1781 return "ProvisioningError: " + self.value
1784 class InvalidNetbiosName(Exception):
1785 """A specified name was not a valid NetBIOS name."""
1786 def __init__(self, name):
1787 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)