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.namedconf = os.path.join(paths.private_dir, "named.conf")
297 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
298 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
299 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
300 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
301 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
302 paths.phpldapadminconfig = os.path.join(paths.private_dir,
303 "phpldapadmin-config.php")
304 paths.hklm = "hklm.ldb"
305 paths.hkcr = "hkcr.ldb"
306 paths.hkcu = "hkcu.ldb"
307 paths.hku = "hku.ldb"
308 paths.hkpd = "hkpd.ldb"
309 paths.hkpt = "hkpt.ldb"
310 paths.sysvol = lp.get("path", "sysvol")
311 paths.netlogon = lp.get("path", "netlogon")
312 paths.smbconf = lp.configfile
316 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
317 serverrole=None, rootdn=None, domaindn=None, configdn=None,
318 schemadn=None, serverdn=None, sitename=None):
319 """Guess configuration settings to use."""
322 hostname = socket.gethostname().split(".")[0]
324 netbiosname = lp.get("netbios name")
325 if netbiosname is None:
326 netbiosname = hostname
327 assert netbiosname is not None
328 netbiosname = netbiosname.upper()
329 if not valid_netbios_name(netbiosname):
330 raise InvalidNetbiosName(netbiosname)
332 if dnsdomain is None:
333 dnsdomain = lp.get("realm")
334 if dnsdomain is None or dnsdomain == "":
335 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
337 dnsdomain = dnsdomain.lower()
339 if serverrole is None:
340 serverrole = lp.get("server role")
341 if serverrole is None:
342 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
344 serverrole = serverrole.lower()
346 realm = dnsdomain.upper()
348 if lp.get("realm") == "":
349 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
351 if lp.get("realm").upper() != realm:
352 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))
354 if lp.get("server role").lower() != serverrole:
355 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))
357 if serverrole == "domain controller":
359 # This will, for better or worse, default to 'WORKGROUP'
360 domain = lp.get("workgroup")
361 domain = domain.upper()
363 if lp.get("workgroup").upper() != domain:
364 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))
367 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
371 domaindn = "DC=" + netbiosname
373 if not valid_netbios_name(domain):
374 raise InvalidNetbiosName(domain)
376 if hostname.upper() == realm:
377 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
378 if netbiosname == realm:
379 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
381 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
387 configdn = "CN=Configuration," + rootdn
389 schemadn = "CN=Schema," + configdn
394 names = ProvisionNames()
395 names.rootdn = rootdn
396 names.domaindn = domaindn
397 names.configdn = configdn
398 names.schemadn = schemadn
399 names.ldapmanagerdn = "CN=Manager," + rootdn
400 names.dnsdomain = dnsdomain
401 names.domain = domain
403 names.netbiosname = netbiosname
404 names.hostname = hostname
405 names.sitename = sitename
406 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
411 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
412 targetdir, sid_generator,eadb):
413 """Create a new smb.conf file based on a couple of basic settings.
415 assert smbconf is not None
417 hostname = socket.gethostname().split(".")[0]
418 netbiosname = hostname.upper()
420 if serverrole is None:
421 serverrole = "standalone"
423 assert serverrole in ("domain controller", "member server", "standalone")
424 if serverrole == "domain controller":
426 elif serverrole == "member server":
427 smbconfsuffix = "member"
428 elif serverrole == "standalone":
429 smbconfsuffix = "standalone"
431 if sid_generator is None:
432 sid_generator = "internal"
434 assert domain is not None
435 domain = domain.upper()
437 assert realm is not None
438 realm = realm.upper()
440 default_lp = param.LoadParm()
441 #Load non-existant file
442 if os.path.exists(smbconf):
443 default_lp.load(smbconf)
445 if targetdir is not None:
446 privdir = os.path.join(targetdir, "private")
448 privdir = default_lp.get("private dir")
449 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
453 if targetdir is not None:
454 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
455 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
457 default_lp.set("lock dir", os.path.abspath(targetdir))
462 if sid_generator == "internal":
463 sid_generator_line = ""
465 sid_generator_line = "sid generator = " + sid_generator
467 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
468 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
470 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
472 "NETBIOS_NAME": netbiosname,
475 "SERVERROLE": serverrole,
476 "NETLOGONPATH": netlogon,
477 "SYSVOLPATH": sysvol,
478 "SIDGENERATOR_LINE": sid_generator_line,
479 "PRIVATEDIR_LINE": privatedir_line,
480 "LOCKDIR_LINE": lockdir_line,
481 "POSIXEADB_LINE": posixeadb_line
485 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
486 users_gid, wheel_gid):
487 """setup reasonable name mappings for sam names to unix names.
489 :param samdb: SamDB object.
490 :param idmap: IDmap db object.
491 :param sid: The domain sid.
492 :param domaindn: The domain DN.
493 :param root_uid: uid of the UNIX root user.
494 :param nobody_uid: uid of the UNIX nobody user.
495 :param users_gid: gid of the UNIX users group.
496 :param wheel_gid: gid of the UNIX wheel group."""
497 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
498 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
500 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
501 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
504 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
505 provision_backend, names, schema,
508 """Setup the partitions for the SAM database.
510 Alternatively, provision() may call this, and then populate the database.
512 :note: This will wipe the Sam Database!
514 :note: This function always removes the local SAM LDB file. The erase
515 parameter controls whether to erase the existing data, which
516 may not be stored locally but in LDAP.
519 assert session_info is not None
521 # We use options=["modules:"] to stop the modules loading - we
522 # just want to wipe and re-initialise the database, not start it up
525 os.unlink(samdb_path)
529 samdb = Ldb(url=samdb_path, session_info=session_info,
530 lp=lp, options=["modules:"])
532 ldap_backend_line = "# No LDAP backend"
533 if provision_backend.type is not "ldb":
534 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
536 samdb.transaction_start()
538 message("Setting up sam.ldb partitions and settings")
539 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
540 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
541 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
542 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
543 "LDAP_BACKEND_LINE": ldap_backend_line,
547 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
548 "BACKEND_TYPE": provision_backend.type,
549 "SERVER_ROLE": serverrole
552 message("Setting up sam.ldb rootDSE")
553 setup_samdb_rootdse(samdb, setup_path, names)
555 samdb.transaction_cancel()
558 samdb.transaction_commit()
561 def secretsdb_self_join(secretsdb, domain,
562 netbiosname, machinepass, domainsid=None,
563 realm=None, dnsdomain=None,
565 key_version_number=1,
566 secure_channel_type=SEC_CHAN_WKSTA):
567 """Add domain join-specific bits to a secrets database.
569 :param secretsdb: Ldb Handle to the secrets database
570 :param machinepass: Machine password
572 attrs=["whenChanged",
580 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
581 msg["secureChannelType"] = str(secure_channel_type)
582 msg["flatname"] = [domain]
583 msg["objectClass"] = ["top", "primaryDomain"]
584 if realm is not None:
585 if dnsdomain is None:
586 dnsdomain = realm.lower()
587 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
589 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
590 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
591 msg["privateKeytab"] = ["secrets.keytab"]
594 msg["secret"] = [machinepass]
595 msg["samAccountName"] = ["%s$" % netbiosname]
596 msg["secureChannelType"] = [str(secure_channel_type)]
597 if domainsid is not None:
598 msg["objectSid"] = [ndr_pack(domainsid)]
600 res = secretsdb.search(base="cn=Primary Domains",
602 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
603 scope=ldb.SCOPE_ONELEVEL)
606 if del_msg.dn is not msg.dn:
607 secretsdb.delete(del_msg.dn)
609 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
612 msg["priorSecret"] = res[0]["secret"]
613 msg["priorWhenChanged"] = res[0]["whenChanged"]
615 if res["privateKeytab"] is not None:
616 msg["privateKeytab"] = res[0]["privateKeytab"]
618 if res["krb5Keytab"] is not None:
619 msg["krb5Keytab"] = res[0]["krb5Keytab"]
622 el.set_flags(ldb.FLAG_MOD_REPLACE)
623 secretsdb.modify(msg)
628 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
630 dns_keytab_path, dnspass):
631 """Add DNS specific bits to a secrets database.
633 :param secretsdb: Ldb Handle to the secrets database
634 :param setup_path: Setup path function
635 :param machinepass: Machine password
638 os.unlink(os.path.join(private_dir, dns_keytab_path))
642 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
644 "DNSDOMAIN": dnsdomain,
645 "DNS_KEYTAB": dns_keytab_path,
646 "DNSPASS_B64": b64encode(dnspass),
650 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
651 """Setup the secrets database.
653 :param path: Path to the secrets database.
654 :param setup_path: Get the path to a setup file.
655 :param session_info: Session info.
656 :param credentials: Credentials
657 :param lp: Loadparm context
658 :return: LDB handle for the created secrets database
660 if os.path.exists(path):
662 secrets_ldb = Ldb(path, session_info=session_info,
665 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
666 secrets_ldb = Ldb(path, session_info=session_info,
668 secrets_ldb.transaction_start()
669 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
671 if backend_credentials is not None and backend_credentials.authentication_requested():
672 if backend_credentials.get_bind_dn() is not None:
673 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
674 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
675 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
678 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
679 "LDAPADMINUSER": backend_credentials.get_username(),
680 "LDAPADMINREALM": backend_credentials.get_realm(),
681 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
686 def setup_privileges(path, setup_path, session_info, lp):
687 """Setup the privileges database.
689 :param path: Path to the privileges database.
690 :param setup_path: Get the path to a setup file.
691 :param session_info: Session info.
692 :param credentials: Credentials
693 :param lp: Loadparm context
694 :return: LDB handle for the created secrets database
696 if os.path.exists(path):
698 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
699 privilege_ldb.erase()
700 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
703 def setup_registry(path, setup_path, session_info, lp):
704 """Setup the registry.
706 :param path: Path to the registry database
707 :param setup_path: Function that returns the path to a setup.
708 :param session_info: Session information
709 :param credentials: Credentials
710 :param lp: Loadparm context
712 reg = registry.Registry()
713 hive = registry.open_ldb(path, session_info=session_info,
715 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
716 provision_reg = setup_path("provision.reg")
717 assert os.path.exists(provision_reg)
718 reg.diff_apply(provision_reg)
721 def setup_idmapdb(path, setup_path, session_info, lp):
722 """Setup the idmap database.
724 :param path: path to the idmap database
725 :param setup_path: Function that returns a path to a setup file
726 :param session_info: Session information
727 :param credentials: Credentials
728 :param lp: Loadparm context
730 if os.path.exists(path):
733 idmap_ldb = IDmapDB(path, session_info=session_info,
737 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
741 def setup_samdb_rootdse(samdb, setup_path, names):
742 """Setup the SamDB rootdse.
744 :param samdb: Sam Database handle
745 :param setup_path: Obtain setup path
747 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
748 "SCHEMADN": names.schemadn,
749 "NETBIOSNAME": names.netbiosname,
750 "DNSDOMAIN": names.dnsdomain,
751 "REALM": names.realm,
752 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
753 "DOMAINDN": names.domaindn,
754 "ROOTDN": names.rootdn,
755 "CONFIGDN": names.configdn,
756 "SERVERDN": names.serverdn,
760 def setup_self_join(samdb, names,
761 machinepass, dnspass,
762 domainsid, invocationid, setup_path,
763 policyguid, policyguid_dc, domainControllerFunctionality,
765 """Join a host to its own domain."""
766 assert isinstance(invocationid, str)
767 if ntdsguid is not None:
768 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
771 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
772 "CONFIGDN": names.configdn,
773 "SCHEMADN": names.schemadn,
774 "DOMAINDN": names.domaindn,
775 "SERVERDN": names.serverdn,
776 "INVOCATIONID": invocationid,
777 "NETBIOSNAME": names.netbiosname,
778 "DEFAULTSITE": names.sitename,
779 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
780 "MACHINEPASS_B64": b64encode(machinepass),
781 "REALM": names.realm,
782 "DOMAIN": names.domain,
783 "DOMAINSID": str(domainsid),
784 "DNSDOMAIN": names.dnsdomain,
785 "SAMBA_VERSION_STRING": version,
786 "NTDSGUID": ntdsguid_line,
787 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
789 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
790 "POLICYGUID": policyguid,
791 "POLICYGUID_DC": policyguid_dc,
792 "DNSDOMAIN": names.dnsdomain,
793 "DOMAINSID": str(domainsid),
794 "DOMAINDN": names.domaindn})
796 # add the NTDSGUID based SPNs
797 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
798 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
799 expression="", scope=ldb.SCOPE_BASE)
800 assert isinstance(names.ntdsguid, str)
802 # Setup fSMORoleOwner entries to point at the newly created DC entry
803 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
804 "DOMAIN": names.domain,
805 "DNSDOMAIN": names.dnsdomain,
806 "DOMAINDN": names.domaindn,
807 "CONFIGDN": names.configdn,
808 "SCHEMADN": names.schemadn,
809 "DEFAULTSITE": names.sitename,
810 "SERVERDN": names.serverdn,
811 "NETBIOSNAME": names.netbiosname,
812 "NTDSGUID": names.ntdsguid,
813 "DNSPASS_B64": b64encode(dnspass),
816 def getpolicypath(sysvolpath, dnsdomain, guid):
819 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
822 def create_gpo_struct(policy_path):
823 os.makedirs(policy_path, 0755)
824 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
825 "[General]\r\nVersion=65543")
826 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
827 os.makedirs(os.path.join(policy_path, "USER"), 0755)
830 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
831 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
832 create_gpo_struct(policy_path)
834 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
835 create_gpo_struct(policy_path)
838 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
840 domainsid, domainguid, policyguid, policyguid_dc,
841 fill, adminpass, krbtgtpass,
842 machinepass, invocationid, dnspass, ntdsguid,
843 serverrole, dom_for_fun_level=None,
845 """Setup a complete SAM Database.
847 :note: This will wipe the main SAM database file!
850 # ATTENTION: Do NOT change these default values without discussion with the
851 # team and/or release manager. They have a big impact on the whole program!
852 domainControllerFunctionality = DS_DC_FUNCTION_2008
854 if dom_for_fun_level is None:
855 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
856 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
857 message("You want to run SAMBA 4 on a domain and forest function level"
858 " lower than Windows 2003 (Native). This is not recommended")
860 if dom_for_fun_level > domainControllerFunctionality:
861 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!")
863 domainFunctionality = dom_for_fun_level
864 forestFunctionality = dom_for_fun_level
866 # Also wipes the database
867 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
868 provision_backend=provision_backend, session_info=session_info,
869 names=names, serverrole=serverrole, schema=schema)
872 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
874 # Load the database, but importantly, use Ldb not SamDB as we don't want to
875 # load the global schema
876 samdb = Ldb(session_info=session_info,
877 credentials=provision_backend.credentials, lp=lp)
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)
905 message("Adding DomainDN: %s" % names.domaindn)
907 #impersonate domain admin
908 admin_session_info = admin_session(lp, str(domainsid))
909 samdb.set_session_info(admin_session_info)
910 if domainguid is not None:
911 domainguid_line = "objectGUID: %s\n-" % domainguid
915 descr = b64encode(get_domain_descriptor(domainsid))
916 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
917 "DOMAINDN": names.domaindn,
918 "DOMAINGUID": domainguid_line,
923 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
924 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
925 "DOMAINSID": str(domainsid),
926 "SCHEMADN": names.schemadn,
927 "NETBIOSNAME": names.netbiosname,
928 "DEFAULTSITE": names.sitename,
929 "CONFIGDN": names.configdn,
930 "SERVERDN": names.serverdn,
931 "POLICYGUID": policyguid,
932 "DOMAINDN": names.domaindn,
933 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
934 "SAMBA_VERSION_STRING": version
937 message("Adding configuration container")
938 descr = b64encode(get_config_descriptor(domainsid))
939 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
940 "CONFIGDN": names.configdn,
944 # The LDIF here was created when the Schema object was constructed
945 message("Setting up sam.ldb schema")
946 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
947 samdb.modify_ldif(schema.schema_dn_modify)
948 samdb.write_prefixes_from_schema()
949 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
950 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
951 {"SCHEMADN": names.schemadn})
953 message("Reopening sam.ldb with new schema")
955 samdb.transaction_cancel()
958 samdb.transaction_commit()
960 samdb = SamDB(session_info=admin_session_info,
961 credentials=provision_backend.credentials, lp=lp,
964 samdb.transaction_start()
966 samdb.invocation_id = invocationid
968 message("Setting up sam.ldb configuration data")
969 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
970 "CONFIGDN": names.configdn,
971 "NETBIOSNAME": names.netbiosname,
972 "DEFAULTSITE": names.sitename,
973 "DNSDOMAIN": names.dnsdomain,
974 "DOMAIN": names.domain,
975 "SCHEMADN": names.schemadn,
976 "DOMAINDN": names.domaindn,
977 "SERVERDN": names.serverdn,
978 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
981 message("Setting up display specifiers")
982 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
983 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
984 check_all_substituted(display_specifiers_ldif)
985 samdb.add_ldif(display_specifiers_ldif)
987 message("Adding users container")
988 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
989 "DOMAINDN": names.domaindn})
990 message("Modifying users container")
991 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
992 "DOMAINDN": names.domaindn})
993 message("Adding computers container")
994 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
995 "DOMAINDN": names.domaindn})
996 message("Modifying computers container")
997 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
998 "DOMAINDN": names.domaindn})
999 message("Setting up sam.ldb data")
1000 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1001 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1002 "DOMAINDN": names.domaindn,
1003 "NETBIOSNAME": names.netbiosname,
1004 "DEFAULTSITE": names.sitename,
1005 "CONFIGDN": names.configdn,
1006 "SERVERDN": names.serverdn,
1007 "POLICYGUID_DC": policyguid_dc
1010 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1011 "DOMAINDN": names.domaindn})
1013 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1014 "CONFIGDN": names.configdn,
1015 "SCHEMADN": names.schemadn})
1016 if fill == FILL_FULL:
1017 message("Setting up sam.ldb users and groups")
1018 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1019 "DOMAINDN": names.domaindn,
1020 "DOMAINSID": str(domainsid),
1021 "CONFIGDN": names.configdn,
1022 "ADMINPASS_B64": b64encode(adminpass),
1023 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1026 message("Setting up self join")
1027 setup_self_join(samdb, names=names, invocationid=invocationid,
1029 machinepass=machinepass,
1030 domainsid=domainsid, policyguid=policyguid,
1031 policyguid_dc=policyguid_dc,
1032 setup_path=setup_path,
1033 domainControllerFunctionality=domainControllerFunctionality,
1036 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1037 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1038 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1039 assert isinstance(names.ntdsguid, str)
1041 samdb.transaction_cancel()
1044 samdb.transaction_commit()
1049 FILL_NT4SYNC = "NT4SYNC"
1051 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1052 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)"
1054 def set_dir_acl(path, acl, lp, domsid):
1055 setntacl(lp, path, acl, domsid)
1056 for root, dirs, files in os.walk(path, topdown=False):
1058 setntacl(lp, os.path.join(root, name), acl, domsid)
1060 setntacl(lp, os.path.join(root, name), acl, domsid)
1063 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1065 policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1066 set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
1068 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1069 attrs=["cn","nTSecurityDescriptor"],
1070 expression="", scope=ldb.SCOPE_ONELEVEL)
1072 acl = ndr_unpack(security.descriptor,
1073 str(policy["nTSecurityDescriptor"])).as_sddl()
1074 policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1075 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1078 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1081 os.chown(sysvol,-1,gid)
1087 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1088 for root, dirs, files in os.walk(sysvol, topdown=False):
1091 os.chown(os.path.join(root, name),-1,gid)
1092 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1095 os.chown(os.path.join(root, name),-1,gid)
1096 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1097 set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1100 def provision(setup_dir, message, session_info,
1101 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1103 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1105 domain=None, hostname=None, hostip=None, hostip6=None,
1106 domainsid=None, adminpass=None, ldapadminpass=None,
1107 krbtgtpass=None, domainguid=None,
1108 policyguid=None, policyguid_dc=None, invocationid=None,
1109 machinepass=None, ntdsguid=None,
1110 dnspass=None, root=None, nobody=None, users=None,
1111 wheel=None, backup=None, aci=None, serverrole=None,
1112 dom_for_fun_level=None,
1113 ldap_backend_extra_port=None, backend_type=None,
1115 ol_mmr_urls=None, ol_olc=None,
1116 setup_ds_path=None, slapd_path=None, nosync=False,
1117 ldap_dryrun_mode=False,useeadb=False):
1120 :note: caution, this wipes all existing data!
1123 def setup_path(file):
1124 return os.path.join(setup_dir, file)
1126 if domainsid is None:
1127 domainsid = security.random_sid()
1129 domainsid = security.dom_sid(domainsid)
1131 # create/adapt the group policy GUIDs
1132 if policyguid is None:
1133 policyguid = str(uuid.uuid4())
1134 policyguid = policyguid.upper()
1135 if policyguid_dc is None:
1136 policyguid_dc = str(uuid.uuid4())
1137 policyguid_dc = policyguid_dc.upper()
1139 if adminpass is None:
1140 adminpass = samba.generate_random_password(12, 32)
1141 if krbtgtpass is None:
1142 krbtgtpass = samba.generate_random_password(128, 255)
1143 if machinepass is None:
1144 machinepass = samba.generate_random_password(128, 255)
1146 dnspass = samba.generate_random_password(128, 255)
1147 if ldapadminpass is None:
1148 #Make a new, random password between Samba and it's LDAP server
1149 ldapadminpass=samba.generate_random_password(128, 255)
1151 if backend_type is None:
1152 backend_type = "ldb"
1154 sid_generator = "internal"
1155 if backend_type == "fedora-ds":
1156 sid_generator = "backend"
1158 root_uid = findnss_uid([root or "root"])
1159 nobody_uid = findnss_uid([nobody or "nobody"])
1160 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1162 wheel_gid = findnss_gid(["wheel", "adm"])
1164 wheel_gid = findnss_gid([wheel])
1166 bind_gid = findnss_gid(["bind", "named"])
1170 if targetdir is not None:
1171 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1172 os.makedirs(os.path.join(targetdir, "etc"))
1173 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1174 elif smbconf is None:
1175 smbconf = param.default_path()
1177 # only install a new smb.conf if there isn't one there already
1178 if os.path.exists(smbconf):
1179 # if Samba Team members can't figure out the weird errors
1180 # loading an empty smb.conf gives, then we need to be smarter.
1181 # Pretend it just didn't exist --abartlet
1182 data = open(smbconf, 'r').read()
1183 data = data.lstrip()
1184 if data is None or data == "":
1185 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1186 serverrole, targetdir, sid_generator, useeadb)
1188 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1189 targetdir, sid_generator, useeadb)
1191 lp = param.LoadParm()
1194 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1195 dnsdomain=realm, serverrole=serverrole,
1196 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1197 serverdn=serverdn, sitename=sitename)
1199 paths = provision_paths_from_lp(lp, names.dnsdomain)
1201 paths.bind_gid = bind_gid
1204 hostips = samba.interface_ips(lp, False)
1205 if len(hostips) == 0:
1206 message("No external IPv4 address has been found: I use the loopback.")
1207 hostip = '127.0.0.1'
1210 if len(hostips) > 1:
1211 message("More than one IPv4 address found: I use " + hostip + ".")
1215 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1218 if hostip6 == '::1' and ip[-1][0] != '::1':
1220 except socket.gaierror, (socket.EAI_NODATA, msg):
1223 if serverrole is None:
1224 serverrole = lp.get("server role")
1226 assert serverrole in ("domain controller", "member server", "standalone")
1227 if invocationid is None:
1228 invocationid = str(uuid.uuid4())
1230 if not os.path.exists(paths.private_dir):
1231 os.mkdir(paths.private_dir)
1232 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1233 os.mkdir(os.path.join(paths.private_dir,"tls"))
1235 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1237 schema = Schema(setup_path, domainsid, schemadn=names.schemadn,
1238 serverdn=names.serverdn)
1240 if backend_type == "ldb":
1241 provision_backend = LDBBackend(backend_type,
1242 paths=paths, setup_path=setup_path,
1243 lp=lp, credentials=credentials,
1246 elif backend_type == "existing":
1247 provision_backend = ExistingBackend(backend_type,
1248 paths=paths, setup_path=setup_path,
1249 lp=lp, credentials=credentials,
1252 ldapi_url=ldapi_url)
1253 elif backend_type == "fedora-ds":
1254 provision_backend = FDSBackend(backend_type,
1255 paths=paths, setup_path=setup_path,
1256 lp=lp, credentials=credentials,
1259 domainsid=domainsid,
1262 ldapadminpass=ldapadminpass,
1263 slapd_path=slapd_path,
1264 ldap_backend_extra_port=ldap_backend_extra_port,
1265 ldap_dryrun_mode=ldap_dryrun_mode,
1267 setup_ds_path=setup_ds_path)
1268 elif backend_type == "openldap":
1269 provision_backend = OpenLDAPBackend(backend_type,
1270 paths=paths, setup_path=setup_path,
1271 lp=lp, credentials=credentials,
1274 domainsid=domainsid,
1277 ldapadminpass=ldapadminpass,
1278 slapd_path=slapd_path,
1279 ldap_backend_extra_port=ldap_backend_extra_port,
1280 ldap_dryrun_mode=ldap_dryrun_mode,
1281 ol_mmr_urls=ol_mmr_urls,
1284 raise ValueError("Unknown LDAP backend type selected")
1286 provision_backend.init()
1287 provision_backend.start()
1289 # only install a new shares config db if there is none
1290 if not os.path.exists(paths.shareconf):
1291 message("Setting up share.ldb")
1292 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1294 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1297 message("Setting up secrets.ldb")
1298 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1299 session_info=session_info,
1300 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1302 message("Setting up the registry")
1303 setup_registry(paths.hklm, setup_path, session_info,
1306 message("Setting up the privileges database")
1307 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1309 message("Setting up idmap db")
1310 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1313 message("Setting up SAM db")
1314 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1315 provision_backend, lp, names,
1317 domainsid=domainsid,
1318 schema=schema, domainguid=domainguid,
1319 policyguid=policyguid, policyguid_dc=policyguid_dc,
1321 adminpass=adminpass, krbtgtpass=krbtgtpass,
1322 invocationid=invocationid,
1323 machinepass=machinepass, dnspass=dnspass,
1324 ntdsguid=ntdsguid, serverrole=serverrole,
1325 dom_for_fun_level=dom_for_fun_level)
1327 if serverrole == "domain controller":
1328 if paths.netlogon is None:
1329 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1330 message("Please either remove %s or see the template at %s" %
1331 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1332 assert paths.netlogon is not None
1334 if paths.sysvol is None:
1335 message("Existing smb.conf does not have a [sysvol] share, but you"
1336 " are configuring a DC.")
1337 message("Please either remove %s or see the template at %s" %
1338 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1339 assert paths.sysvol is not None
1341 if not os.path.isdir(paths.netlogon):
1342 os.makedirs(paths.netlogon, 0755)
1344 if samdb_fill == FILL_FULL:
1345 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1346 root_uid=root_uid, nobody_uid=nobody_uid,
1347 users_gid=users_gid, wheel_gid=wheel_gid)
1349 if serverrole == "domain controller":
1350 # Set up group policies (domain policy and domain controller policy)
1351 setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1352 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1353 domainsid, names.dnsdomain, names.domaindn, lp)
1355 message("Setting up sam.ldb rootDSE marking as synchronized")
1356 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1358 secretsdb_self_join(secrets_ldb, domain=names.domain,
1360 dnsdomain=names.dnsdomain,
1361 netbiosname=names.netbiosname,
1362 domainsid=domainsid,
1363 machinepass=machinepass,
1364 secure_channel_type=SEC_CHAN_BDC)
1366 if serverrole == "domain controller":
1367 secretsdb_setup_dns(secrets_ldb, setup_path,
1369 realm=names.realm, dnsdomain=names.dnsdomain,
1370 dns_keytab_path=paths.dns_keytab,
1373 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1374 assert isinstance(domainguid, str)
1376 # Only make a zone file on the first DC, it should be replicated
1377 # with DNS replication
1378 create_zone_file(lp, message, paths, targetdir, setup_path,
1379 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1380 hostname=names.hostname, realm=names.realm,
1381 domainguid=domainguid, ntdsguid=names.ntdsguid)
1383 create_named_conf(paths, setup_path, realm=names.realm,
1384 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1386 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1387 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1388 keytab_name=paths.dns_keytab)
1389 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1390 message("and %s for further documentation required for secure DNS "
1391 "updates" % paths.namedtxt)
1393 create_krb5_conf(paths.krb5conf, setup_path,
1394 dnsdomain=names.dnsdomain, hostname=names.hostname,
1396 message("A Kerberos configuration suitable for Samba 4 has been "
1397 "generated at %s" % paths.krb5conf)
1399 if serverrole == "domain controller":
1400 create_dns_update_list(lp, message, paths, setup_path)
1402 provision_backend.post_setup()
1403 provision_backend.shutdown()
1405 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1408 #Now commit the secrets.ldb to disk
1409 secrets_ldb.transaction_commit()
1411 # the commit creates the dns.keytab, now chown it
1412 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1413 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1415 os.chmod(dns_keytab_path, 0640)
1416 os.chown(dns_keytab_path, -1, paths.bind_gid)
1418 message("Failed to chown %s to bind gid %u" % (dns_keytab_path,
1422 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1424 message("Once the above files are installed, your Samba4 server will be ready to use")
1425 message("Server Role: %s" % serverrole)
1426 message("Hostname: %s" % names.hostname)
1427 message("NetBIOS Domain: %s" % names.domain)
1428 message("DNS Domain: %s" % names.dnsdomain)
1429 message("DOMAIN SID: %s" % str(domainsid))
1430 if samdb_fill == FILL_FULL:
1431 message("Admin password: %s" % adminpass)
1432 if provision_backend.type is not "ldb":
1433 if provision_backend.credentials.get_bind_dn() is not None:
1434 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1436 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1438 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1440 if provision_backend.slapd_command_escaped is not None:
1441 # now display slapd_command_file.txt to show how slapd must be started next time
1442 message("Use later the following commandline to start slapd, then Samba:")
1443 message(provision_backend.slapd_command_escaped)
1444 message("This slapd-Commandline is also stored under: " + provision_backend.ldapdir + "/ldap_backend_startup.sh")
1447 result = ProvisionResult()
1448 result.domaindn = domaindn
1449 result.paths = paths
1451 result.samdb = samdb
1455 def provision_become_dc(setup_dir=None,
1456 smbconf=None, targetdir=None, realm=None,
1457 rootdn=None, domaindn=None, schemadn=None,
1458 configdn=None, serverdn=None,
1459 domain=None, hostname=None, domainsid=None,
1460 adminpass=None, krbtgtpass=None, domainguid=None,
1461 policyguid=None, policyguid_dc=None, invocationid=None,
1463 dnspass=None, root=None, nobody=None, users=None,
1464 wheel=None, backup=None, serverrole=None,
1465 ldap_backend=None, ldap_backend_type=None,
1466 sitename=None, debuglevel=1):
1469 """print a message if quiet is not set."""
1472 samba.set_debug_level(debuglevel)
1474 return provision(setup_dir, message, system_session(), None,
1475 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1476 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1477 configdn=configdn, serverdn=serverdn, domain=domain,
1478 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1479 machinepass=machinepass, serverrole="domain controller",
1483 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1484 """Create a PHP LDAP admin configuration file.
1486 :param path: Path to write the configuration to.
1487 :param setup_path: Function to generate setup paths.
1489 setup_file(setup_path("phpldapadmin-config.php"), path,
1490 {"S4_LDAPI_URI": ldapi_uri})
1493 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1494 hostip, hostip6, hostname, realm, domainguid,
1496 """Write out a DNS zone file, from the info in the current database.
1498 :param paths: paths object
1499 :param setup_path: Setup path function.
1500 :param dnsdomain: DNS Domain name
1501 :param domaindn: DN of the Domain
1502 :param hostip: Local IPv4 IP
1503 :param hostip6: Local IPv6 IP
1504 :param hostname: Local hostname
1505 :param realm: Realm name
1506 :param domainguid: GUID of the domain.
1507 :param ntdsguid: GUID of the hosts nTDSDSA record.
1509 assert isinstance(domainguid, str)
1511 if hostip6 is not None:
1512 hostip6_base_line = " IN AAAA " + hostip6
1513 hostip6_host_line = hostname + " IN AAAA " + hostip6
1515 hostip6_base_line = ""
1516 hostip6_host_line = ""
1518 if hostip is not None:
1519 hostip_base_line = " IN A " + hostip
1520 hostip_host_line = hostname + " IN A " + hostip
1522 hostip_base_line = ""
1523 hostip_host_line = ""
1525 dns_dir = os.path.dirname(paths.dns)
1528 shutil.rmtree(dns_dir, True)
1532 os.mkdir(dns_dir, 0775)
1534 # we need to freeze the zone while we update the contents
1535 if targetdir is None:
1536 rndc = ' '.join(lp.get("rndc command"))
1537 os.system(rndc + " freeze " + lp.get("realm"))
1539 setup_file(setup_path("provision.zone"), paths.dns, {
1540 "HOSTNAME": hostname,
1541 "DNSDOMAIN": dnsdomain,
1543 "HOSTIP_BASE_LINE": hostip_base_line,
1544 "HOSTIP_HOST_LINE": hostip_host_line,
1545 "DOMAINGUID": domainguid,
1546 "DATESTRING": time.strftime("%Y%m%d%H"),
1547 "DEFAULTSITE": DEFAULTSITE,
1548 "NTDSGUID": ntdsguid,
1549 "HOSTIP6_BASE_LINE": hostip6_base_line,
1550 "HOSTIP6_HOST_LINE": hostip6_host_line,
1553 # note that we use no variable substitution on this file
1554 # the substitution is done at runtime by samba_dnsupdate
1555 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1557 if paths.bind_gid is not None:
1559 os.chown(dns_dir, -1, paths.bind_gid)
1560 os.chown(paths.dns, -1, paths.bind_gid)
1561 # chmod needed to cope with umask
1562 os.chmod(dns_dir, 0775)
1563 os.chmod(paths.dns, 0664)
1565 message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1567 if targetdir is None:
1568 os.system(rndc + " unfreeze " + lp.get("realm"))
1571 def create_dns_update_list(lp, message, paths, setup_path):
1572 """Write out a dns_update_list file"""
1573 # note that we use no variable substitution on this file
1574 # the substitution is done at runtime by samba_dnsupdate
1575 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1578 def create_named_conf(paths, setup_path, realm, dnsdomain,
1580 """Write out a file containing zone statements suitable for inclusion in a
1581 named.conf file (including GSS-TSIG configuration).
1583 :param paths: all paths
1584 :param setup_path: Setup path function.
1585 :param realm: Realm name
1586 :param dnsdomain: DNS Domain name
1587 :param private_dir: Path to private directory
1588 :param keytab_name: File name of DNS keytab file
1591 setup_file(setup_path("named.conf"), paths.namedconf, {
1592 "DNSDOMAIN": dnsdomain,
1594 "ZONE_FILE": paths.dns,
1595 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1596 "NAMED_CONF": paths.namedconf,
1597 "NAMED_CONF_UPDATE": paths.namedconf_update
1600 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1602 def create_named_txt(path, setup_path, realm, dnsdomain,
1603 private_dir, keytab_name):
1604 """Write out a file containing zone statements suitable for inclusion in a
1605 named.conf file (including GSS-TSIG configuration).
1607 :param path: Path of the new named.conf file.
1608 :param setup_path: Setup path function.
1609 :param realm: Realm name
1610 :param dnsdomain: DNS Domain name
1611 :param private_dir: Path to private directory
1612 :param keytab_name: File name of DNS keytab file
1615 setup_file(setup_path("named.txt"), path, {
1616 "DNSDOMAIN": dnsdomain,
1618 "DNS_KEYTAB": keytab_name,
1619 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1620 "PRIVATE_DIR": private_dir
1623 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1624 """Write out a file containing zone statements suitable for inclusion in a
1625 named.conf file (including GSS-TSIG configuration).
1627 :param path: Path of the new named.conf file.
1628 :param setup_path: Setup path function.
1629 :param dnsdomain: DNS Domain name
1630 :param hostname: Local hostname
1631 :param realm: Realm name
1633 setup_file(setup_path("krb5.conf"), path, {
1634 "DNSDOMAIN": dnsdomain,
1635 "HOSTNAME": hostname,
1640 class ProvisioningError(Exception):
1641 """A generic provision error."""
1643 def __init__(self, value):
1647 return "ProvisioningError: " + self.value
1650 class InvalidNetbiosName(Exception):
1651 """A specified name was not a valid NetBIOS name."""
1652 def __init__(self, name):
1653 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)