2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
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
43 from samba import glue, version, Ldb, substitute_var, valid_netbios_name
44 from samba import check_all_substituted, read_and_sub_file, setup_file
45 from samba import DS_DOMAIN_FUNCTION_2003, DS_DC_FUNCTION_2008
46 from samba.dcerpc import security
47 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
48 from samba.idmap import IDmapDB
49 from samba.ntacls import setntacl, dsacl2fsacl
50 from samba.ndr import ndr_pack,ndr_unpack
51 from samba.schema import Schema
52 from ms_display_specifiers import read_ms_ldif
53 from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
54 from provisionexceptions import ProvisioningError, InvalidNetbiosName
56 __docformat__ = "restructuredText"
59 """Find the setup directory used by provision."""
60 dirname = os.path.dirname(__file__)
61 if "/site-packages/" in dirname:
62 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
63 for suffix in ["share/setup", "share/samba/setup", "setup"]:
64 ret = os.path.join(prefix, suffix)
65 if os.path.isdir(ret):
68 ret = os.path.join(dirname, "../../../setup")
69 if os.path.isdir(ret):
71 raise Exception("Unable to find setup directory.")
73 # descriptors of the naming contexts
74 # hard coded at this point, but will probably be changed when
75 # we enable different fsmo roles
77 def get_config_descriptor(domain_sid):
78 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
79 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
81 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
84 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
85 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
86 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
87 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
88 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
89 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
90 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
91 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
92 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
93 sec = security.descriptor.from_sddl(sddl, domain_sid)
96 def get_domain_descriptor(domain_sid):
97 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
98 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
99 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
100 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
101 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
102 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
103 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
104 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
105 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
106 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
107 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
108 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
109 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
110 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
111 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
112 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
113 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
114 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
119 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
120 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
121 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
122 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
123 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
125 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
126 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
127 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
128 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
129 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
133 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
134 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
135 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
138 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
140 "(A;;RPLCLORC;;;ED)" \
141 "(A;;RPLCLORC;;;AU)" \
142 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
143 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
144 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
145 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
146 sec = security.descriptor.from_sddl(sddl, domain_sid)
149 DEFAULTSITE = "Default-First-Site-Name"
153 class ProvisionPaths(object):
155 self.shareconf = None
166 self.dns_keytab = None
169 self.private_dir = None
172 class ProvisionNames(object):
178 self.ldapmanagerdn = None
179 self.dnsdomain = None
181 self.netbiosname = None
188 class ProvisionResult(object):
195 def check_install(lp, session_info, credentials):
196 """Check whether the current install seems ok.
198 :param lp: Loadparm context
199 :param session_info: Session information
200 :param credentials: Credentials
202 if lp.get("realm") == "":
203 raise Exception("Realm empty")
204 ldb = Ldb(lp.get("sam database"), session_info=session_info,
205 credentials=credentials, lp=lp)
206 if len(ldb.search("(cn=Administrator)")) != 1:
207 raise ProvisioningError("No administrator account found")
210 def findnss(nssfn, names):
211 """Find a user or group from a list of possibilities.
213 :param nssfn: NSS Function to try (should raise KeyError if not found)
214 :param names: Names to check.
215 :return: Value return by first names list.
222 raise KeyError("Unable to find user/group in %r" % names)
225 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
226 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
229 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
230 """Setup a ldb in the private dir.
232 :param ldb: LDB file to import data into
233 :param ldif_path: Path of the LDIF file to load
234 :param subst_vars: Optional variables to subsitute in LDIF.
235 :param nocontrols: Optional list of controls, can be None for no controls
237 assert isinstance(ldif_path, str)
238 data = read_and_sub_file(ldif_path, subst_vars)
239 ldb.add_ldif(data, controls)
242 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
243 """Modify a ldb in the private dir.
245 :param ldb: LDB object.
246 :param ldif_path: LDIF file path.
247 :param subst_vars: Optional dictionary with substitution variables.
249 data = read_and_sub_file(ldif_path, subst_vars)
250 ldb.modify_ldif(data)
253 def setup_ldb(ldb, ldif_path, subst_vars):
254 """Import a LDIF a file into a LDB handle, optionally substituting variables.
256 :note: Either all LDIF data will be added or none (using transactions).
258 :param ldb: LDB file to import into.
259 :param ldif_path: Path to the LDIF file.
260 :param subst_vars: Dictionary with substitution variables.
262 assert ldb is not None
263 ldb.transaction_start()
265 setup_add_ldif(ldb, ldif_path, subst_vars)
267 ldb.transaction_cancel()
269 ldb.transaction_commit()
272 def provision_paths_from_lp(lp, dnsdomain):
273 """Set the default paths for provisioning.
275 :param lp: Loadparm context.
276 :param dnsdomain: DNS Domain name
278 paths = ProvisionPaths()
279 paths.private_dir = lp.get("private dir")
280 paths.dns_keytab = "dns.keytab"
282 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
283 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
284 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
285 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
286 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
287 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
288 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
289 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
290 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
291 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
292 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
293 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
294 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
295 paths.phpldapadminconfig = os.path.join(paths.private_dir,
296 "phpldapadmin-config.php")
297 paths.hklm = "hklm.ldb"
298 paths.hkcr = "hkcr.ldb"
299 paths.hkcu = "hkcu.ldb"
300 paths.hku = "hku.ldb"
301 paths.hkpd = "hkpd.ldb"
302 paths.hkpt = "hkpt.ldb"
303 paths.sysvol = lp.get("path", "sysvol")
304 paths.netlogon = lp.get("path", "netlogon")
305 paths.smbconf = lp.configfile
309 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
310 serverrole=None, rootdn=None, domaindn=None, configdn=None,
311 schemadn=None, serverdn=None, sitename=None):
312 """Guess configuration settings to use."""
315 hostname = socket.gethostname().split(".")[0]
317 netbiosname = lp.get("netbios name")
318 if netbiosname is None:
319 netbiosname = hostname
320 assert netbiosname is not None
321 netbiosname = netbiosname.upper()
322 if not valid_netbios_name(netbiosname):
323 raise InvalidNetbiosName(netbiosname)
325 if dnsdomain is None:
326 dnsdomain = lp.get("realm")
327 if dnsdomain is None or dnsdomain == "":
328 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
330 dnsdomain = dnsdomain.lower()
332 if serverrole is None:
333 serverrole = lp.get("server role")
334 if serverrole is None:
335 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
337 serverrole = serverrole.lower()
339 realm = dnsdomain.upper()
341 if lp.get("realm") == "":
342 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
344 if lp.get("realm").upper() != realm:
345 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))
347 if lp.get("server role").lower() != serverrole:
348 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))
350 if serverrole == "domain controller":
352 # This will, for better or worse, default to 'WORKGROUP'
353 domain = lp.get("workgroup")
354 domain = domain.upper()
356 if lp.get("workgroup").upper() != domain:
357 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))
360 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
364 domaindn = "DC=" + netbiosname
366 if not valid_netbios_name(domain):
367 raise InvalidNetbiosName(domain)
369 if hostname.upper() == realm:
370 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
371 if netbiosname == realm:
372 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
374 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
380 configdn = "CN=Configuration," + rootdn
382 schemadn = "CN=Schema," + configdn
387 names = ProvisionNames()
388 names.rootdn = rootdn
389 names.domaindn = domaindn
390 names.configdn = configdn
391 names.schemadn = schemadn
392 names.ldapmanagerdn = "CN=Manager," + rootdn
393 names.dnsdomain = dnsdomain
394 names.domain = domain
396 names.netbiosname = netbiosname
397 names.hostname = hostname
398 names.sitename = sitename
399 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
404 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
405 targetdir, sid_generator,eadb):
406 """Create a new smb.conf file based on a couple of basic settings.
408 assert smbconf is not None
410 hostname = socket.gethostname().split(".")[0]
411 netbiosname = hostname.upper()
413 if serverrole is None:
414 serverrole = "standalone"
416 assert serverrole in ("domain controller", "member server", "standalone")
417 if serverrole == "domain controller":
419 elif serverrole == "member server":
420 smbconfsuffix = "member"
421 elif serverrole == "standalone":
422 smbconfsuffix = "standalone"
424 if sid_generator is None:
425 sid_generator = "internal"
427 assert domain is not None
428 domain = domain.upper()
430 assert realm is not None
431 realm = realm.upper()
433 default_lp = param.LoadParm()
434 #Load non-existant file
435 if os.path.exists(smbconf):
436 default_lp.load(smbconf)
438 if targetdir is not None:
439 privdir = os.path.join(targetdir, "private")
441 privdir = default_lp.get("private dir")
442 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
446 if targetdir is not None:
447 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
448 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
450 default_lp.set("lock dir", os.path.abspath(targetdir))
455 if sid_generator == "internal":
456 sid_generator_line = ""
458 sid_generator_line = "sid generator = " + sid_generator
460 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
461 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
463 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
465 "NETBIOS_NAME": netbiosname,
468 "SERVERROLE": serverrole,
469 "NETLOGONPATH": netlogon,
470 "SYSVOLPATH": sysvol,
471 "SIDGENERATOR_LINE": sid_generator_line,
472 "PRIVATEDIR_LINE": privatedir_line,
473 "LOCKDIR_LINE": lockdir_line,
474 "POSIXEADB_LINE": posixeadb_line
478 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
479 users_gid, wheel_gid):
480 """setup reasonable name mappings for sam names to unix names.
482 :param samdb: SamDB object.
483 :param idmap: IDmap db object.
484 :param sid: The domain sid.
485 :param domaindn: The domain DN.
486 :param root_uid: uid of the UNIX root user.
487 :param nobody_uid: uid of the UNIX nobody user.
488 :param users_gid: gid of the UNIX users group.
489 :param wheel_gid: gid of the UNIX wheel group."""
491 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
492 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
494 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
495 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
497 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
498 provision_backend, names, schema,
501 """Setup the partitions for the SAM database.
503 Alternatively, provision() may call this, and then populate the database.
505 :note: This will wipe the Sam Database!
507 :note: This function always removes the local SAM LDB file. The erase
508 parameter controls whether to erase the existing data, which
509 may not be stored locally but in LDAP.
512 assert session_info is not None
514 # We use options=["modules:"] to stop the modules loading - we
515 # just want to wipe and re-initialise the database, not start it up
518 os.unlink(samdb_path)
522 samdb = Ldb(url=samdb_path, session_info=session_info,
523 lp=lp, options=["modules:"])
525 ldap_backend_line = "# No LDAP backend"
526 if provision_backend.type is not "ldb":
527 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
529 samdb.transaction_start()
531 message("Setting up sam.ldb partitions and settings")
532 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
533 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
534 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
535 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
536 "LDAP_BACKEND_LINE": ldap_backend_line,
540 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
541 "BACKEND_TYPE": provision_backend.type,
542 "SERVER_ROLE": serverrole
545 message("Setting up sam.ldb rootDSE")
546 setup_samdb_rootdse(samdb, setup_path, names)
549 samdb.transaction_cancel()
552 samdb.transaction_commit()
555 def secretsdb_self_join(secretsdb, domain,
556 netbiosname, machinepass, domainsid=None,
557 realm=None, dnsdomain=None,
559 key_version_number=1,
560 secure_channel_type=SEC_CHAN_WKSTA):
561 """Add domain join-specific bits to a secrets database.
563 :param secretsdb: Ldb Handle to the secrets database
564 :param machinepass: Machine password
566 attrs=["whenChanged",
574 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
575 msg["secureChannelType"] = str(secure_channel_type)
576 msg["flatname"] = [domain]
577 msg["objectClass"] = ["top", "primaryDomain"]
578 if realm is not None:
579 if dnsdomain is None:
580 dnsdomain = realm.lower()
581 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
583 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
584 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
585 msg["privateKeytab"] = ["secrets.keytab"]
588 msg["secret"] = [machinepass]
589 msg["samAccountName"] = ["%s$" % netbiosname]
590 msg["secureChannelType"] = [str(secure_channel_type)]
591 if domainsid is not None:
592 msg["objectSid"] = [ndr_pack(domainsid)]
594 res = secretsdb.search(base="cn=Primary Domains",
596 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
597 scope=ldb.SCOPE_ONELEVEL)
600 if del_msg.dn is not msg.dn:
601 secretsdb.delete(del_msg.dn)
603 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
606 msg["priorSecret"] = res[0]["secret"]
607 msg["priorWhenChanged"] = res[0]["whenChanged"]
609 if res["privateKeytab"] is not None:
610 msg["privateKeytab"] = res[0]["privateKeytab"]
612 if res["krb5Keytab"] is not None:
613 msg["krb5Keytab"] = res[0]["krb5Keytab"]
616 el.set_flags(ldb.FLAG_MOD_REPLACE)
617 secretsdb.modify(msg)
622 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
624 dns_keytab_path, dnspass):
625 """Add DNS specific bits to a secrets database.
627 :param secretsdb: Ldb Handle to the secrets database
628 :param setup_path: Setup path function
629 :param machinepass: Machine password
632 os.unlink(os.path.join(private_dir, dns_keytab_path))
636 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
638 "DNSDOMAIN": dnsdomain,
639 "DNS_KEYTAB": dns_keytab_path,
640 "DNSPASS_B64": b64encode(dnspass),
644 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
645 """Setup the secrets database.
647 :param path: Path to the secrets database.
648 :param setup_path: Get the path to a setup file.
649 :param session_info: Session info.
650 :param credentials: Credentials
651 :param lp: Loadparm context
652 :return: LDB handle for the created secrets database
654 if os.path.exists(path):
656 secrets_ldb = Ldb(path, session_info=session_info,
659 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
660 secrets_ldb = Ldb(path, session_info=session_info,
662 secrets_ldb.transaction_start()
663 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
665 if backend_credentials is not None and backend_credentials.authentication_requested():
666 if backend_credentials.get_bind_dn() is not None:
667 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
668 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
669 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
672 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
673 "LDAPADMINUSER": backend_credentials.get_username(),
674 "LDAPADMINREALM": backend_credentials.get_realm(),
675 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
680 def setup_privileges(path, setup_path, session_info, lp):
681 """Setup the privileges database.
683 :param path: Path to the privileges database.
684 :param setup_path: Get the path to a setup file.
685 :param session_info: Session info.
686 :param credentials: Credentials
687 :param lp: Loadparm context
688 :return: LDB handle for the created secrets database
690 if os.path.exists(path):
692 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
693 privilege_ldb.erase()
694 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
697 def setup_registry(path, setup_path, session_info, lp):
698 """Setup the registry.
700 :param path: Path to the registry database
701 :param setup_path: Function that returns the path to a setup.
702 :param session_info: Session information
703 :param credentials: Credentials
704 :param lp: Loadparm context
706 reg = registry.Registry()
707 hive = registry.open_ldb(path, session_info=session_info,
709 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
710 provision_reg = setup_path("provision.reg")
711 assert os.path.exists(provision_reg)
712 reg.diff_apply(provision_reg)
715 def setup_idmapdb(path, setup_path, session_info, lp):
716 """Setup the idmap database.
718 :param path: path to the idmap database
719 :param setup_path: Function that returns a path to a setup file
720 :param session_info: Session information
721 :param credentials: Credentials
722 :param lp: Loadparm context
724 if os.path.exists(path):
727 idmap_ldb = IDmapDB(path, session_info=session_info,
731 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
735 def setup_samdb_rootdse(samdb, setup_path, names):
736 """Setup the SamDB rootdse.
738 :param samdb: Sam Database handle
739 :param setup_path: Obtain setup path
741 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
742 "SCHEMADN": names.schemadn,
743 "NETBIOSNAME": names.netbiosname,
744 "DNSDOMAIN": names.dnsdomain,
745 "REALM": names.realm,
746 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
747 "DOMAINDN": names.domaindn,
748 "ROOTDN": names.rootdn,
749 "CONFIGDN": names.configdn,
750 "SERVERDN": names.serverdn,
754 def setup_self_join(samdb, names,
755 machinepass, dnspass,
756 domainsid, invocationid, setup_path,
757 policyguid, policyguid_dc, domainControllerFunctionality,
759 """Join a host to its own domain."""
760 assert isinstance(invocationid, str)
761 if ntdsguid is not None:
762 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
765 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
766 "CONFIGDN": names.configdn,
767 "SCHEMADN": names.schemadn,
768 "DOMAINDN": names.domaindn,
769 "SERVERDN": names.serverdn,
770 "INVOCATIONID": invocationid,
771 "NETBIOSNAME": names.netbiosname,
772 "DEFAULTSITE": names.sitename,
773 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
774 "MACHINEPASS_B64": b64encode(machinepass),
775 "REALM": names.realm,
776 "DOMAIN": names.domain,
777 "DOMAINSID": str(domainsid),
778 "DNSDOMAIN": names.dnsdomain,
779 "SAMBA_VERSION_STRING": version,
780 "NTDSGUID": ntdsguid_line,
781 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
783 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
784 "POLICYGUID": policyguid,
785 "POLICYGUID_DC": policyguid_dc,
786 "DNSDOMAIN": names.dnsdomain,
787 "DOMAINSID": str(domainsid),
788 "DOMAINDN": names.domaindn})
790 # add the NTDSGUID based SPNs
791 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
792 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
793 expression="", scope=ldb.SCOPE_BASE)
794 assert isinstance(names.ntdsguid, str)
796 # Setup fSMORoleOwner entries to point at the newly created DC entry
797 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
798 "DOMAIN": names.domain,
799 "DNSDOMAIN": names.dnsdomain,
800 "DOMAINDN": names.domaindn,
801 "CONFIGDN": names.configdn,
802 "SCHEMADN": names.schemadn,
803 "DEFAULTSITE": names.sitename,
804 "SERVERDN": names.serverdn,
805 "NETBIOSNAME": names.netbiosname,
806 "NTDSGUID": names.ntdsguid,
807 "DNSPASS_B64": b64encode(dnspass),
810 def getpolicypath(sysvolpath, dnsdomain, guid):
813 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
816 def create_gpo_struct(policy_path):
817 os.makedirs(policy_path, 0755)
818 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
819 "[General]\r\nVersion=65543")
820 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
821 os.makedirs(os.path.join(policy_path, "USER"), 0755)
823 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
824 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
825 create_gpo_struct(policy_path)
827 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
828 create_gpo_struct(policy_path)
830 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
832 domainsid, domainguid, policyguid, policyguid_dc,
833 fill, adminpass, krbtgtpass,
834 machinepass, invocationid, dnspass, ntdsguid,
835 serverrole, dom_for_fun_level=None,
837 """Setup a complete SAM Database.
839 :note: This will wipe the main SAM database file!
842 # ATTENTION: Do NOT change these default values without discussion with the
843 # team and/or release manager. They have a big impact on the whole program!
844 domainControllerFunctionality = DS_DC_FUNCTION_2008
846 if dom_for_fun_level is None:
847 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
848 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
849 message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
851 if dom_for_fun_level > domainControllerFunctionality:
852 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!")
854 domainFunctionality = dom_for_fun_level
855 forestFunctionality = dom_for_fun_level
857 # Also wipes the database
858 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
859 provision_backend=provision_backend, session_info=session_info,
861 serverrole=serverrole, schema=schema)
864 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
866 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
867 samdb = Ldb(session_info=session_info,
868 credentials=provision_backend.credentials, lp=lp)
870 message("Pre-loading the Samba 4 and AD schema")
872 # Load the schema from the one we computed earlier
873 samdb.set_schema_from_ldb(schema.ldb)
875 # And now we can connect to the DB - the schema won't be loaded from the DB
881 samdb.transaction_start()
883 # Set the domain functionality levels onto the database.
884 # Various module (the password_hash module in particular) need
885 # to know what level of AD we are emulating.
887 # These will be fixed into the database via the database
888 # modifictions below, but we need them set from the start.
889 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
890 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
891 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
893 samdb.set_domain_sid(str(domainsid))
894 samdb.set_invocation_id(invocationid)
896 message("Adding DomainDN: %s" % names.domaindn)
898 #impersonate domain admin
899 admin_session_info = admin_session(lp, str(domainsid))
900 samdb.set_session_info(admin_session_info)
901 if domainguid is not None:
902 domainguid_line = "objectGUID: %s\n-" % domainguid
906 descr = b64encode(get_domain_descriptor(domainsid))
907 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
908 "DOMAINDN": names.domaindn,
909 "DOMAINGUID": domainguid_line,
914 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
915 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
916 "DOMAINSID": str(domainsid),
917 "SCHEMADN": names.schemadn,
918 "NETBIOSNAME": names.netbiosname,
919 "DEFAULTSITE": names.sitename,
920 "CONFIGDN": names.configdn,
921 "SERVERDN": names.serverdn,
922 "POLICYGUID": policyguid,
923 "DOMAINDN": names.domaindn,
924 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
925 "SAMBA_VERSION_STRING": version
928 message("Adding configuration container")
929 descr = b64encode(get_config_descriptor(domainsid))
930 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
931 "CONFIGDN": names.configdn,
935 # The LDIF here was created when the Schema object was constructed
936 message("Setting up sam.ldb schema")
937 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
938 samdb.modify_ldif(schema.schema_dn_modify)
939 samdb.write_prefixes_from_schema()
940 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
941 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
942 {"SCHEMADN": names.schemadn})
944 message("Reopening sam.ldb with new schema")
945 samdb.transaction_commit()
946 samdb = Ldb(session_info=admin_session_info,
947 credentials=provision_backend.credentials, lp=lp)
949 samdb.transaction_start()
950 samdb.set_invocation_id(invocationid)
952 message("Setting up sam.ldb configuration data")
953 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
954 "CONFIGDN": names.configdn,
955 "NETBIOSNAME": names.netbiosname,
956 "DEFAULTSITE": names.sitename,
957 "DNSDOMAIN": names.dnsdomain,
958 "DOMAIN": names.domain,
959 "SCHEMADN": names.schemadn,
960 "DOMAINDN": names.domaindn,
961 "SERVERDN": names.serverdn,
962 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
965 message("Setting up display specifiers")
966 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
967 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
968 check_all_substituted(display_specifiers_ldif)
969 samdb.add_ldif(display_specifiers_ldif)
971 message("Adding users container")
972 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
973 "DOMAINDN": names.domaindn})
974 message("Modifying users container")
975 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
976 "DOMAINDN": names.domaindn})
977 message("Adding computers container")
978 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
979 "DOMAINDN": names.domaindn})
980 message("Modifying computers container")
981 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
982 "DOMAINDN": names.domaindn})
983 message("Setting up sam.ldb data")
984 setup_add_ldif(samdb, setup_path("provision.ldif"), {
985 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
986 "DOMAINDN": names.domaindn,
987 "NETBIOSNAME": names.netbiosname,
988 "DEFAULTSITE": names.sitename,
989 "CONFIGDN": names.configdn,
990 "SERVERDN": names.serverdn,
991 "POLICYGUID_DC": policyguid_dc
994 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
995 "DOMAINDN": names.domaindn})
997 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
998 "CONFIGDN": names.configdn,
999 "SCHEMADN": names.schemadn})
1000 if fill == FILL_FULL:
1001 message("Setting up sam.ldb users and groups")
1002 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1003 "DOMAINDN": names.domaindn,
1004 "DOMAINSID": str(domainsid),
1005 "CONFIGDN": names.configdn,
1006 "ADMINPASS_B64": b64encode(adminpass),
1007 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1010 message("Setting up self join")
1011 setup_self_join(samdb, names=names, invocationid=invocationid,
1013 machinepass=machinepass,
1014 domainsid=domainsid, policyguid=policyguid,
1015 policyguid_dc=policyguid_dc,
1016 setup_path=setup_path,
1017 domainControllerFunctionality=domainControllerFunctionality,
1020 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1021 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1022 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1023 assert isinstance(names.ntdsguid, str)
1026 samdb.transaction_cancel()
1029 samdb.transaction_commit()
1034 FILL_NT4SYNC = "NT4SYNC"
1036 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1037 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)"
1039 def set_dir_acl(path, acl, lp, domsid):
1040 setntacl(lp, path, acl, domsid)
1041 for root, dirs, files in os.walk(path, topdown=False):
1043 setntacl(lp, os.path.join(root, name), acl, domsid)
1045 setntacl(lp, os.path.join(root, name), acl, domsid)
1048 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1050 policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1051 set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
1053 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1054 attrs=["cn","nTSecurityDescriptor"],
1055 expression="", scope=ldb.SCOPE_ONELEVEL)
1057 acl = ndr_unpack(security.descriptor,str(policy["nTSecurityDescriptor"])).as_sddl()
1058 policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1059 set_dir_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
1061 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1064 os.chown(sysvol,-1,gid)
1070 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1071 for root, dirs, files in os.walk(sysvol, topdown=False):
1074 os.chown(os.path.join(root, name),-1,gid)
1075 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1078 os.chown(os.path.join(root, name),-1,gid)
1079 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1080 set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1083 def provision(setup_dir, message, session_info,
1084 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1086 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1088 domain=None, hostname=None, hostip=None, hostip6=None,
1089 domainsid=None, adminpass=None, ldapadminpass=None,
1090 krbtgtpass=None, domainguid=None,
1091 policyguid=None, policyguid_dc=None, invocationid=None,
1092 machinepass=None, ntdsguid=None,
1093 dnspass=None, root=None, nobody=None, users=None,
1094 wheel=None, backup=None, aci=None, serverrole=None,
1095 dom_for_fun_level=None,
1096 ldap_backend_extra_port=None, backend_type=None,
1098 ol_mmr_urls=None, ol_olc=None,
1099 setup_ds_path=None, slapd_path=None, nosync=False,
1100 ldap_dryrun_mode=False,useeadb=False):
1103 :note: caution, this wipes all existing data!
1106 def setup_path(file):
1107 return os.path.join(setup_dir, file)
1109 if domainsid is None:
1110 domainsid = security.random_sid()
1112 domainsid = security.dom_sid(domainsid)
1114 # create/adapt the group policy GUIDs
1115 if policyguid is None:
1116 policyguid = str(uuid.uuid4())
1117 policyguid = policyguid.upper()
1118 if policyguid_dc is None:
1119 policyguid_dc = str(uuid.uuid4())
1120 policyguid_dc = policyguid_dc.upper()
1122 if adminpass is None:
1123 adminpass = glue.generate_random_password(12, 32)
1124 if krbtgtpass is None:
1125 krbtgtpass = glue.generate_random_password(128, 255)
1126 if machinepass is None:
1127 machinepass = glue.generate_random_password(128, 255)
1129 dnspass = glue.generate_random_password(128, 255)
1130 if ldapadminpass is None:
1131 #Make a new, random password between Samba and it's LDAP server
1132 ldapadminpass=glue.generate_random_password(128, 255)
1134 if backend_type is None:
1135 backend_type = "ldb"
1137 sid_generator = "internal"
1138 if backend_type == "fedora-ds":
1139 sid_generator = "backend"
1141 root_uid = findnss_uid([root or "root"])
1142 nobody_uid = findnss_uid([nobody or "nobody"])
1143 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1145 wheel_gid = findnss_gid(["wheel", "adm"])
1147 wheel_gid = findnss_gid([wheel])
1149 bind_gid = findnss_gid(["bind", "named"])
1153 if targetdir is not None:
1154 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1155 os.makedirs(os.path.join(targetdir, "etc"))
1156 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1157 elif smbconf is None:
1158 smbconf = param.default_path()
1160 # only install a new smb.conf if there isn't one there already
1161 if os.path.exists(smbconf):
1162 # if Samba Team members can't figure out the weird errors
1163 # loading an empty smb.conf gives, then we need to be smarter.
1164 # Pretend it just didn't exist --abartlet
1165 data = open(smbconf, 'r').read()
1166 data = data.lstrip()
1167 if data is None or data == "":
1168 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1169 targetdir, sid_generator, useeadb)
1171 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1172 targetdir, sid_generator, useeadb)
1174 lp = param.LoadParm()
1177 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1178 dnsdomain=realm, serverrole=serverrole,
1179 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1180 serverdn=serverdn, sitename=sitename)
1182 paths = provision_paths_from_lp(lp, names.dnsdomain)
1184 paths.bind_gid = bind_gid
1187 hostips = glue.interface_ips(lp, False)
1188 if len(hostips) == 0:
1189 message("No external IPv4 address has been found: I use the loopback.")
1190 hostip = '127.0.0.1'
1193 if len(hostips) > 1:
1194 message("More than one IPv4 address found: I use " + hostip + ".")
1198 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1201 if hostip6 == '::1' and ip[-1][0] != '::1':
1203 except socket.gaierror, (socket.EAI_NODATA, msg):
1206 if serverrole is None:
1207 serverrole = lp.get("server role")
1209 assert serverrole in ("domain controller", "member server", "standalone")
1210 if invocationid is None:
1211 invocationid = str(uuid.uuid4())
1213 if not os.path.exists(paths.private_dir):
1214 os.mkdir(paths.private_dir)
1215 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1216 os.mkdir(os.path.join(paths.private_dir,"tls"))
1218 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1220 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1222 if backend_type == "ldb":
1223 provision_backend = LDBBackend(backend_type,
1224 paths=paths, setup_path=setup_path,
1225 lp=lp, credentials=credentials,
1228 elif backend_type == "existing":
1229 provision_backend = ExistingBackend(backend_type,
1230 paths=paths, setup_path=setup_path,
1231 lp=lp, credentials=credentials,
1234 ldapi_url=ldapi_url)
1235 elif backend_type == "fedora-ds":
1236 provision_backend = FDSBackend(backend_type,
1237 paths=paths, setup_path=setup_path,
1238 lp=lp, credentials=credentials,
1241 domainsid=domainsid,
1244 ldapadminpass=ldapadminpass,
1245 slapd_path=slapd_path,
1246 ldap_backend_extra_port=ldap_backend_extra_port,
1247 ldap_dryrun_mode=ldap_dryrun_mode,
1249 setup_ds_path=setup_ds_path)
1250 elif backend_type == "openldap":
1251 provision_backend = OpenLDAPBackend(backend_type,
1252 paths=paths, setup_path=setup_path,
1253 lp=lp, credentials=credentials,
1256 domainsid=domainsid,
1259 ldapadminpass=ldapadminpass,
1260 slapd_path=slapd_path,
1261 ldap_backend_extra_port=ldap_backend_extra_port,
1262 ldap_dryrun_mode=ldap_dryrun_mode,
1263 ol_mmr_urls=ol_mmr_urls,
1266 raise ProvisioningError("Unknown LDAP backend type selected")
1268 provision_backend.init()
1269 provision_backend.start()
1271 # only install a new shares config db if there is none
1272 if not os.path.exists(paths.shareconf):
1273 message("Setting up share.ldb")
1274 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1276 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1279 message("Setting up secrets.ldb")
1280 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1281 session_info=session_info,
1282 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1284 message("Setting up the registry")
1285 setup_registry(paths.hklm, setup_path, session_info,
1288 message("Setting up the privileges database")
1289 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1291 message("Setting up idmap db")
1292 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1295 message("Setting up SAM db")
1296 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1297 provision_backend, lp, names,
1299 domainsid=domainsid,
1300 schema=schema, domainguid=domainguid,
1301 policyguid=policyguid, policyguid_dc=policyguid_dc,
1303 adminpass=adminpass, krbtgtpass=krbtgtpass,
1304 invocationid=invocationid,
1305 machinepass=machinepass, dnspass=dnspass,
1306 ntdsguid=ntdsguid, serverrole=serverrole,
1307 dom_for_fun_level=dom_for_fun_level)
1309 if serverrole == "domain controller":
1310 if paths.netlogon is None:
1311 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1312 message("Please either remove %s or see the template at %s" %
1313 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1314 assert(paths.netlogon is not None)
1316 if paths.sysvol is None:
1317 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1318 message("Please either remove %s or see the template at %s" %
1319 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1320 assert(paths.sysvol is not None)
1323 if not os.path.isdir(paths.netlogon):
1324 os.makedirs(paths.netlogon, 0755)
1326 if samdb_fill == FILL_FULL:
1327 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1328 root_uid=root_uid, nobody_uid=nobody_uid,
1329 users_gid=users_gid, wheel_gid=wheel_gid)
1331 if serverrole == "domain controller":
1332 # Set up group policies (domain policy and domain controller policy)
1333 setup_gpo(paths.sysvol,names.dnsdomain,policyguid,policyguid_dc)
1334 setsysvolacl(samdb,paths.netlogon,paths.sysvol,wheel_gid,domainsid,names.dnsdomain,names.domaindn,lp)
1336 message("Setting up sam.ldb rootDSE marking as synchronized")
1337 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1339 secretsdb_self_join(secrets_ldb, domain=names.domain,
1341 dnsdomain=names.dnsdomain,
1342 netbiosname=names.netbiosname,
1343 domainsid=domainsid,
1344 machinepass=machinepass,
1345 secure_channel_type=SEC_CHAN_BDC)
1347 if serverrole == "domain controller":
1348 secretsdb_setup_dns(secrets_ldb, setup_path,
1350 realm=names.realm, dnsdomain=names.dnsdomain,
1351 dns_keytab_path=paths.dns_keytab,
1354 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1355 assert isinstance(domainguid, str)
1357 # Only make a zone file on the first DC, it should be replicated
1358 # with DNS replication
1359 create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain=names.dnsdomain,
1361 hostip6=hostip6, hostname=names.hostname,
1363 domainguid=domainguid, ntdsguid=names.ntdsguid)
1365 create_named_conf(paths, setup_path, realm=names.realm,
1366 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1368 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1369 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1370 keytab_name=paths.dns_keytab)
1371 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1372 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1374 create_krb5_conf(paths.krb5conf, setup_path,
1375 dnsdomain=names.dnsdomain, hostname=names.hostname,
1377 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1379 if serverrole == "domain controller":
1380 create_dns_update_list(lp, message, paths, setup_path)
1382 provision_backend.post_setup()
1383 provision_backend.shutdown()
1385 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1388 #Now commit the secrets.ldb to disk
1389 secrets_ldb.transaction_commit()
1391 # the commit creates the dns.keytab, now chown it
1392 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1393 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1395 os.chmod(dns_keytab_path, 0640)
1396 os.chown(dns_keytab_path, -1, paths.bind_gid)
1398 message("Failed to chown %s to bind gid %u" % (dns_keytab_path, paths.bind_gid))
1401 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1403 message("Once the above files are installed, your Samba4 server will be ready to use")
1404 message("Server Role: %s" % serverrole)
1405 message("Hostname: %s" % names.hostname)
1406 message("NetBIOS Domain: %s" % names.domain)
1407 message("DNS Domain: %s" % names.dnsdomain)
1408 message("DOMAIN SID: %s" % str(domainsid))
1409 if samdb_fill == FILL_FULL:
1410 message("Admin password: %s" % adminpass)
1411 if provision_backend.type is not "ldb":
1412 if provision_backend.credentials.get_bind_dn() is not None:
1413 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1415 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1417 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1419 if provision_backend.slapd_command_escaped is not None:
1420 # now display slapd_command_file.txt to show how slapd must be started next time
1421 message("Use later the following commandline to start slapd, then Samba:")
1422 message(provision_backend.slapd_command_escaped)
1423 message("This slapd-Commandline is also stored under: " + provision_backend.ldapdir + "/ldap_backend_startup.sh")
1426 result = ProvisionResult()
1427 result.domaindn = domaindn
1428 result.paths = paths
1430 result.samdb = samdb
1435 def provision_become_dc(setup_dir=None,
1436 smbconf=None, targetdir=None, realm=None,
1437 rootdn=None, domaindn=None, schemadn=None,
1438 configdn=None, serverdn=None,
1439 domain=None, hostname=None, domainsid=None,
1440 adminpass=None, krbtgtpass=None, domainguid=None,
1441 policyguid=None, policyguid_dc=None, invocationid=None,
1443 dnspass=None, root=None, nobody=None, users=None,
1444 wheel=None, backup=None, serverrole=None,
1445 ldap_backend=None, ldap_backend_type=None,
1446 sitename=None, debuglevel=1):
1449 """print a message if quiet is not set."""
1452 glue.set_debug_level(debuglevel)
1454 return provision(setup_dir, message, system_session(), None,
1455 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1456 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1457 configdn=configdn, serverdn=serverdn, domain=domain,
1458 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1459 machinepass=machinepass, serverrole="domain controller",
1463 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1464 """Create a PHP LDAP admin configuration file.
1466 :param path: Path to write the configuration to.
1467 :param setup_path: Function to generate setup paths.
1469 setup_file(setup_path("phpldapadmin-config.php"), path,
1470 {"S4_LDAPI_URI": ldapi_uri})
1473 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1474 hostip, hostip6, hostname, realm, domainguid,
1476 """Write out a DNS zone file, from the info in the current database.
1478 :param paths: paths object
1479 :param setup_path: Setup path function.
1480 :param dnsdomain: DNS Domain name
1481 :param domaindn: DN of the Domain
1482 :param hostip: Local IPv4 IP
1483 :param hostip6: Local IPv6 IP
1484 :param hostname: Local hostname
1485 :param realm: Realm name
1486 :param domainguid: GUID of the domain.
1487 :param ntdsguid: GUID of the hosts nTDSDSA record.
1489 assert isinstance(domainguid, str)
1491 if hostip6 is not None:
1492 hostip6_base_line = " IN AAAA " + hostip6
1493 hostip6_host_line = hostname + " IN AAAA " + hostip6
1495 hostip6_base_line = ""
1496 hostip6_host_line = ""
1498 if hostip is not None:
1499 hostip_base_line = " IN A " + hostip
1500 hostip_host_line = hostname + " IN A " + hostip
1502 hostip_base_line = ""
1503 hostip_host_line = ""
1505 dns_dir = os.path.dirname(paths.dns)
1508 shutil.rmtree(dns_dir, True)
1512 os.mkdir(dns_dir, 0775)
1514 # we need to freeze the zone while we update the contents
1515 if targetdir is None:
1516 rndc = ' '.join(lp.get("rndc command"))
1517 os.system(rndc + " freeze " + lp.get("realm"))
1519 setup_file(setup_path("provision.zone"), paths.dns, {
1520 "HOSTNAME": hostname,
1521 "DNSDOMAIN": dnsdomain,
1523 "HOSTIP_BASE_LINE": hostip_base_line,
1524 "HOSTIP_HOST_LINE": hostip_host_line,
1525 "DOMAINGUID": domainguid,
1526 "DATESTRING": time.strftime("%Y%m%d%H"),
1527 "DEFAULTSITE": DEFAULTSITE,
1528 "NTDSGUID": ntdsguid,
1529 "HOSTIP6_BASE_LINE": hostip6_base_line,
1530 "HOSTIP6_HOST_LINE": hostip6_host_line,
1533 # note that we use no variable substitution on this file
1534 # the substitution is done at runtime by samba_dnsupdate
1535 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1537 if paths.bind_gid is not None:
1539 os.chown(dns_dir, -1, paths.bind_gid)
1540 os.chown(paths.dns, -1, paths.bind_gid)
1541 # chmod needed to cope with umask
1542 os.chmod(dns_dir, 0775)
1543 os.chmod(paths.dns, 0664)
1545 message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1547 if targetdir is None:
1548 os.system(rndc + " unfreeze " + lp.get("realm"))
1551 def create_dns_update_list(lp, message, paths, setup_path):
1552 """Write out a dns_update_list file"""
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)
1558 def create_named_conf(paths, setup_path, realm, dnsdomain,
1560 """Write out a file containing zone statements suitable for inclusion in a
1561 named.conf file (including GSS-TSIG configuration).
1563 :param paths: all paths
1564 :param setup_path: Setup path function.
1565 :param realm: Realm name
1566 :param dnsdomain: DNS Domain name
1567 :param private_dir: Path to private directory
1568 :param keytab_name: File name of DNS keytab file
1571 setup_file(setup_path("named.conf"), paths.namedconf, {
1572 "DNSDOMAIN": dnsdomain,
1574 "ZONE_FILE": paths.dns,
1575 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1576 "NAMED_CONF": paths.namedconf,
1577 "NAMED_CONF_UPDATE": paths.namedconf_update
1580 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1582 def create_named_txt(path, setup_path, realm, dnsdomain,
1583 private_dir, keytab_name):
1584 """Write out a file containing zone statements suitable for inclusion in a
1585 named.conf file (including GSS-TSIG configuration).
1587 :param path: Path of the new named.conf file.
1588 :param setup_path: Setup path function.
1589 :param realm: Realm name
1590 :param dnsdomain: DNS Domain name
1591 :param private_dir: Path to private directory
1592 :param keytab_name: File name of DNS keytab file
1595 setup_file(setup_path("named.txt"), path, {
1596 "DNSDOMAIN": dnsdomain,
1598 "DNS_KEYTAB": keytab_name,
1599 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1600 "PRIVATE_DIR": private_dir
1603 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
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 dnsdomain: DNS Domain name
1610 :param hostname: Local hostname
1611 :param realm: Realm name
1614 setup_file(setup_path("krb5.conf"), path, {
1615 "DNSDOMAIN": dnsdomain,
1616 "HOSTNAME": hostname,