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