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