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, ENC_ALL_TYPES
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 "RIDALLOCATIONSTART": str(next_rid + 100),
924 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
927 # This is partially Samba4 specific and should be replaced by the correct
929 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
930 "DNSDOMAIN": names.dnsdomain,
931 "DOMAINDN": names.domaindn,
932 "DNSPASS_B64": b64encode(dnspass),
935 def getpolicypath(sysvolpath, dnsdomain, guid):
938 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
941 def create_gpo_struct(policy_path):
942 os.makedirs(policy_path, 0755)
943 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
944 "[General]\r\nVersion=65543")
945 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
946 os.makedirs(os.path.join(policy_path, "USER"), 0755)
949 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
950 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
951 create_gpo_struct(policy_path)
953 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
954 create_gpo_struct(policy_path)
957 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
958 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
959 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
960 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
962 """Setup a complete SAM Database.
964 :note: This will wipe the main SAM database file!
968 # Provision does not make much sense values larger than 1000000000
969 # as the upper range of the rIDAvailablePool is 1073741823 and
970 # we don't want to create a domain that cannot allocate rids.
971 if next_rid < 1000 or next_rid > 1000000000:
972 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
973 error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
974 raise ProvisioningError(error)
976 # ATTENTION: Do NOT change these default values without discussion with the
977 # team and/or release manager. They have a big impact on the whole program!
978 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
980 if dom_for_fun_level is None:
981 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
983 if dom_for_fun_level > domainControllerFunctionality:
984 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!")
986 domainFunctionality = dom_for_fun_level
987 forestFunctionality = dom_for_fun_level
989 # Also wipes the database
990 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
991 provision_backend=provision_backend, session_info=session_info,
992 names=names, serverrole=serverrole, schema=schema)
995 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
997 # Load the database, but don's load the global schema and don't connect quite yet
998 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
999 credentials=provision_backend.credentials, lp=lp, global_schema=False,
1002 logger.info("Pre-loading the Samba 4 and AD schema")
1004 # Load the schema from the one we computed earlier
1005 samdb.set_schema(schema)
1007 # And now we can connect to the DB - the schema won't be loaded from the DB
1010 if fill == FILL_DRS:
1013 samdb.transaction_start()
1015 # Set the domain functionality levels onto the database.
1016 # Various module (the password_hash module in particular) need
1017 # to know what level of AD we are emulating.
1019 # These will be fixed into the database via the database
1020 # modifictions below, but we need them set from the start.
1021 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1022 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1023 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1025 samdb.set_domain_sid(str(domainsid))
1026 samdb.set_invocation_id(invocationid)
1027 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1029 logger.info("Adding DomainDN: %s" % names.domaindn)
1031 #impersonate domain admin
1032 admin_session_info = admin_session(lp, str(domainsid))
1033 samdb.set_session_info(admin_session_info)
1034 if domainguid is not None:
1035 domainguid_line = "objectGUID: %s\n-" % domainguid
1037 domainguid_line = ""
1039 descr = b64encode(get_domain_descriptor(domainsid))
1040 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1041 "DOMAINDN": names.domaindn,
1042 "DOMAINGUID": domainguid_line,
1047 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1048 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1049 "DOMAINSID": str(domainsid),
1050 "NEXTRID": str(next_rid),
1051 "SCHEMADN": names.schemadn,
1052 "NETBIOSNAME": names.netbiosname,
1053 "DEFAULTSITE": names.sitename,
1054 "CONFIGDN": names.configdn,
1055 "SERVERDN": names.serverdn,
1056 "POLICYGUID": policyguid,
1057 "DOMAINDN": names.domaindn,
1058 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1059 "SAMBA_VERSION_STRING": version
1062 logger.info("Adding configuration container")
1063 descr = b64encode(get_config_descriptor(domainsid))
1064 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1065 "CONFIGDN": names.configdn,
1066 "DESCRIPTOR": descr,
1069 # The LDIF here was created when the Schema object was constructed
1070 logger.info("Setting up sam.ldb schema")
1071 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1072 samdb.modify_ldif(schema.schema_dn_modify)
1073 samdb.write_prefixes_from_schema()
1074 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1075 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1076 {"SCHEMADN": names.schemadn})
1078 logger.info("Reopening sam.ldb with new schema")
1080 samdb.transaction_cancel()
1083 samdb.transaction_commit()
1085 samdb = SamDB(session_info=admin_session_info,
1086 credentials=provision_backend.credentials, lp=lp,
1087 global_schema=False, am_rodc=am_rodc)
1089 samdb.transaction_start()
1091 samdb.invocation_id = invocationid
1093 logger.info("Setting up sam.ldb configuration data")
1094 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1095 "CONFIGDN": names.configdn,
1096 "NETBIOSNAME": names.netbiosname,
1097 "DEFAULTSITE": names.sitename,
1098 "DNSDOMAIN": names.dnsdomain,
1099 "DOMAIN": names.domain,
1100 "SCHEMADN": names.schemadn,
1101 "DOMAINDN": names.domaindn,
1102 "SERVERDN": names.serverdn,
1103 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1104 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
1107 logger.info("Setting up display specifiers")
1108 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1109 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1110 check_all_substituted(display_specifiers_ldif)
1111 samdb.add_ldif(display_specifiers_ldif)
1113 logger.info("Adding users container")
1114 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1115 "DOMAINDN": names.domaindn})
1116 logger.info("Modifying users container")
1117 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1118 "DOMAINDN": names.domaindn})
1119 logger.info("Adding computers container")
1120 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1121 "DOMAINDN": names.domaindn})
1122 logger.info("Modifying computers container")
1123 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1124 "DOMAINDN": names.domaindn})
1125 logger.info("Setting up sam.ldb data")
1126 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1127 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1128 "DOMAINDN": names.domaindn,
1129 "NETBIOSNAME": names.netbiosname,
1130 "DEFAULTSITE": names.sitename,
1131 "CONFIGDN": names.configdn,
1132 "SERVERDN": names.serverdn,
1133 "RIDAVAILABLESTART": str(next_rid + 600),
1134 "POLICYGUID_DC": policyguid_dc
1137 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1138 "DOMAINDN": names.domaindn})
1140 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1141 "CONFIGDN": names.configdn,
1142 "SCHEMADN": names.schemadn})
1143 if fill == FILL_FULL:
1144 logger.info("Setting up sam.ldb users and groups")
1145 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1146 "DOMAINDN": names.domaindn,
1147 "DOMAINSID": str(domainsid),
1148 "CONFIGDN": names.configdn,
1149 "ADMINPASS_B64": b64encode(adminpass),
1150 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1153 logger.info("Setting up self join")
1154 setup_self_join(samdb, names=names, invocationid=invocationid,
1156 machinepass=machinepass,
1157 domainsid=domainsid,
1159 policyguid=policyguid,
1160 policyguid_dc=policyguid_dc,
1161 setup_path=setup_path,
1162 domainControllerFunctionality=domainControllerFunctionality,
1165 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=%s,CN=Sites,CN=Configuration,%s" % (names.hostname, names.sitename, names.domaindn)
1166 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1167 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1168 assert isinstance(names.ntdsguid, str)
1170 samdb.transaction_cancel()
1173 samdb.transaction_commit()
1178 FILL_NT4SYNC = "NT4SYNC"
1180 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1181 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)"
1183 def set_dir_acl(path, acl, lp, domsid):
1184 setntacl(lp, path, acl, domsid)
1185 for root, dirs, files in os.walk(path, topdown=False):
1187 setntacl(lp, os.path.join(root, name), acl, domsid)
1189 setntacl(lp, os.path.join(root, name), acl, domsid)
1192 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1194 policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1195 set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
1197 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1198 attrs=["cn", "nTSecurityDescriptor"],
1199 expression="", scope=ldb.SCOPE_ONELEVEL)
1201 acl = ndr_unpack(security.descriptor,
1202 str(policy["nTSecurityDescriptor"])).as_sddl()
1203 policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1204 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1207 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1210 os.chown(sysvol,-1,gid)
1216 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1217 for root, dirs, files in os.walk(sysvol, topdown=False):
1220 os.chown(os.path.join(root, name),-1,gid)
1221 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1224 os.chown(os.path.join(root, name),-1,gid)
1225 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1226 set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1229 def provision(setup_dir, logger, session_info,
1230 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1232 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1234 domain=None, hostname=None, hostip=None, hostip6=None,
1235 domainsid=None, next_rid=1000,
1236 adminpass=None, ldapadminpass=None,
1237 krbtgtpass=None, domainguid=None,
1238 policyguid=None, policyguid_dc=None, invocationid=None,
1239 machinepass=None, ntdsguid=None,
1240 dnspass=None, root=None, nobody=None, users=None,
1241 wheel=None, backup=None, aci=None, serverrole=None,
1242 dom_for_fun_level=None,
1243 ldap_backend_extra_port=None, backend_type=None,
1245 ol_mmr_urls=None, ol_olc=None,
1246 setup_ds_path=None, slapd_path=None, nosync=False,
1247 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1250 :note: caution, this wipes all existing data!
1253 def setup_path(file):
1254 return os.path.join(setup_dir, file)
1256 if domainsid is None:
1257 domainsid = security.random_sid()
1259 domainsid = security.dom_sid(domainsid)
1261 # create/adapt the group policy GUIDs
1262 if policyguid is None:
1263 policyguid = str(uuid.uuid4())
1264 policyguid = policyguid.upper()
1265 if policyguid_dc is None:
1266 policyguid_dc = str(uuid.uuid4())
1267 policyguid_dc = policyguid_dc.upper()
1269 if adminpass is None:
1270 adminpass = samba.generate_random_password(12, 32)
1271 if krbtgtpass is None:
1272 krbtgtpass = samba.generate_random_password(128, 255)
1273 if machinepass is None:
1274 machinepass = samba.generate_random_password(128, 255)
1276 dnspass = samba.generate_random_password(128, 255)
1277 if ldapadminpass is None:
1278 #Make a new, random password between Samba and it's LDAP server
1279 ldapadminpass=samba.generate_random_password(128, 255)
1281 if backend_type is None:
1282 backend_type = "ldb"
1284 sid_generator = "internal"
1285 if backend_type == "fedora-ds":
1286 sid_generator = "backend"
1288 root_uid = findnss_uid([root or "root"])
1289 nobody_uid = findnss_uid([nobody or "nobody"])
1290 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1292 wheel_gid = findnss_gid(["wheel", "adm"])
1294 wheel_gid = findnss_gid([wheel])
1296 bind_gid = findnss_gid(["bind", "named"])
1300 if targetdir is not None:
1301 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1302 elif smbconf is None:
1303 smbconf = samba.param.default_path()
1304 if not os.path.exists(os.path.dirname(smbconf)):
1305 os.makedirs(os.path.dirname(smbconf))
1307 # only install a new smb.conf if there isn't one there already
1308 if os.path.exists(smbconf):
1309 # if Samba Team members can't figure out the weird errors
1310 # loading an empty smb.conf gives, then we need to be smarter.
1311 # Pretend it just didn't exist --abartlet
1312 data = open(smbconf, 'r').read()
1313 data = data.lstrip()
1314 if data is None or data == "":
1315 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1316 serverrole, targetdir, sid_generator, useeadb)
1318 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1319 targetdir, sid_generator, useeadb)
1321 lp = samba.param.LoadParm()
1324 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1325 dnsdomain=realm, serverrole=serverrole,
1326 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1327 serverdn=serverdn, sitename=sitename)
1329 paths = provision_paths_from_lp(lp, names.dnsdomain)
1331 paths.bind_gid = bind_gid
1334 hostips = samba.interface_ips(lp, False)
1335 if len(hostips) == 0:
1336 logger.warning("No external IPv4 address has been found. Using loopback.")
1337 hostip = '127.0.0.1'
1340 if len(hostips) > 1:
1341 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1345 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1348 if hostip6 == '::1' and ip[-1][0] != '::1':
1350 except socket.gaierror, (socket.EAI_NODATA, msg):
1353 if serverrole is None:
1354 serverrole = lp.get("server role")
1356 assert serverrole in ("domain controller", "member server", "standalone")
1357 if invocationid is None:
1358 invocationid = str(uuid.uuid4())
1360 if not os.path.exists(paths.private_dir):
1361 os.mkdir(paths.private_dir)
1362 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1363 os.mkdir(os.path.join(paths.private_dir, "tls"))
1365 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1367 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1368 serverdn=names.serverdn)
1370 if backend_type == "ldb":
1371 provision_backend = LDBBackend(backend_type,
1372 paths=paths, setup_path=setup_path,
1373 lp=lp, credentials=credentials,
1376 elif backend_type == "existing":
1377 provision_backend = ExistingBackend(backend_type,
1378 paths=paths, setup_path=setup_path,
1379 lp=lp, credentials=credentials,
1382 ldapi_url=ldapi_url)
1383 elif backend_type == "fedora-ds":
1384 provision_backend = FDSBackend(backend_type,
1385 paths=paths, setup_path=setup_path,
1386 lp=lp, credentials=credentials,
1389 domainsid=domainsid,
1392 ldapadminpass=ldapadminpass,
1393 slapd_path=slapd_path,
1394 ldap_backend_extra_port=ldap_backend_extra_port,
1395 ldap_dryrun_mode=ldap_dryrun_mode,
1397 setup_ds_path=setup_ds_path)
1398 elif backend_type == "openldap":
1399 provision_backend = OpenLDAPBackend(backend_type,
1400 paths=paths, setup_path=setup_path,
1401 lp=lp, credentials=credentials,
1404 domainsid=domainsid,
1407 ldapadminpass=ldapadminpass,
1408 slapd_path=slapd_path,
1409 ldap_backend_extra_port=ldap_backend_extra_port,
1410 ldap_dryrun_mode=ldap_dryrun_mode,
1411 ol_mmr_urls=ol_mmr_urls,
1414 raise ValueError("Unknown LDAP backend type selected")
1416 provision_backend.init()
1417 provision_backend.start()
1419 # only install a new shares config db if there is none
1420 if not os.path.exists(paths.shareconf):
1421 logger.info("Setting up share.ldb")
1422 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1424 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1427 logger.info("Setting up secrets.ldb")
1428 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1429 session_info=session_info,
1430 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1433 logger.info("Setting up the registry")
1434 setup_registry(paths.hklm, setup_path, session_info,
1437 logger.info("Setting up the privileges database")
1438 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1440 logger.info("Setting up idmap db")
1441 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1444 logger.info("Setting up SAM db")
1445 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1446 provision_backend, lp, names,
1448 domainsid=domainsid,
1449 schema=schema, domainguid=domainguid,
1450 policyguid=policyguid, policyguid_dc=policyguid_dc,
1452 adminpass=adminpass, krbtgtpass=krbtgtpass,
1453 invocationid=invocationid,
1454 machinepass=machinepass, dnspass=dnspass,
1455 ntdsguid=ntdsguid, serverrole=serverrole,
1456 dom_for_fun_level=dom_for_fun_level,
1457 am_rodc=am_rodc, next_rid=next_rid)
1459 if serverrole == "domain controller":
1460 if paths.netlogon is None:
1461 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1462 logger.info("Please either remove %s or see the template at %s" %
1463 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1464 assert paths.netlogon is not None
1466 if paths.sysvol is None:
1467 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1468 " are configuring a DC.")
1469 logger.info("Please either remove %s or see the template at %s" %
1470 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1471 assert paths.sysvol is not None
1473 if not os.path.isdir(paths.netlogon):
1474 os.makedirs(paths.netlogon, 0755)
1476 if samdb_fill == FILL_FULL:
1477 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1478 root_uid=root_uid, nobody_uid=nobody_uid,
1479 users_gid=users_gid, wheel_gid=wheel_gid)
1481 if serverrole == "domain controller":
1482 # Set up group policies (domain policy and domain controller policy)
1483 setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1484 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1485 domainsid, names.dnsdomain, names.domaindn, lp)
1487 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1488 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1490 secretsdb_self_join(secrets_ldb, domain=names.domain,
1492 dnsdomain=names.dnsdomain,
1493 netbiosname=names.netbiosname,
1494 domainsid=domainsid,
1495 machinepass=machinepass,
1496 secure_channel_type=SEC_CHAN_BDC)
1498 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1499 # In future, this might be determined from some configuration
1500 kerberos_enctypes = str(ENC_ALL_TYPES)
1503 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1504 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes,
1505 flags=ldb.FLAG_MOD_REPLACE,
1506 name="msDS-SupportedEncryptionTypes")
1508 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1509 # It might be that this attribute does not exist in this schema
1513 if serverrole == "domain controller":
1514 secretsdb_setup_dns(secrets_ldb, setup_path,
1516 realm=names.realm, dnsdomain=names.dnsdomain,
1517 dns_keytab_path=paths.dns_keytab,
1520 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1521 assert isinstance(domainguid, str)
1523 # Only make a zone file on the first DC, it should be replicated
1524 # with DNS replication
1525 create_zone_file(lp, logger, paths, targetdir, setup_path,
1526 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1527 hostname=names.hostname, realm=names.realm,
1528 domainguid=domainguid, ntdsguid=names.ntdsguid)
1530 create_named_conf(paths, setup_path, realm=names.realm,
1531 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1533 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1534 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1535 keytab_name=paths.dns_keytab)
1536 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1537 logger.info("and %s for further documentation required for secure DNS "
1538 "updates", paths.namedtxt)
1540 create_krb5_conf(paths.krb5conf, setup_path,
1541 dnsdomain=names.dnsdomain, hostname=names.hostname,
1543 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1544 "generated at %s", paths.krb5conf)
1546 lastProvisionUSNs = get_last_provision_usn(samdb)
1547 maxUSN = get_max_usn(samdb, str(names.rootdn))
1548 if lastProvisionUSNs is not None:
1549 update_provision_usn(samdb, 0, maxUSN, 1)
1551 set_provision_usn(samdb, 0, maxUSN)
1553 if serverrole == "domain controller":
1554 create_dns_update_list(lp, logger, paths, setup_path)
1556 provision_backend.post_setup()
1557 provision_backend.shutdown()
1559 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1562 secrets_ldb.transaction_cancel()
1565 #Now commit the secrets.ldb to disk
1566 secrets_ldb.transaction_commit()
1568 # the commit creates the dns.keytab, now chown it
1569 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1570 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1572 os.chmod(dns_keytab_path, 0640)
1573 os.chown(dns_keytab_path, -1, paths.bind_gid)
1575 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1579 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1580 paths.phpldapadminconfig)
1582 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1583 logger.info("Server Role: %s" % serverrole)
1584 logger.info("Hostname: %s" % names.hostname)
1585 logger.info("NetBIOS Domain: %s" % names.domain)
1586 logger.info("DNS Domain: %s" % names.dnsdomain)
1587 logger.info("DOMAIN SID: %s" % str(domainsid))
1588 if samdb_fill == FILL_FULL:
1589 logger.info("Admin password: %s" % adminpass)
1590 if provision_backend.type is not "ldb":
1591 if provision_backend.credentials.get_bind_dn() is not None:
1592 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1594 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1596 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1598 if provision_backend.slapd_command_escaped is not None:
1599 # now display slapd_command_file.txt to show how slapd must be started next time
1600 logger.info("Use later the following commandline to start slapd, then Samba:")
1601 logger.info(provision_backend.slapd_command_escaped)
1602 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1603 provision_backend.ldapdir)
1605 result = ProvisionResult()
1606 result.domaindn = domaindn
1607 result.paths = paths
1609 result.samdb = samdb
1613 def provision_become_dc(setup_dir=None,
1614 smbconf=None, targetdir=None, realm=None,
1615 rootdn=None, domaindn=None, schemadn=None,
1616 configdn=None, serverdn=None,
1617 domain=None, hostname=None, domainsid=None,
1618 adminpass=None, krbtgtpass=None, domainguid=None,
1619 policyguid=None, policyguid_dc=None, invocationid=None,
1621 dnspass=None, root=None, nobody=None, users=None,
1622 wheel=None, backup=None, serverrole=None,
1623 ldap_backend=None, ldap_backend_type=None,
1624 sitename=None, debuglevel=1):
1626 logger = logging.getLogger("provision")
1627 samba.set_debug_level(debuglevel)
1629 return provision(setup_dir, logger, system_session(), None,
1630 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1631 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1632 configdn=configdn, serverdn=serverdn, domain=domain,
1633 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1634 machinepass=machinepass, serverrole="domain controller",
1638 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1639 """Create a PHP LDAP admin configuration file.
1641 :param path: Path to write the configuration to.
1642 :param setup_path: Function to generate setup paths.
1644 setup_file(setup_path("phpldapadmin-config.php"), path,
1645 {"S4_LDAPI_URI": ldapi_uri})
1648 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1649 hostip, hostip6, hostname, realm, domainguid,
1651 """Write out a DNS zone file, from the info in the current database.
1653 :param paths: paths object
1654 :param setup_path: Setup path function.
1655 :param dnsdomain: DNS Domain name
1656 :param domaindn: DN of the Domain
1657 :param hostip: Local IPv4 IP
1658 :param hostip6: Local IPv6 IP
1659 :param hostname: Local hostname
1660 :param realm: Realm name
1661 :param domainguid: GUID of the domain.
1662 :param ntdsguid: GUID of the hosts nTDSDSA record.
1664 assert isinstance(domainguid, str)
1666 if hostip6 is not None:
1667 hostip6_base_line = " IN AAAA " + hostip6
1668 hostip6_host_line = hostname + " IN AAAA " + hostip6
1669 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1671 hostip6_base_line = ""
1672 hostip6_host_line = ""
1673 gc_msdcs_ip6_line = ""
1675 if hostip is not None:
1676 hostip_base_line = " IN A " + hostip
1677 hostip_host_line = hostname + " IN A " + hostip
1678 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1680 hostip_base_line = ""
1681 hostip_host_line = ""
1682 gc_msdcs_ip_line = ""
1684 dns_dir = os.path.dirname(paths.dns)
1687 shutil.rmtree(dns_dir, True)
1691 os.mkdir(dns_dir, 0775)
1693 # we need to freeze the zone while we update the contents
1694 if targetdir is None:
1695 rndc = ' '.join(lp.get("rndc command"))
1696 os.system(rndc + " freeze " + lp.get("realm"))
1698 setup_file(setup_path("provision.zone"), paths.dns, {
1699 "HOSTNAME": hostname,
1700 "DNSDOMAIN": dnsdomain,
1702 "HOSTIP_BASE_LINE": hostip_base_line,
1703 "HOSTIP_HOST_LINE": hostip_host_line,
1704 "DOMAINGUID": domainguid,
1705 "DATESTRING": time.strftime("%Y%m%d%H"),
1706 "DEFAULTSITE": DEFAULTSITE,
1707 "NTDSGUID": ntdsguid,
1708 "HOSTIP6_BASE_LINE": hostip6_base_line,
1709 "HOSTIP6_HOST_LINE": hostip6_host_line,
1710 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1711 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1714 # note that we use no variable substitution on this file
1715 # the substitution is done at runtime by samba_dnsupdate
1716 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1718 # and the SPN update list
1719 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1721 if paths.bind_gid is not None:
1723 os.chown(dns_dir, -1, paths.bind_gid)
1724 os.chown(paths.dns, -1, paths.bind_gid)
1725 # chmod needed to cope with umask
1726 os.chmod(dns_dir, 0775)
1727 os.chmod(paths.dns, 0664)
1729 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1731 if targetdir is None:
1732 os.system(rndc + " unfreeze " + lp.get("realm"))
1735 def create_dns_update_list(lp, logger, paths, setup_path):
1736 """Write out a dns_update_list file"""
1737 # note that we use no variable substitution on this file
1738 # the substitution is done at runtime by samba_dnsupdate
1739 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1740 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1743 def create_named_conf(paths, setup_path, realm, dnsdomain,
1745 """Write out a file containing zone statements suitable for inclusion in a
1746 named.conf file (including GSS-TSIG configuration).
1748 :param paths: all paths
1749 :param setup_path: Setup path function.
1750 :param realm: Realm name
1751 :param dnsdomain: DNS Domain name
1752 :param private_dir: Path to private directory
1753 :param keytab_name: File name of DNS keytab file
1756 setup_file(setup_path("named.conf"), paths.namedconf, {
1757 "DNSDOMAIN": dnsdomain,
1759 "ZONE_FILE": paths.dns,
1760 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1761 "NAMED_CONF": paths.namedconf,
1762 "NAMED_CONF_UPDATE": paths.namedconf_update
1765 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1768 def create_named_txt(path, setup_path, realm, dnsdomain,
1769 private_dir, keytab_name):
1770 """Write out a file containing zone statements suitable for inclusion in a
1771 named.conf file (including GSS-TSIG configuration).
1773 :param path: Path of the new named.conf file.
1774 :param setup_path: Setup path function.
1775 :param realm: Realm name
1776 :param dnsdomain: DNS Domain name
1777 :param private_dir: Path to private directory
1778 :param keytab_name: File name of DNS keytab file
1781 setup_file(setup_path("named.txt"), path, {
1782 "DNSDOMAIN": dnsdomain,
1784 "DNS_KEYTAB": keytab_name,
1785 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1786 "PRIVATE_DIR": private_dir
1790 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1791 """Write out a file containing zone statements suitable for inclusion in a
1792 named.conf file (including GSS-TSIG configuration).
1794 :param path: Path of the new named.conf file.
1795 :param setup_path: Setup path function.
1796 :param dnsdomain: DNS Domain name
1797 :param hostname: Local hostname
1798 :param realm: Realm name
1800 setup_file(setup_path("krb5.conf"), path, {
1801 "DNSDOMAIN": dnsdomain,
1802 "HOSTNAME": hostname,
1807 class ProvisioningError(Exception):
1808 """A generic provision error."""
1810 def __init__(self, value):
1814 return "ProvisioningError: " + self.value
1817 class InvalidNetbiosName(Exception):
1818 """A specified name was not a valid NetBIOS name."""
1819 def __init__(self, name):
1820 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)