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_DC_FUNCTION_2008
47 from samba.dcerpc import security
48 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
49 from samba.idmap import IDmapDB
50 from samba.ntacls import setntacl, dsacl2fsacl
51 from samba.ndr import ndr_pack,ndr_unpack
52 from samba.schema import Schema
53 from samba.samdb import SamDB
54 from ms_display_specifiers import read_ms_ldif
55 from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
57 __docformat__ = "restructuredText"
60 """Find the setup directory used by provision."""
61 dirname = os.path.dirname(__file__)
62 if "/site-packages/" in dirname:
63 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
64 for suffix in ["share/setup", "share/samba/setup", "setup"]:
65 ret = os.path.join(prefix, suffix)
66 if os.path.isdir(ret):
69 ret = os.path.join(dirname, "../../../setup")
70 if os.path.isdir(ret):
72 raise Exception("Unable to find setup directory.")
74 # descriptors of the naming contexts
75 # hard coded at this point, but will probably be changed when
76 # we enable different fsmo roles
78 def get_config_descriptor(domain_sid):
79 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
81 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
82 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
84 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
85 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
86 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
87 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
88 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
89 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
90 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
91 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
92 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
93 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
94 sec = security.descriptor.from_sddl(sddl, domain_sid)
97 def get_domain_descriptor(domain_sid):
98 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
99 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
100 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
101 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
102 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
103 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
104 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
105 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
106 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
107 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
108 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
109 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
110 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
111 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
112 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
113 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
114 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
119 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
120 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
121 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
122 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
125 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
126 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
127 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
128 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
129 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
133 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
134 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
135 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
136 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
139 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
141 "(A;;RPLCLORC;;;ED)" \
142 "(A;;RPLCLORC;;;AU)" \
143 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
144 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
145 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
146 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
147 sec = security.descriptor.from_sddl(sddl, domain_sid)
150 DEFAULTSITE = "Default-First-Site-Name"
152 class ProvisionPaths(object):
155 self.shareconf = None
166 self.dns_keytab = None
169 self.private_dir = None
172 class ProvisionNames(object):
179 self.ldapmanagerdn = None
180 self.dnsdomain = None
182 self.netbiosname = None
189 class ProvisionResult(object):
198 def check_install(lp, session_info, credentials):
199 """Check whether the current install seems ok.
201 :param lp: Loadparm context
202 :param session_info: Session information
203 :param credentials: Credentials
205 if lp.get("realm") == "":
206 raise Exception("Realm empty")
207 samdb = Ldb(lp.get("sam database"), session_info=session_info,
208 credentials=credentials, lp=lp)
209 if len(samdb.search("(cn=Administrator)")) != 1:
210 raise ProvisioningError("No administrator account found")
213 def findnss(nssfn, names):
214 """Find a user or group from a list of possibilities.
216 :param nssfn: NSS Function to try (should raise KeyError if not found)
217 :param names: Names to check.
218 :return: Value return by first names list.
225 raise KeyError("Unable to find user/group in %r" % names)
228 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
229 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
232 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
233 """Setup a ldb in the private dir.
235 :param ldb: LDB file to import data into
236 :param ldif_path: Path of the LDIF file to load
237 :param subst_vars: Optional variables to subsitute in LDIF.
238 :param nocontrols: Optional list of controls, can be None for no controls
240 assert isinstance(ldif_path, str)
241 data = read_and_sub_file(ldif_path, subst_vars)
242 ldb.add_ldif(data, controls)
245 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
246 """Modify a ldb in the private dir.
248 :param ldb: LDB object.
249 :param ldif_path: LDIF file path.
250 :param subst_vars: Optional dictionary with substitution variables.
252 data = read_and_sub_file(ldif_path, subst_vars)
253 ldb.modify_ldif(data)
256 def setup_ldb(ldb, ldif_path, subst_vars):
257 """Import a LDIF a file into a LDB handle, optionally substituting variables.
259 :note: Either all LDIF data will be added or none (using transactions).
261 :param ldb: LDB file to import into.
262 :param ldif_path: Path to the LDIF file.
263 :param subst_vars: Dictionary with substitution variables.
265 assert ldb is not None
266 ldb.transaction_start()
268 setup_add_ldif(ldb, ldif_path, subst_vars)
270 ldb.transaction_cancel()
273 ldb.transaction_commit()
276 def provision_paths_from_lp(lp, dnsdomain):
277 """Set the default paths for provisioning.
279 :param lp: Loadparm context.
280 :param dnsdomain: DNS Domain name
282 paths = ProvisionPaths()
283 paths.private_dir = lp.get("private dir")
285 # This is stored without path prefix for the "privateKeytab" attribute in
286 # "secrets_dns.ldif".
287 paths.dns_keytab = "dns.keytab"
289 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
290 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
291 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
292 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
293 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
294 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
295 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
296 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
297 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
298 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
299 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
300 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
301 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
302 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
303 paths.phpldapadminconfig = os.path.join(paths.private_dir,
304 "phpldapadmin-config.php")
305 paths.hklm = "hklm.ldb"
306 paths.hkcr = "hkcr.ldb"
307 paths.hkcu = "hkcu.ldb"
308 paths.hku = "hku.ldb"
309 paths.hkpd = "hkpd.ldb"
310 paths.hkpt = "hkpt.ldb"
311 paths.sysvol = lp.get("path", "sysvol")
312 paths.netlogon = lp.get("path", "netlogon")
313 paths.smbconf = lp.configfile
317 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
318 serverrole=None, rootdn=None, domaindn=None, configdn=None,
319 schemadn=None, serverdn=None, sitename=None):
320 """Guess configuration settings to use."""
323 hostname = socket.gethostname().split(".")[0]
325 netbiosname = lp.get("netbios name")
326 if netbiosname is None:
327 netbiosname = hostname
328 assert netbiosname is not None
329 netbiosname = netbiosname.upper()
330 if not valid_netbios_name(netbiosname):
331 raise InvalidNetbiosName(netbiosname)
333 if dnsdomain is None:
334 dnsdomain = lp.get("realm")
335 if dnsdomain is None or dnsdomain == "":
336 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
338 dnsdomain = dnsdomain.lower()
340 if serverrole is None:
341 serverrole = lp.get("server role")
342 if serverrole is None:
343 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
345 serverrole = serverrole.lower()
347 realm = dnsdomain.upper()
349 if lp.get("realm") == "":
350 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
352 if lp.get("realm").upper() != realm:
353 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))
355 if lp.get("server role").lower() != serverrole:
356 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))
358 if serverrole == "domain controller":
360 # This will, for better or worse, default to 'WORKGROUP'
361 domain = lp.get("workgroup")
362 domain = domain.upper()
364 if lp.get("workgroup").upper() != domain:
365 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))
368 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
372 domaindn = "DC=" + netbiosname
374 if not valid_netbios_name(domain):
375 raise InvalidNetbiosName(domain)
377 if hostname.upper() == realm:
378 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
379 if netbiosname == realm:
380 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
382 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
388 configdn = "CN=Configuration," + rootdn
390 schemadn = "CN=Schema," + configdn
395 names = ProvisionNames()
396 names.rootdn = rootdn
397 names.domaindn = domaindn
398 names.configdn = configdn
399 names.schemadn = schemadn
400 names.ldapmanagerdn = "CN=Manager," + rootdn
401 names.dnsdomain = dnsdomain
402 names.domain = domain
404 names.netbiosname = netbiosname
405 names.hostname = hostname
406 names.sitename = sitename
407 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
412 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
413 targetdir, sid_generator,eadb):
414 """Create a new smb.conf file based on a couple of basic settings.
416 assert smbconf is not None
418 hostname = socket.gethostname().split(".")[0]
419 netbiosname = hostname.upper()
421 if serverrole is None:
422 serverrole = "standalone"
424 assert serverrole in ("domain controller", "member server", "standalone")
425 if serverrole == "domain controller":
427 elif serverrole == "member server":
428 smbconfsuffix = "member"
429 elif serverrole == "standalone":
430 smbconfsuffix = "standalone"
432 if sid_generator is None:
433 sid_generator = "internal"
435 assert domain is not None
436 domain = domain.upper()
438 assert realm is not None
439 realm = realm.upper()
441 default_lp = param.LoadParm()
442 #Load non-existant file
443 if os.path.exists(smbconf):
444 default_lp.load(smbconf)
446 if targetdir is not None:
447 privdir = os.path.join(targetdir, "private")
449 privdir = default_lp.get("private dir")
450 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
454 if targetdir is not None:
455 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
456 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
458 default_lp.set("lock dir", os.path.abspath(targetdir))
463 if sid_generator == "internal":
464 sid_generator_line = ""
466 sid_generator_line = "sid generator = " + sid_generator
468 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
469 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
471 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
473 "NETBIOS_NAME": netbiosname,
476 "SERVERROLE": serverrole,
477 "NETLOGONPATH": netlogon,
478 "SYSVOLPATH": sysvol,
479 "SIDGENERATOR_LINE": sid_generator_line,
480 "PRIVATEDIR_LINE": privatedir_line,
481 "LOCKDIR_LINE": lockdir_line,
482 "POSIXEADB_LINE": posixeadb_line
486 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
487 users_gid, wheel_gid):
488 """setup reasonable name mappings for sam names to unix names.
490 :param samdb: SamDB object.
491 :param idmap: IDmap db object.
492 :param sid: The domain sid.
493 :param domaindn: The domain DN.
494 :param root_uid: uid of the UNIX root user.
495 :param nobody_uid: uid of the UNIX nobody user.
496 :param users_gid: gid of the UNIX users group.
497 :param wheel_gid: gid of the UNIX wheel group."""
498 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
499 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
501 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
502 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
505 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
506 provision_backend, names, schema,
509 """Setup the partitions for the SAM database.
511 Alternatively, provision() may call this, and then populate the database.
513 :note: This will wipe the Sam Database!
515 :note: This function always removes the local SAM LDB file. The erase
516 parameter controls whether to erase the existing data, which
517 may not be stored locally but in LDAP.
520 assert session_info is not None
522 # We use options=["modules:"] to stop the modules loading - we
523 # just want to wipe and re-initialise the database, not start it up
526 os.unlink(samdb_path)
530 samdb = Ldb(url=samdb_path, session_info=session_info,
531 lp=lp, options=["modules:"])
533 ldap_backend_line = "# No LDAP backend"
534 if provision_backend.type is not "ldb":
535 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
537 samdb.transaction_start()
539 message("Setting up sam.ldb partitions and settings")
540 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
541 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
542 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
543 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
544 "LDAP_BACKEND_LINE": ldap_backend_line,
548 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
549 "BACKEND_TYPE": provision_backend.type,
550 "SERVER_ROLE": serverrole
553 message("Setting up sam.ldb rootDSE")
554 setup_samdb_rootdse(samdb, setup_path, names)
556 samdb.transaction_cancel()
559 samdb.transaction_commit()
562 def secretsdb_self_join(secretsdb, domain,
563 netbiosname, machinepass, domainsid=None,
564 realm=None, dnsdomain=None,
566 key_version_number=1,
567 secure_channel_type=SEC_CHAN_WKSTA):
568 """Add domain join-specific bits to a secrets database.
570 :param secretsdb: Ldb Handle to the secrets database
571 :param machinepass: Machine password
573 attrs=["whenChanged",
581 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
582 msg["secureChannelType"] = str(secure_channel_type)
583 msg["flatname"] = [domain]
584 msg["objectClass"] = ["top", "primaryDomain"]
585 if realm is not None:
586 if dnsdomain is None:
587 dnsdomain = realm.lower()
588 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
590 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
591 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
592 msg["privateKeytab"] = ["secrets.keytab"]
595 msg["secret"] = [machinepass]
596 msg["samAccountName"] = ["%s$" % netbiosname]
597 msg["secureChannelType"] = [str(secure_channel_type)]
598 if domainsid is not None:
599 msg["objectSid"] = [ndr_pack(domainsid)]
601 res = secretsdb.search(base="cn=Primary Domains",
603 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
604 scope=ldb.SCOPE_ONELEVEL)
607 if del_msg.dn is not msg.dn:
608 secretsdb.delete(del_msg.dn)
610 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
613 msg["priorSecret"] = res[0]["secret"]
614 msg["priorWhenChanged"] = res[0]["whenChanged"]
616 if res["privateKeytab"] is not None:
617 msg["privateKeytab"] = res[0]["privateKeytab"]
619 if res["krb5Keytab"] is not None:
620 msg["krb5Keytab"] = res[0]["krb5Keytab"]
623 el.set_flags(ldb.FLAG_MOD_REPLACE)
624 secretsdb.modify(msg)
629 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
631 dns_keytab_path, dnspass):
632 """Add DNS specific bits to a secrets database.
634 :param secretsdb: Ldb Handle to the secrets database
635 :param setup_path: Setup path function
636 :param machinepass: Machine password
639 os.unlink(os.path.join(private_dir, dns_keytab_path))
643 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
645 "DNSDOMAIN": dnsdomain,
646 "DNS_KEYTAB": dns_keytab_path,
647 "DNSPASS_B64": b64encode(dnspass),
651 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
652 """Setup the secrets database.
654 :param path: Path to the secrets database.
655 :param setup_path: Get the path to a setup file.
656 :param session_info: Session info.
657 :param credentials: Credentials
658 :param lp: Loadparm context
659 :return: LDB handle for the created secrets database
661 if os.path.exists(path):
663 secrets_ldb = Ldb(path, session_info=session_info,
666 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
667 secrets_ldb = Ldb(path, session_info=session_info,
669 secrets_ldb.transaction_start()
670 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
672 if backend_credentials is not None and backend_credentials.authentication_requested():
673 if backend_credentials.get_bind_dn() is not None:
674 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
675 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
676 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
679 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
680 "LDAPADMINUSER": backend_credentials.get_username(),
681 "LDAPADMINREALM": backend_credentials.get_realm(),
682 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
687 def setup_privileges(path, setup_path, session_info, lp):
688 """Setup the privileges database.
690 :param path: Path to the privileges database.
691 :param setup_path: Get the path to a setup file.
692 :param session_info: Session info.
693 :param credentials: Credentials
694 :param lp: Loadparm context
695 :return: LDB handle for the created secrets database
697 if os.path.exists(path):
699 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
700 privilege_ldb.erase()
701 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
704 def setup_registry(path, setup_path, session_info, lp):
705 """Setup the registry.
707 :param path: Path to the registry database
708 :param setup_path: Function that returns the path to a setup.
709 :param session_info: Session information
710 :param credentials: Credentials
711 :param lp: Loadparm context
713 reg = registry.Registry()
714 hive = registry.open_ldb(path, session_info=session_info,
716 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
717 provision_reg = setup_path("provision.reg")
718 assert os.path.exists(provision_reg)
719 reg.diff_apply(provision_reg)
722 def setup_idmapdb(path, setup_path, session_info, lp):
723 """Setup the idmap database.
725 :param path: path to the idmap database
726 :param setup_path: Function that returns a path to a setup file
727 :param session_info: Session information
728 :param credentials: Credentials
729 :param lp: Loadparm context
731 if os.path.exists(path):
734 idmap_ldb = IDmapDB(path, session_info=session_info,
738 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
742 def setup_samdb_rootdse(samdb, setup_path, names):
743 """Setup the SamDB rootdse.
745 :param samdb: Sam Database handle
746 :param setup_path: Obtain setup path
748 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
749 "SCHEMADN": names.schemadn,
750 "NETBIOSNAME": names.netbiosname,
751 "DNSDOMAIN": names.dnsdomain,
752 "REALM": names.realm,
753 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
754 "DOMAINDN": names.domaindn,
755 "ROOTDN": names.rootdn,
756 "CONFIGDN": names.configdn,
757 "SERVERDN": names.serverdn,
761 def setup_self_join(samdb, names,
762 machinepass, dnspass,
763 domainsid, invocationid, setup_path,
764 policyguid, policyguid_dc, domainControllerFunctionality,
766 """Join a host to its own domain."""
767 assert isinstance(invocationid, str)
768 if ntdsguid is not None:
769 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
772 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
773 "CONFIGDN": names.configdn,
774 "SCHEMADN": names.schemadn,
775 "DOMAINDN": names.domaindn,
776 "SERVERDN": names.serverdn,
777 "INVOCATIONID": invocationid,
778 "NETBIOSNAME": names.netbiosname,
779 "DEFAULTSITE": names.sitename,
780 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
781 "MACHINEPASS_B64": b64encode(machinepass),
782 "REALM": names.realm,
783 "DOMAIN": names.domain,
784 "DOMAINSID": str(domainsid),
785 "DNSDOMAIN": names.dnsdomain,
786 "SAMBA_VERSION_STRING": version,
787 "NTDSGUID": ntdsguid_line,
788 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
790 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
791 "POLICYGUID": policyguid,
792 "POLICYGUID_DC": policyguid_dc,
793 "DNSDOMAIN": names.dnsdomain,
794 "DOMAINSID": str(domainsid),
795 "DOMAINDN": names.domaindn})
797 # add the NTDSGUID based SPNs
798 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
799 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
800 expression="", scope=ldb.SCOPE_BASE)
801 assert isinstance(names.ntdsguid, str)
803 # Setup fSMORoleOwner entries to point at the newly created DC entry
804 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
805 "DOMAIN": names.domain,
806 "DNSDOMAIN": names.dnsdomain,
807 "DOMAINDN": names.domaindn,
808 "CONFIGDN": names.configdn,
809 "SCHEMADN": names.schemadn,
810 "DEFAULTSITE": names.sitename,
811 "SERVERDN": names.serverdn,
812 "NETBIOSNAME": names.netbiosname,
813 "NTDSGUID": names.ntdsguid,
814 "DNSPASS_B64": b64encode(dnspass),
817 def getpolicypath(sysvolpath, dnsdomain, guid):
820 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
823 def create_gpo_struct(policy_path):
824 os.makedirs(policy_path, 0755)
825 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
826 "[General]\r\nVersion=65543")
827 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
828 os.makedirs(os.path.join(policy_path, "USER"), 0755)
831 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
832 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
833 create_gpo_struct(policy_path)
835 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
836 create_gpo_struct(policy_path)
839 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
841 domainsid, domainguid, policyguid, policyguid_dc,
842 fill, adminpass, krbtgtpass,
843 machinepass, invocationid, dnspass, ntdsguid,
844 serverrole, dom_for_fun_level=None,
846 """Setup a complete SAM Database.
848 :note: This will wipe the main SAM database file!
851 # ATTENTION: Do NOT change these default values without discussion with the
852 # team and/or release manager. They have a big impact on the whole program!
853 domainControllerFunctionality = DS_DC_FUNCTION_2008
855 if dom_for_fun_level is None:
856 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
857 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
858 message("You want to run SAMBA 4 on a domain and forest function level"
859 " lower than Windows 2003 (Native). This is not recommended")
861 if dom_for_fun_level > domainControllerFunctionality:
862 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008). This won't work!")
864 domainFunctionality = dom_for_fun_level
865 forestFunctionality = dom_for_fun_level
867 # Also wipes the database
868 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
869 provision_backend=provision_backend, session_info=session_info,
870 names=names, serverrole=serverrole, schema=schema)
873 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
875 # Load the database, but don's load the global schema and don't connect quite yet
876 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
877 credentials=provision_backend.credentials, lp=lp, global_schema=False)
879 message("Pre-loading the Samba 4 and AD schema")
881 # Load the schema from the one we computed earlier
882 samdb.set_schema_from_ldb(schema.ldb)
884 # And now we can connect to the DB - the schema won't be loaded from the DB
890 samdb.transaction_start()
892 # Set the domain functionality levels onto the database.
893 # Various module (the password_hash module in particular) need
894 # to know what level of AD we are emulating.
896 # These will be fixed into the database via the database
897 # modifictions below, but we need them set from the start.
898 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
899 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
900 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
902 samdb.set_domain_sid(str(domainsid))
903 samdb.set_invocation_id(invocationid)
904 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
906 message("Adding DomainDN: %s" % names.domaindn)
908 #impersonate domain admin
909 admin_session_info = admin_session(lp, str(domainsid))
910 samdb.set_session_info(admin_session_info)
911 if domainguid is not None:
912 domainguid_line = "objectGUID: %s\n-" % domainguid
916 descr = b64encode(get_domain_descriptor(domainsid))
917 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
918 "DOMAINDN": names.domaindn,
919 "DOMAINGUID": domainguid_line,
924 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
925 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
926 "DOMAINSID": str(domainsid),
927 "SCHEMADN": names.schemadn,
928 "NETBIOSNAME": names.netbiosname,
929 "DEFAULTSITE": names.sitename,
930 "CONFIGDN": names.configdn,
931 "SERVERDN": names.serverdn,
932 "POLICYGUID": policyguid,
933 "DOMAINDN": names.domaindn,
934 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
935 "SAMBA_VERSION_STRING": version
938 message("Adding configuration container")
939 descr = b64encode(get_config_descriptor(domainsid))
940 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
941 "CONFIGDN": names.configdn,
945 # The LDIF here was created when the Schema object was constructed
946 message("Setting up sam.ldb schema")
947 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
948 samdb.modify_ldif(schema.schema_dn_modify)
949 # set schemaInfo to defalt value for a new Forest
950 samdb.set_schema_info()
951 samdb.write_prefixes_from_schema()
952 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
953 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
954 {"SCHEMADN": names.schemadn})
956 message("Reopening sam.ldb with new schema")
958 samdb.transaction_cancel()
961 samdb.transaction_commit()
963 samdb = SamDB(session_info=admin_session_info,
964 credentials=provision_backend.credentials, lp=lp,
967 samdb.transaction_start()
969 samdb.invocation_id = invocationid
971 message("Setting up sam.ldb configuration data")
972 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
973 "CONFIGDN": names.configdn,
974 "NETBIOSNAME": names.netbiosname,
975 "DEFAULTSITE": names.sitename,
976 "DNSDOMAIN": names.dnsdomain,
977 "DOMAIN": names.domain,
978 "SCHEMADN": names.schemadn,
979 "DOMAINDN": names.domaindn,
980 "SERVERDN": names.serverdn,
981 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
984 message("Setting up display specifiers")
985 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
986 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
987 check_all_substituted(display_specifiers_ldif)
988 samdb.add_ldif(display_specifiers_ldif)
990 message("Adding users container")
991 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
992 "DOMAINDN": names.domaindn})
993 message("Modifying users container")
994 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
995 "DOMAINDN": names.domaindn})
996 message("Adding computers container")
997 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
998 "DOMAINDN": names.domaindn})
999 message("Modifying computers container")
1000 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1001 "DOMAINDN": names.domaindn})
1002 message("Setting up sam.ldb data")
1003 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1004 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1005 "DOMAINDN": names.domaindn,
1006 "NETBIOSNAME": names.netbiosname,
1007 "DEFAULTSITE": names.sitename,
1008 "CONFIGDN": names.configdn,
1009 "SERVERDN": names.serverdn,
1010 "POLICYGUID_DC": policyguid_dc
1013 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1014 "DOMAINDN": names.domaindn})
1016 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1017 "CONFIGDN": names.configdn,
1018 "SCHEMADN": names.schemadn})
1019 if fill == FILL_FULL:
1020 message("Setting up sam.ldb users and groups")
1021 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1022 "DOMAINDN": names.domaindn,
1023 "DOMAINSID": str(domainsid),
1024 "CONFIGDN": names.configdn,
1025 "ADMINPASS_B64": b64encode(adminpass),
1026 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1029 message("Setting up self join")
1030 setup_self_join(samdb, names=names, invocationid=invocationid,
1032 machinepass=machinepass,
1033 domainsid=domainsid, policyguid=policyguid,
1034 policyguid_dc=policyguid_dc,
1035 setup_path=setup_path,
1036 domainControllerFunctionality=domainControllerFunctionality,
1039 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1040 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1041 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1042 assert isinstance(names.ntdsguid, str)
1044 samdb.transaction_cancel()
1047 samdb.transaction_commit()
1052 FILL_NT4SYNC = "NT4SYNC"
1054 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1055 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)"
1057 def set_dir_acl(path, acl, lp, domsid):
1058 setntacl(lp, path, acl, domsid)
1059 for root, dirs, files in os.walk(path, topdown=False):
1061 setntacl(lp, os.path.join(root, name), acl, domsid)
1063 setntacl(lp, os.path.join(root, name), acl, domsid)
1066 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1068 policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1069 set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
1071 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1072 attrs=["cn","nTSecurityDescriptor"],
1073 expression="", scope=ldb.SCOPE_ONELEVEL)
1075 acl = ndr_unpack(security.descriptor,
1076 str(policy["nTSecurityDescriptor"])).as_sddl()
1077 policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1078 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1081 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1084 os.chown(sysvol,-1,gid)
1090 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1091 for root, dirs, files in os.walk(sysvol, topdown=False):
1094 os.chown(os.path.join(root, name),-1,gid)
1095 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1098 os.chown(os.path.join(root, name),-1,gid)
1099 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1100 set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1103 def provision(setup_dir, message, session_info,
1104 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1106 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1108 domain=None, hostname=None, hostip=None, hostip6=None,
1109 domainsid=None, adminpass=None, ldapadminpass=None,
1110 krbtgtpass=None, domainguid=None,
1111 policyguid=None, policyguid_dc=None, invocationid=None,
1112 machinepass=None, ntdsguid=None,
1113 dnspass=None, root=None, nobody=None, users=None,
1114 wheel=None, backup=None, aci=None, serverrole=None,
1115 dom_for_fun_level=None,
1116 ldap_backend_extra_port=None, backend_type=None,
1118 ol_mmr_urls=None, ol_olc=None,
1119 setup_ds_path=None, slapd_path=None, nosync=False,
1120 ldap_dryrun_mode=False,useeadb=False):
1123 :note: caution, this wipes all existing data!
1126 def setup_path(file):
1127 return os.path.join(setup_dir, file)
1129 if domainsid is None:
1130 domainsid = security.random_sid()
1132 domainsid = security.dom_sid(domainsid)
1134 # create/adapt the group policy GUIDs
1135 if policyguid is None:
1136 policyguid = str(uuid.uuid4())
1137 policyguid = policyguid.upper()
1138 if policyguid_dc is None:
1139 policyguid_dc = str(uuid.uuid4())
1140 policyguid_dc = policyguid_dc.upper()
1142 if adminpass is None:
1143 adminpass = samba.generate_random_password(12, 32)
1144 if krbtgtpass is None:
1145 krbtgtpass = samba.generate_random_password(128, 255)
1146 if machinepass is None:
1147 machinepass = samba.generate_random_password(128, 255)
1149 dnspass = samba.generate_random_password(128, 255)
1150 if ldapadminpass is None:
1151 #Make a new, random password between Samba and it's LDAP server
1152 ldapadminpass=samba.generate_random_password(128, 255)
1154 if backend_type is None:
1155 backend_type = "ldb"
1157 sid_generator = "internal"
1158 if backend_type == "fedora-ds":
1159 sid_generator = "backend"
1161 root_uid = findnss_uid([root or "root"])
1162 nobody_uid = findnss_uid([nobody or "nobody"])
1163 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1165 wheel_gid = findnss_gid(["wheel", "adm"])
1167 wheel_gid = findnss_gid([wheel])
1169 bind_gid = findnss_gid(["bind", "named"])
1173 if targetdir is not None:
1174 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1175 elif smbconf is None:
1176 smbconf = param.default_path()
1177 if not os.path.exists(os.path.dirname(smbconf)):
1178 os.makedirs(os.path.dirname(smbconf))
1180 # only install a new smb.conf if there isn't one there already
1181 if os.path.exists(smbconf):
1182 # if Samba Team members can't figure out the weird errors
1183 # loading an empty smb.conf gives, then we need to be smarter.
1184 # Pretend it just didn't exist --abartlet
1185 data = open(smbconf, 'r').read()
1186 data = data.lstrip()
1187 if data is None or data == "":
1188 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1189 serverrole, targetdir, sid_generator, useeadb)
1191 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1192 targetdir, sid_generator, useeadb)
1194 lp = param.LoadParm()
1197 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1198 dnsdomain=realm, serverrole=serverrole,
1199 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1200 serverdn=serverdn, sitename=sitename)
1202 paths = provision_paths_from_lp(lp, names.dnsdomain)
1204 paths.bind_gid = bind_gid
1207 hostips = samba.interface_ips(lp, False)
1208 if len(hostips) == 0:
1209 message("No external IPv4 address has been found: I use the loopback.")
1210 hostip = '127.0.0.1'
1213 if len(hostips) > 1:
1214 message("More than one IPv4 address found: I use " + hostip + ".")
1218 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1221 if hostip6 == '::1' and ip[-1][0] != '::1':
1223 except socket.gaierror, (socket.EAI_NODATA, msg):
1226 if serverrole is None:
1227 serverrole = lp.get("server role")
1229 assert serverrole in ("domain controller", "member server", "standalone")
1230 if invocationid is None:
1231 invocationid = str(uuid.uuid4())
1233 if not os.path.exists(paths.private_dir):
1234 os.mkdir(paths.private_dir)
1235 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1236 os.mkdir(os.path.join(paths.private_dir,"tls"))
1238 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1240 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1241 serverdn=names.serverdn)
1243 if backend_type == "ldb":
1244 provision_backend = LDBBackend(backend_type,
1245 paths=paths, setup_path=setup_path,
1246 lp=lp, credentials=credentials,
1249 elif backend_type == "existing":
1250 provision_backend = ExistingBackend(backend_type,
1251 paths=paths, setup_path=setup_path,
1252 lp=lp, credentials=credentials,
1255 ldapi_url=ldapi_url)
1256 elif backend_type == "fedora-ds":
1257 provision_backend = FDSBackend(backend_type,
1258 paths=paths, setup_path=setup_path,
1259 lp=lp, credentials=credentials,
1262 domainsid=domainsid,
1265 ldapadminpass=ldapadminpass,
1266 slapd_path=slapd_path,
1267 ldap_backend_extra_port=ldap_backend_extra_port,
1268 ldap_dryrun_mode=ldap_dryrun_mode,
1270 setup_ds_path=setup_ds_path)
1271 elif backend_type == "openldap":
1272 provision_backend = OpenLDAPBackend(backend_type,
1273 paths=paths, setup_path=setup_path,
1274 lp=lp, credentials=credentials,
1277 domainsid=domainsid,
1280 ldapadminpass=ldapadminpass,
1281 slapd_path=slapd_path,
1282 ldap_backend_extra_port=ldap_backend_extra_port,
1283 ldap_dryrun_mode=ldap_dryrun_mode,
1284 ol_mmr_urls=ol_mmr_urls,
1287 raise ValueError("Unknown LDAP backend type selected")
1289 provision_backend.init()
1290 provision_backend.start()
1292 # only install a new shares config db if there is none
1293 if not os.path.exists(paths.shareconf):
1294 message("Setting up share.ldb")
1295 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1297 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1300 message("Setting up secrets.ldb")
1301 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1302 session_info=session_info,
1303 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1305 message("Setting up the registry")
1306 setup_registry(paths.hklm, setup_path, session_info,
1309 message("Setting up the privileges database")
1310 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1312 message("Setting up idmap db")
1313 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1316 message("Setting up SAM db")
1317 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1318 provision_backend, lp, names,
1320 domainsid=domainsid,
1321 schema=schema, domainguid=domainguid,
1322 policyguid=policyguid, policyguid_dc=policyguid_dc,
1324 adminpass=adminpass, krbtgtpass=krbtgtpass,
1325 invocationid=invocationid,
1326 machinepass=machinepass, dnspass=dnspass,
1327 ntdsguid=ntdsguid, serverrole=serverrole,
1328 dom_for_fun_level=dom_for_fun_level)
1330 if serverrole == "domain controller":
1331 if paths.netlogon is None:
1332 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1333 message("Please either remove %s or see the template at %s" %
1334 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1335 assert paths.netlogon is not None
1337 if paths.sysvol is None:
1338 message("Existing smb.conf does not have a [sysvol] share, but you"
1339 " are configuring a DC.")
1340 message("Please either remove %s or see the template at %s" %
1341 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1342 assert paths.sysvol is not None
1344 if not os.path.isdir(paths.netlogon):
1345 os.makedirs(paths.netlogon, 0755)
1347 if samdb_fill == FILL_FULL:
1348 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1349 root_uid=root_uid, nobody_uid=nobody_uid,
1350 users_gid=users_gid, wheel_gid=wheel_gid)
1352 if serverrole == "domain controller":
1353 # Set up group policies (domain policy and domain controller policy)
1354 setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1355 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1356 domainsid, names.dnsdomain, names.domaindn, lp)
1358 message("Setting up sam.ldb rootDSE marking as synchronized")
1359 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1361 secretsdb_self_join(secrets_ldb, domain=names.domain,
1363 dnsdomain=names.dnsdomain,
1364 netbiosname=names.netbiosname,
1365 domainsid=domainsid,
1366 machinepass=machinepass,
1367 secure_channel_type=SEC_CHAN_BDC)
1369 if serverrole == "domain controller":
1370 secretsdb_setup_dns(secrets_ldb, setup_path,
1372 realm=names.realm, dnsdomain=names.dnsdomain,
1373 dns_keytab_path=paths.dns_keytab,
1376 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1377 assert isinstance(domainguid, str)
1379 # Only make a zone file on the first DC, it should be replicated
1380 # with DNS replication
1381 create_zone_file(lp, message, paths, targetdir, setup_path,
1382 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1383 hostname=names.hostname, realm=names.realm,
1384 domainguid=domainguid, ntdsguid=names.ntdsguid)
1386 create_named_conf(paths, setup_path, realm=names.realm,
1387 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1389 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1390 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1391 keytab_name=paths.dns_keytab)
1392 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1393 message("and %s for further documentation required for secure DNS "
1394 "updates" % paths.namedtxt)
1396 create_krb5_conf(paths.krb5conf, setup_path,
1397 dnsdomain=names.dnsdomain, hostname=names.hostname,
1399 message("A Kerberos configuration suitable for Samba 4 has been "
1400 "generated at %s" % paths.krb5conf)
1402 if serverrole == "domain controller":
1403 create_dns_update_list(lp, message, paths, setup_path)
1405 provision_backend.post_setup()
1406 provision_backend.shutdown()
1408 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1411 #Now commit the secrets.ldb to disk
1412 secrets_ldb.transaction_commit()
1414 # the commit creates the dns.keytab, now chown it
1415 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1416 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1418 os.chmod(dns_keytab_path, 0640)
1419 os.chown(dns_keytab_path, -1, paths.bind_gid)
1421 message("Failed to chown %s to bind gid %u" % (dns_keytab_path,
1425 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1427 message("Once the above files are installed, your Samba4 server will be ready to use")
1428 message("Server Role: %s" % serverrole)
1429 message("Hostname: %s" % names.hostname)
1430 message("NetBIOS Domain: %s" % names.domain)
1431 message("DNS Domain: %s" % names.dnsdomain)
1432 message("DOMAIN SID: %s" % str(domainsid))
1433 if samdb_fill == FILL_FULL:
1434 message("Admin password: %s" % adminpass)
1435 if provision_backend.type is not "ldb":
1436 if provision_backend.credentials.get_bind_dn() is not None:
1437 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1439 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1441 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1443 if provision_backend.slapd_command_escaped is not None:
1444 # now display slapd_command_file.txt to show how slapd must be started next time
1445 message("Use later the following commandline to start slapd, then Samba:")
1446 message(provision_backend.slapd_command_escaped)
1447 message("This slapd-Commandline is also stored under: " + provision_backend.ldapdir + "/ldap_backend_startup.sh")
1450 result = ProvisionResult()
1451 result.domaindn = domaindn
1452 result.paths = paths
1454 result.samdb = samdb
1458 def provision_become_dc(setup_dir=None,
1459 smbconf=None, targetdir=None, realm=None,
1460 rootdn=None, domaindn=None, schemadn=None,
1461 configdn=None, serverdn=None,
1462 domain=None, hostname=None, domainsid=None,
1463 adminpass=None, krbtgtpass=None, domainguid=None,
1464 policyguid=None, policyguid_dc=None, invocationid=None,
1466 dnspass=None, root=None, nobody=None, users=None,
1467 wheel=None, backup=None, serverrole=None,
1468 ldap_backend=None, ldap_backend_type=None,
1469 sitename=None, debuglevel=1):
1472 """print a message if quiet is not set."""
1475 samba.set_debug_level(debuglevel)
1477 return provision(setup_dir, message, system_session(), None,
1478 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1479 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1480 configdn=configdn, serverdn=serverdn, domain=domain,
1481 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1482 machinepass=machinepass, serverrole="domain controller",
1486 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1487 """Create a PHP LDAP admin configuration file.
1489 :param path: Path to write the configuration to.
1490 :param setup_path: Function to generate setup paths.
1492 setup_file(setup_path("phpldapadmin-config.php"), path,
1493 {"S4_LDAPI_URI": ldapi_uri})
1496 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1497 hostip, hostip6, hostname, realm, domainguid,
1499 """Write out a DNS zone file, from the info in the current database.
1501 :param paths: paths object
1502 :param setup_path: Setup path function.
1503 :param dnsdomain: DNS Domain name
1504 :param domaindn: DN of the Domain
1505 :param hostip: Local IPv4 IP
1506 :param hostip6: Local IPv6 IP
1507 :param hostname: Local hostname
1508 :param realm: Realm name
1509 :param domainguid: GUID of the domain.
1510 :param ntdsguid: GUID of the hosts nTDSDSA record.
1512 assert isinstance(domainguid, str)
1514 if hostip6 is not None:
1515 hostip6_base_line = " IN AAAA " + hostip6
1516 hostip6_host_line = hostname + " IN AAAA " + hostip6
1518 hostip6_base_line = ""
1519 hostip6_host_line = ""
1521 if hostip is not None:
1522 hostip_base_line = " IN A " + hostip
1523 hostip_host_line = hostname + " IN A " + hostip
1525 hostip_base_line = ""
1526 hostip_host_line = ""
1528 dns_dir = os.path.dirname(paths.dns)
1531 shutil.rmtree(dns_dir, True)
1535 os.mkdir(dns_dir, 0775)
1537 # we need to freeze the zone while we update the contents
1538 if targetdir is None:
1539 rndc = ' '.join(lp.get("rndc command"))
1540 os.system(rndc + " freeze " + lp.get("realm"))
1542 setup_file(setup_path("provision.zone"), paths.dns, {
1543 "HOSTNAME": hostname,
1544 "DNSDOMAIN": dnsdomain,
1546 "HOSTIP_BASE_LINE": hostip_base_line,
1547 "HOSTIP_HOST_LINE": hostip_host_line,
1548 "DOMAINGUID": domainguid,
1549 "DATESTRING": time.strftime("%Y%m%d%H"),
1550 "DEFAULTSITE": DEFAULTSITE,
1551 "NTDSGUID": ntdsguid,
1552 "HOSTIP6_BASE_LINE": hostip6_base_line,
1553 "HOSTIP6_HOST_LINE": hostip6_host_line,
1556 # note that we use no variable substitution on this file
1557 # the substitution is done at runtime by samba_dnsupdate
1558 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1560 # and the SPN update list
1561 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1563 if paths.bind_gid is not None:
1565 os.chown(dns_dir, -1, paths.bind_gid)
1566 os.chown(paths.dns, -1, paths.bind_gid)
1567 # chmod needed to cope with umask
1568 os.chmod(dns_dir, 0775)
1569 os.chmod(paths.dns, 0664)
1571 message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1573 if targetdir is None:
1574 os.system(rndc + " unfreeze " + lp.get("realm"))
1577 def create_dns_update_list(lp, message, paths, setup_path):
1578 """Write out a dns_update_list file"""
1579 # note that we use no variable substitution on this file
1580 # the substitution is done at runtime by samba_dnsupdate
1581 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1582 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1584 def create_named_conf(paths, setup_path, realm, dnsdomain,
1586 """Write out a file containing zone statements suitable for inclusion in a
1587 named.conf file (including GSS-TSIG configuration).
1589 :param paths: all paths
1590 :param setup_path: Setup path function.
1591 :param realm: Realm name
1592 :param dnsdomain: DNS Domain name
1593 :param private_dir: Path to private directory
1594 :param keytab_name: File name of DNS keytab file
1597 setup_file(setup_path("named.conf"), paths.namedconf, {
1598 "DNSDOMAIN": dnsdomain,
1600 "ZONE_FILE": paths.dns,
1601 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1602 "NAMED_CONF": paths.namedconf,
1603 "NAMED_CONF_UPDATE": paths.namedconf_update
1606 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1608 def create_named_txt(path, setup_path, realm, dnsdomain,
1609 private_dir, keytab_name):
1610 """Write out a file containing zone statements suitable for inclusion in a
1611 named.conf file (including GSS-TSIG configuration).
1613 :param path: Path of the new named.conf file.
1614 :param setup_path: Setup path function.
1615 :param realm: Realm name
1616 :param dnsdomain: DNS Domain name
1617 :param private_dir: Path to private directory
1618 :param keytab_name: File name of DNS keytab file
1621 setup_file(setup_path("named.txt"), path, {
1622 "DNSDOMAIN": dnsdomain,
1624 "DNS_KEYTAB": keytab_name,
1625 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1626 "PRIVATE_DIR": private_dir
1629 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1630 """Write out a file containing zone statements suitable for inclusion in a
1631 named.conf file (including GSS-TSIG configuration).
1633 :param path: Path of the new named.conf file.
1634 :param setup_path: Setup path function.
1635 :param dnsdomain: DNS Domain name
1636 :param hostname: Local hostname
1637 :param realm: Realm name
1639 setup_file(setup_path("krb5.conf"), path, {
1640 "DNSDOMAIN": dnsdomain,
1641 "HOSTNAME": hostname,
1646 class ProvisioningError(Exception):
1647 """A generic provision error."""
1649 def __init__(self, value):
1653 return "ProvisioningError: " + self.value
1656 class InvalidNetbiosName(Exception):
1657 """A specified name was not a valid NetBIOS name."""
1658 def __init__(self, name):
1659 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)