2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
42 from samba.auth import system_session, admin_session
43 from samba import glue, version, Ldb, substitute_var, valid_netbios_name
44 from samba import check_all_substituted, read_and_sub_file, setup_file
45 from samba import DS_DOMAIN_FUNCTION_2003, DS_DC_FUNCTION_2008, DS_DC_FUNCTION_2008_R2
46 from samba.dcerpc import security
47 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
48 from samba.idmap import IDmapDB
49 from samba.ntacls import setntacl, dsacl2fsacl
50 from samba.ndr import ndr_pack,ndr_unpack
51 from samba.schema import Schema
52 from ms_display_specifiers import read_ms_ldif
53 from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
54 from provisionexceptions import ProvisioningError, InvalidNetbiosName
56 __docformat__ = "restructuredText"
59 """Find the setup directory used by provision."""
60 dirname = os.path.dirname(__file__)
61 if "/site-packages/" in dirname:
62 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
63 for suffix in ["share/setup", "share/samba/setup", "setup"]:
64 ret = os.path.join(prefix, suffix)
65 if os.path.isdir(ret):
68 ret = os.path.join(dirname, "../../../setup")
69 if os.path.isdir(ret):
71 raise Exception("Unable to find setup directory.")
73 # descriptors of the naming contexts
74 # hard coded at this point, but will probably be changed when
75 # we enable different fsmo roles
77 def get_config_descriptor(domain_sid):
78 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
79 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
81 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
84 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
85 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
86 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
87 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
88 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
89 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
90 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
91 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
92 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
93 sec = security.descriptor.from_sddl(sddl, domain_sid)
96 def get_domain_descriptor(domain_sid):
97 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
98 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
99 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
100 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
101 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
102 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
103 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
104 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
105 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
106 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
107 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
108 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
109 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
110 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
111 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
112 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
113 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
114 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
119 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
120 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
121 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
122 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
123 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
125 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
126 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
127 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
128 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
129 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
133 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
134 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
135 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
138 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
140 "(A;;RPLCLORC;;;ED)" \
141 "(A;;RPLCLORC;;;AU)" \
142 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
143 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
144 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
145 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
146 sec = security.descriptor.from_sddl(sddl, domain_sid)
149 DEFAULTSITE = "Default-First-Site-Name"
153 class ProvisionPaths(object):
155 self.shareconf = None
166 self.dns_keytab = None
169 self.private_dir = None
171 self.slapdconf = None
172 self.modulesconf = None
173 self.memberofconf = None
175 self.olmmrserveridsconf = None
176 self.olmmrsyncreplconf = None
179 self.olcseedldif = None
182 class ProvisionNames(object):
188 self.ldapmanagerdn = None
189 self.dnsdomain = None
191 self.netbiosname = None
198 class ProvisionResult(object):
205 def check_install(lp, session_info, credentials):
206 """Check whether the current install seems ok.
208 :param lp: Loadparm context
209 :param session_info: Session information
210 :param credentials: Credentials
212 if lp.get("realm") == "":
213 raise Exception("Realm empty")
214 ldb = Ldb(lp.get("sam database"), session_info=session_info,
215 credentials=credentials, lp=lp)
216 if len(ldb.search("(cn=Administrator)")) != 1:
217 raise ProvisioningError("No administrator account found")
220 def findnss(nssfn, names):
221 """Find a user or group from a list of possibilities.
223 :param nssfn: NSS Function to try (should raise KeyError if not found)
224 :param names: Names to check.
225 :return: Value return by first names list.
232 raise KeyError("Unable to find user/group %r" % names)
235 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
236 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
239 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
240 """Setup a ldb in the private dir.
242 :param ldb: LDB file to import data into
243 :param ldif_path: Path of the LDIF file to load
244 :param subst_vars: Optional variables to subsitute in LDIF.
245 :param nocontrols: Optional list of controls, can be None for no controls
247 assert isinstance(ldif_path, str)
248 data = read_and_sub_file(ldif_path, subst_vars)
249 ldb.add_ldif(data,controls)
252 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
253 """Modify a ldb in the private dir.
255 :param ldb: LDB object.
256 :param ldif_path: LDIF file path.
257 :param subst_vars: Optional dictionary with substitution variables.
259 data = read_and_sub_file(ldif_path, subst_vars)
261 ldb.modify_ldif(data)
264 def setup_ldb(ldb, ldif_path, subst_vars):
265 """Import a LDIF a file into a LDB handle, optionally substituting variables.
267 :note: Either all LDIF data will be added or none (using transactions).
269 :param ldb: LDB file to import into.
270 :param ldif_path: Path to the LDIF file.
271 :param subst_vars: Dictionary with substitution variables.
273 assert ldb is not None
274 ldb.transaction_start()
276 setup_add_ldif(ldb, ldif_path, subst_vars)
278 ldb.transaction_cancel()
280 ldb.transaction_commit()
283 def provision_paths_from_lp(lp, dnsdomain):
284 """Set the default paths for provisioning.
286 :param lp: Loadparm context.
287 :param dnsdomain: DNS Domain name
289 paths = ProvisionPaths()
290 paths.private_dir = lp.get("private dir")
291 paths.dns_keytab = "dns.keytab"
293 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
294 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
295 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
296 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
297 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
298 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
299 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
300 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
301 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
302 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
303 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
304 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
305 paths.phpldapadminconfig = os.path.join(paths.private_dir,
306 "phpldapadmin-config.php")
307 paths.ldapdir = os.path.join(paths.private_dir,
309 paths.slapdconf = os.path.join(paths.ldapdir,
311 paths.slapdpid = os.path.join(paths.ldapdir,
313 paths.modulesconf = os.path.join(paths.ldapdir,
315 paths.memberofconf = os.path.join(paths.ldapdir,
317 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
318 "mmr_serverids.conf")
319 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
321 paths.olcdir = os.path.join(paths.ldapdir,
323 paths.olcseedldif = os.path.join(paths.ldapdir,
325 paths.hklm = "hklm.ldb"
326 paths.hkcr = "hkcr.ldb"
327 paths.hkcu = "hkcu.ldb"
328 paths.hku = "hku.ldb"
329 paths.hkpd = "hkpd.ldb"
330 paths.hkpt = "hkpt.ldb"
332 paths.sysvol = lp.get("path", "sysvol")
334 paths.netlogon = lp.get("path", "netlogon")
336 paths.smbconf = lp.configfile
341 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
342 serverrole=None, rootdn=None, domaindn=None, configdn=None,
343 schemadn=None, serverdn=None, sitename=None):
344 """Guess configuration settings to use."""
347 hostname = socket.gethostname().split(".")[0]
349 netbiosname = lp.get("netbios name")
350 if netbiosname is None:
351 netbiosname = hostname
352 assert netbiosname is not None
353 netbiosname = netbiosname.upper()
354 if not valid_netbios_name(netbiosname):
355 raise InvalidNetbiosName(netbiosname)
357 if dnsdomain is None:
358 dnsdomain = lp.get("realm")
359 if dnsdomain is None or dnsdomain == "":
360 raise ProvisioningError("guess_names: 'realm' not specified in supplied smb.conf!")
362 dnsdomain = dnsdomain.lower()
364 if serverrole is None:
365 serverrole = lp.get("server role")
366 if serverrole is None:
367 raise ProvisioningError("guess_names: 'server role' not specified in supplied smb.conf!")
369 serverrole = serverrole.lower()
371 realm = dnsdomain.upper()
373 if lp.get("realm").upper() != realm:
374 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
376 if lp.get("server role").lower() != serverrole:
377 raise ProvisioningError("guess_names: server role '%s' in smb.conf must match chosen server role '%s'!", lp.get("server role").upper(), serverrole)
379 if serverrole == "domain controller":
381 domain = lp.get("workgroup")
383 raise ProvisioningError("guess_names: 'workgroup' not specified in supplied smb.conf!")
384 domain = domain.upper()
386 if lp.get("workgroup").upper() != domain:
387 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
390 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
394 domaindn = "DC=" + netbiosname
396 if not valid_netbios_name(domain):
397 raise InvalidNetbiosName(domain)
399 if hostname.upper() == realm:
400 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
401 if netbiosname == realm:
402 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
404 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
410 configdn = "CN=Configuration," + rootdn
412 schemadn = "CN=Schema," + configdn
417 names = ProvisionNames()
418 names.rootdn = rootdn
419 names.domaindn = domaindn
420 names.configdn = configdn
421 names.schemadn = schemadn
422 names.ldapmanagerdn = "CN=Manager," + rootdn
423 names.dnsdomain = dnsdomain
424 names.domain = domain
426 names.netbiosname = netbiosname
427 names.hostname = hostname
428 names.sitename = sitename
429 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
434 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
435 targetdir, sid_generator,eadb):
436 """Create a new smb.conf file based on a couple of basic settings.
438 assert smbconf is not None
440 hostname = socket.gethostname().split(".")[0]
441 netbiosname = hostname.upper()
443 if serverrole is None:
444 serverrole = "standalone"
446 assert serverrole in ("domain controller", "member server", "standalone")
447 if serverrole == "domain controller":
449 elif serverrole == "member server":
450 smbconfsuffix = "member"
451 elif serverrole == "standalone":
452 smbconfsuffix = "standalone"
454 if sid_generator is None:
455 sid_generator = "internal"
457 assert domain is not None
458 domain = domain.upper()
460 assert realm is not None
461 realm = realm.upper()
463 default_lp = param.LoadParm()
464 #Load non-existant file
465 if os.path.exists(smbconf):
466 default_lp.load(smbconf)
468 if targetdir is not None:
469 privdir = os.path.join(targetdir, "private")
471 privdir = default_lp.get("private dir")
472 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
476 if targetdir is not None:
477 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
478 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
480 default_lp.set("lock dir", os.path.abspath(targetdir))
485 if sid_generator == "internal":
486 sid_generator_line = ""
488 sid_generator_line = "sid generator = " + sid_generator
490 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
491 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
493 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
495 "NETBIOS_NAME": netbiosname,
498 "SERVERROLE": serverrole,
499 "NETLOGONPATH": netlogon,
500 "SYSVOLPATH": sysvol,
501 "SIDGENERATOR_LINE": sid_generator_line,
502 "PRIVATEDIR_LINE": privatedir_line,
503 "LOCKDIR_LINE": lockdir_line,
504 "POSIXEADB_LINE": posixeadb_line
508 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
509 users_gid, wheel_gid):
510 """setup reasonable name mappings for sam names to unix names.
512 :param samdb: SamDB object.
513 :param idmap: IDmap db object.
514 :param sid: The domain sid.
515 :param domaindn: The domain DN.
516 :param root_uid: uid of the UNIX root user.
517 :param nobody_uid: uid of the UNIX nobody user.
518 :param users_gid: gid of the UNIX users group.
519 :param wheel_gid: gid of the UNIX wheel group."""
521 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
522 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
524 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
525 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
527 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
528 provision_backend, names, schema,
531 """Setup the partitions for the SAM database.
533 Alternatively, provision() may call this, and then populate the database.
535 :note: This will wipe the Sam Database!
537 :note: This function always removes the local SAM LDB file. The erase
538 parameter controls whether to erase the existing data, which
539 may not be stored locally but in LDAP.
542 assert session_info is not None
544 old_partitions = None
545 new_partitions = None
547 # We use options=["modules:"] to stop the modules loading - we
548 # just want to wipe and re-initialise the database, not start it up
551 os.unlink(samdb_path)
555 samdb = Ldb(url=samdb_path, session_info=session_info,
556 lp=lp, options=["modules:"])
558 ldap_backend_line = "# No LDAP backend"
559 if provision_backend.type is not "ldb":
560 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
562 samdb.transaction_start()
564 message("Setting up sam.ldb partitions and settings")
565 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
566 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
567 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
568 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
569 "LDAP_BACKEND_LINE": ldap_backend_line,
573 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
574 "BACKEND_TYPE": provision_backend.type,
575 "SERVER_ROLE": serverrole
578 message("Setting up sam.ldb rootDSE")
579 setup_samdb_rootdse(samdb, setup_path, names)
582 samdb.transaction_cancel()
585 samdb.transaction_commit()
588 def secretsdb_self_join(secretsdb, domain,
589 netbiosname, machinepass, domainsid=None,
590 realm=None, dnsdomain=None,
592 key_version_number=1,
593 secure_channel_type=SEC_CHAN_WKSTA):
594 """Add domain join-specific bits to a secrets database.
596 :param secretsdb: Ldb Handle to the secrets database
597 :param machinepass: Machine password
599 attrs=["whenChanged",
607 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
608 msg["secureChannelType"] = str(secure_channel_type)
609 msg["flatname"] = [domain]
610 msg["objectClass"] = ["top", "primaryDomain"]
611 if realm is not None:
612 if dnsdomain is None:
613 dnsdomain = realm.lower()
614 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
616 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
617 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
618 msg["privateKeytab"] = ["secrets.keytab"];
621 msg["secret"] = [machinepass]
622 msg["samAccountName"] = ["%s$" % netbiosname]
623 msg["secureChannelType"] = [str(secure_channel_type)]
624 if domainsid is not None:
625 msg["objectSid"] = [ndr_pack(domainsid)]
627 res = secretsdb.search(base="cn=Primary Domains",
629 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
630 scope=ldb.SCOPE_ONELEVEL)
633 if del_msg.dn is not msg.dn:
634 secretsdb.delete(del_msg.dn)
636 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
639 msg["priorSecret"] = res[0]["secret"]
640 msg["priorWhenChanged"] = res[0]["whenChanged"]
642 if res["privateKeytab"] is not None:
643 msg["privateKeytab"] = res[0]["privateKeytab"]
645 if res["krb5Keytab"] is not None:
646 msg["krb5Keytab"] = res[0]["krb5Keytab"]
649 el.set_flags(ldb.FLAG_MOD_REPLACE)
650 secretsdb.modify(msg)
655 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
657 dns_keytab_path, dnspass):
658 """Add DNS specific bits to a secrets database.
660 :param secretsdb: Ldb Handle to the secrets database
661 :param setup_path: Setup path function
662 :param machinepass: Machine password
665 os.unlink(os.path.join(private_dir, dns_keytab_path))
669 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
671 "DNSDOMAIN": dnsdomain,
672 "DNS_KEYTAB": dns_keytab_path,
673 "DNSPASS_B64": b64encode(dnspass),
677 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
678 """Setup the secrets database.
680 :param path: Path to the secrets database.
681 :param setup_path: Get the path to a setup file.
682 :param session_info: Session info.
683 :param credentials: Credentials
684 :param lp: Loadparm context
685 :return: LDB handle for the created secrets database
687 if os.path.exists(path):
689 secrets_ldb = Ldb(path, session_info=session_info,
692 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
693 secrets_ldb = Ldb(path, session_info=session_info,
695 secrets_ldb.transaction_start()
696 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
698 if backend_credentials is not None and backend_credentials.authentication_requested():
699 if backend_credentials.get_bind_dn() is not None:
700 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
701 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
702 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
705 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
706 "LDAPADMINUSER": backend_credentials.get_username(),
707 "LDAPADMINREALM": backend_credentials.get_realm(),
708 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
713 def setup_privileges(path, setup_path, session_info, lp):
714 """Setup the privileges database.
716 :param path: Path to the privileges database.
717 :param setup_path: Get the path to a setup file.
718 :param session_info: Session info.
719 :param credentials: Credentials
720 :param lp: Loadparm context
721 :return: LDB handle for the created secrets database
723 if os.path.exists(path):
725 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
726 privilege_ldb.erase()
727 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
730 def setup_registry(path, setup_path, session_info, lp):
731 """Setup the registry.
733 :param path: Path to the registry database
734 :param setup_path: Function that returns the path to a setup.
735 :param session_info: Session information
736 :param credentials: Credentials
737 :param lp: Loadparm context
739 reg = registry.Registry()
740 hive = registry.open_ldb(path, session_info=session_info,
742 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
743 provision_reg = setup_path("provision.reg")
744 assert os.path.exists(provision_reg)
745 reg.diff_apply(provision_reg)
748 def setup_idmapdb(path, setup_path, session_info, lp):
749 """Setup the idmap database.
751 :param path: path to the idmap database
752 :param setup_path: Function that returns a path to a setup file
753 :param session_info: Session information
754 :param credentials: Credentials
755 :param lp: Loadparm context
757 if os.path.exists(path):
760 idmap_ldb = IDmapDB(path, session_info=session_info,
764 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
768 def setup_samdb_rootdse(samdb, setup_path, names):
769 """Setup the SamDB rootdse.
771 :param samdb: Sam Database handle
772 :param setup_path: Obtain setup path
774 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
775 "SCHEMADN": names.schemadn,
776 "NETBIOSNAME": names.netbiosname,
777 "DNSDOMAIN": names.dnsdomain,
778 "REALM": names.realm,
779 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
780 "DOMAINDN": names.domaindn,
781 "ROOTDN": names.rootdn,
782 "CONFIGDN": names.configdn,
783 "SERVERDN": names.serverdn,
787 def setup_self_join(samdb, names,
788 machinepass, dnspass,
789 domainsid, invocationid, setup_path,
790 policyguid, policyguid_dc, domainControllerFunctionality,
792 """Join a host to its own domain."""
793 assert isinstance(invocationid, str)
794 if ntdsguid is not None:
795 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
798 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
799 "CONFIGDN": names.configdn,
800 "SCHEMADN": names.schemadn,
801 "DOMAINDN": names.domaindn,
802 "SERVERDN": names.serverdn,
803 "INVOCATIONID": invocationid,
804 "NETBIOSNAME": names.netbiosname,
805 "DEFAULTSITE": names.sitename,
806 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
807 "MACHINEPASS_B64": b64encode(machinepass),
808 "REALM": names.realm,
809 "DOMAIN": names.domain,
810 "DOMAINSID": str(domainsid),
811 "DNSDOMAIN": names.dnsdomain,
812 "SAMBA_VERSION_STRING": version,
813 "NTDSGUID": ntdsguid_line,
814 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
816 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
817 "POLICYGUID": policyguid,
818 "POLICYGUID_DC": policyguid_dc,
819 "DNSDOMAIN": names.dnsdomain,
820 "DOMAINSID": str(domainsid),
821 "DOMAINDN": names.domaindn})
823 # add the NTDSGUID based SPNs
824 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
825 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
826 expression="", scope=ldb.SCOPE_BASE)
827 assert isinstance(names.ntdsguid, str)
829 # Setup fSMORoleOwner entries to point at the newly created DC entry
830 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
831 "DOMAIN": names.domain,
832 "DNSDOMAIN": names.dnsdomain,
833 "DOMAINDN": names.domaindn,
834 "CONFIGDN": names.configdn,
835 "SCHEMADN": names.schemadn,
836 "DEFAULTSITE": names.sitename,
837 "SERVERDN": names.serverdn,
838 "NETBIOSNAME": names.netbiosname,
839 "NTDSGUID": names.ntdsguid,
840 "DNSPASS_B64": b64encode(dnspass),
844 def setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid):
845 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
846 "{" + policyguid + "}")
847 os.makedirs(policy_path, 0755)
848 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
849 "[General]\r\nVersion=65543")
850 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
851 os.makedirs(os.path.join(policy_path, "USER"), 0755)
853 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
854 "{" + policyguid_dc + "}")
855 os.makedirs(policy_path_dc, 0755)
856 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
857 "[General]\r\nVersion=2")
858 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
859 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
862 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
864 domainsid, domainguid, policyguid, policyguid_dc,
865 fill, adminpass, krbtgtpass,
866 machinepass, invocationid, dnspass, ntdsguid,
867 serverrole, dom_for_fun_level=None,
869 """Setup a complete SAM Database.
871 :note: This will wipe the main SAM database file!
874 # ATTENTION: Do NOT change these default values without discussion with the
875 # team and/or release manager. They have a big impact on the whole program!
876 domainControllerFunctionality = DS_DC_FUNCTION_2008
878 if dom_for_fun_level is None:
879 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
880 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
881 message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
883 if dom_for_fun_level > domainControllerFunctionality:
884 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!")
886 domainFunctionality = dom_for_fun_level
887 forestFunctionality = dom_for_fun_level
889 # Also wipes the database
890 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
891 provision_backend=provision_backend, session_info=session_info,
893 serverrole=serverrole, schema=schema)
896 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
898 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
899 samdb = Ldb(session_info=session_info,
900 credentials=provision_backend.credentials, lp=lp)
902 message("Pre-loading the Samba 4 and AD schema")
904 # Load the schema from the one we computed earlier
905 samdb.set_schema_from_ldb(schema.ldb)
907 # And now we can connect to the DB - the schema won't be loaded from the DB
913 samdb.transaction_start()
915 # Set the domain functionality levels onto the database.
916 # Various module (the password_hash module in particular) need
917 # to know what level of AD we are emulating.
919 # These will be fixed into the database via the database
920 # modifictions below, but we need them set from the start.
921 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
922 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
923 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
925 samdb.set_domain_sid(str(domainsid))
926 samdb.set_invocation_id(invocationid)
928 message("Adding DomainDN: %s" % names.domaindn)
930 #impersonate domain admin
931 admin_session_info = admin_session(lp, str(domainsid))
932 samdb.set_session_info(admin_session_info)
933 if domainguid is not None:
934 domainguid_line = "objectGUID: %s\n-" % domainguid
938 descr = b64encode(get_domain_descriptor(domainsid))
939 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
940 "DOMAINDN": names.domaindn,
941 "DOMAINGUID": domainguid_line,
946 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
947 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
948 "DOMAINSID": str(domainsid),
949 "SCHEMADN": names.schemadn,
950 "NETBIOSNAME": names.netbiosname,
951 "DEFAULTSITE": names.sitename,
952 "CONFIGDN": names.configdn,
953 "SERVERDN": names.serverdn,
954 "POLICYGUID": policyguid,
955 "DOMAINDN": names.domaindn,
956 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
957 "SAMBA_VERSION_STRING": version
960 message("Adding configuration container")
961 descr = b64encode(get_config_descriptor(domainsid))
962 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
963 "CONFIGDN": names.configdn,
967 # The LDIF here was created when the Schema object was constructed
968 message("Setting up sam.ldb schema")
969 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
970 samdb.modify_ldif(schema.schema_dn_modify)
971 samdb.write_prefixes_from_schema()
972 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
973 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
974 {"SCHEMADN": names.schemadn})
976 message("Reopening sam.ldb with new schema");
977 samdb.transaction_commit()
978 samdb = Ldb(session_info=admin_session_info,
979 credentials=provision_backend.credentials, lp=lp)
981 samdb.transaction_start()
982 samdb.set_invocation_id(invocationid)
984 message("Setting up sam.ldb configuration data")
985 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
986 "CONFIGDN": names.configdn,
987 "NETBIOSNAME": names.netbiosname,
988 "DEFAULTSITE": names.sitename,
989 "DNSDOMAIN": names.dnsdomain,
990 "DOMAIN": names.domain,
991 "SCHEMADN": names.schemadn,
992 "DOMAINDN": names.domaindn,
993 "SERVERDN": names.serverdn,
994 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
997 message("Setting up display specifiers")
998 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
999 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1000 check_all_substituted(display_specifiers_ldif)
1001 samdb.add_ldif(display_specifiers_ldif)
1003 message("Adding users container")
1004 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1005 "DOMAINDN": names.domaindn})
1006 message("Modifying users container")
1007 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1008 "DOMAINDN": names.domaindn})
1009 message("Adding computers container")
1010 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1011 "DOMAINDN": names.domaindn})
1012 message("Modifying computers container")
1013 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1014 "DOMAINDN": names.domaindn})
1015 message("Setting up sam.ldb data")
1016 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1017 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1018 "DOMAINDN": names.domaindn,
1019 "NETBIOSNAME": names.netbiosname,
1020 "DEFAULTSITE": names.sitename,
1021 "CONFIGDN": names.configdn,
1022 "SERVERDN": names.serverdn,
1023 "POLICYGUID_DC": policyguid_dc
1026 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1027 "DOMAINDN": names.domaindn})
1029 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1030 "CONFIGDN": names.configdn,
1031 "SCHEMADN": names.schemadn})
1032 if fill == FILL_FULL:
1033 message("Setting up sam.ldb users and groups")
1034 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1035 "DOMAINDN": names.domaindn,
1036 "DOMAINSID": str(domainsid),
1037 "CONFIGDN": names.configdn,
1038 "ADMINPASS_B64": b64encode(adminpass),
1039 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1042 message("Setting up self join")
1043 setup_self_join(samdb, names=names, invocationid=invocationid,
1045 machinepass=machinepass,
1046 domainsid=domainsid, policyguid=policyguid,
1047 policyguid_dc=policyguid_dc,
1048 setup_path=setup_path,
1049 domainControllerFunctionality=domainControllerFunctionality,
1052 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1053 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1054 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1055 assert isinstance(names.ntdsguid, str)
1058 samdb.transaction_cancel()
1061 samdb.transaction_commit()
1066 FILL_NT4SYNC = "NT4SYNC"
1068 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1069 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)"
1071 def set_gpo_acl(path,acl,lp,domsid):
1072 setntacl(lp,path,acl,domsid)
1073 for root, dirs, files in os.walk(path, topdown=False):
1075 setntacl(lp,os.path.join(root, name),acl,domsid)
1077 setntacl(lp,os.path.join(root, name),acl,domsid)
1079 def setsysvolacl(samdb,names,netlogon,sysvol,gid,domainsid,lp):
1082 os.chown(sysvol,-1,gid)
1086 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1087 for root, dirs, files in os.walk(sysvol, topdown=False):
1090 os.chown(os.path.join(root, name),-1,gid)
1091 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1094 os.chown(os.path.join(root, name),-1,gid)
1095 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1098 policy_path = os.path.join(sysvol, names.dnsdomain, "Policies")
1099 set_gpo_acl(policy_path,dsacl2fsacl(POLICIES_ACL,str(domainsid)),lp,str(domainsid))
1100 res = samdb.search(base="CN=Policies,CN=System,%s"%(names.domaindn),
1101 attrs=["cn","nTSecurityDescriptor"],
1102 expression="", scope=ldb.SCOPE_ONELEVEL)
1104 acl = ndr_unpack(security.descriptor,str(policy["nTSecurityDescriptor"])).as_sddl()
1105 policy_path = os.path.join(sysvol, names.dnsdomain, "Policies",
1107 set_gpo_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
1111 def provision(setup_dir, message, session_info,
1112 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1114 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1116 domain=None, hostname=None, hostip=None, hostip6=None,
1117 domainsid=None, adminpass=None, ldapadminpass=None,
1118 krbtgtpass=None, domainguid=None,
1119 policyguid=None, policyguid_dc=None, invocationid=None,
1120 machinepass=None, ntdsguid=None,
1121 dnspass=None, root=None, nobody=None, users=None,
1122 wheel=None, backup=None, aci=None, serverrole=None,
1123 dom_for_fun_level=None,
1124 ldap_backend_extra_port=None, backend_type=None,
1126 ol_mmr_urls=None, ol_olc=None,
1127 setup_ds_path=None, slapd_path=None, nosync=False,
1128 ldap_dryrun_mode=False,useeadb=False):
1131 :note: caution, this wipes all existing data!
1134 def setup_path(file):
1135 return os.path.join(setup_dir, file)
1137 if domainsid is None:
1138 domainsid = security.random_sid()
1140 domainsid = security.dom_sid(domainsid)
1142 # create/adapt the group policy GUIDs
1143 if policyguid is None:
1144 policyguid = str(uuid.uuid4())
1145 policyguid = policyguid.upper()
1146 if policyguid_dc is None:
1147 policyguid_dc = str(uuid.uuid4())
1148 policyguid_dc = policyguid_dc.upper()
1150 if adminpass is None:
1151 adminpass = glue.generate_random_str(12)
1152 if krbtgtpass is None:
1153 krbtgtpass = glue.generate_random_str(12)
1154 if machinepass is None:
1155 machinepass = glue.generate_random_str(12)
1157 dnspass = glue.generate_random_str(12)
1158 if ldapadminpass is None:
1159 #Make a new, random password between Samba and it's LDAP server
1160 ldapadminpass=glue.generate_random_str(12)
1162 if backend_type is None:
1163 backend_type = "ldb"
1165 sid_generator = "internal"
1166 if backend_type == "fedora-ds":
1167 sid_generator = "backend"
1169 root_uid = findnss_uid([root or "root"])
1170 nobody_uid = findnss_uid([nobody or "nobody"])
1171 users_gid = findnss_gid([users or "users"])
1173 wheel_gid = findnss_gid(["wheel", "adm"])
1175 wheel_gid = findnss_gid([wheel])
1177 bind_gid = findnss_gid(["bind", "named"])
1181 if targetdir is not None:
1182 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1183 os.makedirs(os.path.join(targetdir, "etc"))
1184 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1185 elif smbconf is None:
1186 smbconf = param.default_path()
1188 # only install a new smb.conf if there isn't one there already
1189 if os.path.exists(smbconf):
1190 # if Samba Team members can't figure out the weird errors
1191 # loading an empty smb.conf gives, then we need to be smarter.
1192 # Pretend it just didn't exist --abartlet
1193 data = open(smbconf, 'r').read()
1194 data = data.lstrip()
1195 if data is None or data == "":
1196 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1197 targetdir, sid_generator, useeadb)
1199 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1200 targetdir, sid_generator, useeadb)
1202 lp = param.LoadParm()
1205 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1206 dnsdomain=realm, serverrole=serverrole,
1207 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1208 serverdn=serverdn, sitename=sitename)
1210 paths = provision_paths_from_lp(lp, names.dnsdomain)
1212 paths.bind_gid = bind_gid
1216 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1217 except socket.gaierror, (socket.EAI_NODATA, msg):
1222 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1223 except socket.gaierror, (socket.EAI_NODATA, msg):
1226 if serverrole is None:
1227 serverrole = lp.get("server role")
1229 assert serverrole in ("domain controller", "member server", "standalone")
1230 if invocationid is None:
1231 invocationid = str(uuid.uuid4())
1233 if not os.path.exists(paths.private_dir):
1234 os.mkdir(paths.private_dir)
1235 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1236 os.mkdir(os.path.join(paths.private_dir,"tls"))
1238 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1240 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1242 if backend_type == "ldb":
1243 provision_backend = LDBBackend(backend_type,
1244 paths=paths, setup_path=setup_path,
1245 lp=lp, credentials=credentials,
1248 elif backend_type == "existing":
1249 provision_backend = ExistingBackend(backend_type,
1250 paths=paths, setup_path=setup_path,
1251 lp=lp, credentials=credentials,
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 ProvisioningError("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 are configuring a DC.")
1337 message("Please either remove %s or see the template at %s" %
1338 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1339 assert(paths.sysvol is not None)
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,names,samdb,policyguid,policyguid_dc,domainsid)
1353 setsysvolacl(samdb,names,paths.netlogon,paths.sysvol,wheel_gid,domainsid,lp)
1355 message("Setting up sam.ldb rootDSE marking as synchronized")
1356 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1358 secretsdb_self_join(secrets_ldb, domain=names.domain,
1360 dnsdomain=names.dnsdomain,
1361 netbiosname=names.netbiosname,
1362 domainsid=domainsid,
1363 machinepass=machinepass,
1364 secure_channel_type=SEC_CHAN_BDC)
1366 if serverrole == "domain controller":
1367 secretsdb_setup_dns(secrets_ldb, setup_path,
1369 realm=names.realm, dnsdomain=names.dnsdomain,
1370 dns_keytab_path=paths.dns_keytab,
1373 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1374 assert isinstance(domainguid, str)
1376 # Only make a zone file on the first DC, it should be replicated
1377 # with DNS replication
1378 create_zone_file(message, paths, setup_path, dnsdomain=names.dnsdomain,
1380 hostip6=hostip6, hostname=names.hostname,
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 updates" % paths.namedtxt)
1393 create_krb5_conf(paths.krb5conf, setup_path,
1394 dnsdomain=names.dnsdomain, hostname=names.hostname,
1396 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1398 provision_backend.post_setup()
1399 provision_backend.shutdown()
1401 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1404 #Now commit the secrets.ldb to disk
1405 secrets_ldb.transaction_commit()
1407 # the commit creates the dns.keytab, now chown it
1408 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1409 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1411 os.chmod(dns_keytab_path, 0640)
1412 os.chown(dns_keytab_path, -1, paths.bind_gid)
1414 message("Failed to chown %s to bind gid %u" % (dns_keytab_path, paths.bind_gid))
1417 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1419 message("Once the above files are installed, your Samba4 server will be ready to use")
1420 message("Server Role: %s" % serverrole)
1421 message("Hostname: %s" % names.hostname)
1422 message("NetBIOS Domain: %s" % names.domain)
1423 message("DNS Domain: %s" % names.dnsdomain)
1424 message("DOMAIN SID: %s" % str(domainsid))
1425 if samdb_fill == FILL_FULL:
1426 message("Admin password: %s" % adminpass)
1427 if provision_backend.type is not "ldb":
1428 if provision_backend.credentials.get_bind_dn() is not None:
1429 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1431 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1433 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1435 if provision_backend.slapd_command_escaped is not None:
1436 # now display slapd_command_file.txt to show how slapd must be started next time
1437 message("Use later the following commandline to start slapd, then Samba:")
1438 message(provision_backend.slapd_command_escaped)
1439 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1442 result = ProvisionResult()
1443 result.domaindn = domaindn
1444 result.paths = paths
1446 result.samdb = samdb
1451 def provision_become_dc(setup_dir=None,
1452 smbconf=None, targetdir=None, realm=None,
1453 rootdn=None, domaindn=None, schemadn=None,
1454 configdn=None, serverdn=None,
1455 domain=None, hostname=None, domainsid=None,
1456 adminpass=None, krbtgtpass=None, domainguid=None,
1457 policyguid=None, policyguid_dc=None, invocationid=None,
1459 dnspass=None, root=None, nobody=None, users=None,
1460 wheel=None, backup=None, serverrole=None,
1461 ldap_backend=None, ldap_backend_type=None,
1462 sitename=None, debuglevel=1):
1465 """print a message if quiet is not set."""
1468 glue.set_debug_level(debuglevel)
1470 return provision(setup_dir, message, system_session(), None,
1471 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1472 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1473 configdn=configdn, serverdn=serverdn, domain=domain,
1474 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1475 machinepass=machinepass, serverrole="domain controller",
1479 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1480 """Create a PHP LDAP admin configuration file.
1482 :param path: Path to write the configuration to.
1483 :param setup_path: Function to generate setup paths.
1485 setup_file(setup_path("phpldapadmin-config.php"), path,
1486 {"S4_LDAPI_URI": ldapi_uri})
1489 def create_zone_file(message, paths, setup_path, dnsdomain,
1490 hostip, hostip6, hostname, realm, domainguid,
1492 """Write out a DNS zone file, from the info in the current database.
1494 :param paths: paths object
1495 :param setup_path: Setup path function.
1496 :param dnsdomain: DNS Domain name
1497 :param domaindn: DN of the Domain
1498 :param hostip: Local IPv4 IP
1499 :param hostip6: Local IPv6 IP
1500 :param hostname: Local hostname
1501 :param realm: Realm name
1502 :param domainguid: GUID of the domain.
1503 :param ntdsguid: GUID of the hosts nTDSDSA record.
1505 assert isinstance(domainguid, str)
1507 if hostip6 is not None:
1508 hostip6_base_line = " IN AAAA " + hostip6
1509 hostip6_host_line = hostname + " IN AAAA " + hostip6
1511 hostip6_base_line = ""
1512 hostip6_host_line = ""
1514 if hostip is not None:
1515 hostip_base_line = " IN A " + hostip
1516 hostip_host_line = hostname + " IN A " + hostip
1518 hostip_base_line = ""
1519 hostip_host_line = ""
1521 dns_dir = os.path.dirname(paths.dns)
1524 shutil.rmtree(dns_dir, True)
1528 os.mkdir(dns_dir, 0775)
1530 setup_file(setup_path("provision.zone"), paths.dns, {
1531 "HOSTNAME": hostname,
1532 "DNSDOMAIN": dnsdomain,
1534 "HOSTIP_BASE_LINE": hostip_base_line,
1535 "HOSTIP_HOST_LINE": hostip_host_line,
1536 "DOMAINGUID": domainguid,
1537 "DATESTRING": time.strftime("%Y%m%d%H"),
1538 "DEFAULTSITE": DEFAULTSITE,
1539 "NTDSGUID": ntdsguid,
1540 "HOSTIP6_BASE_LINE": hostip6_base_line,
1541 "HOSTIP6_HOST_LINE": hostip6_host_line,
1544 if paths.bind_gid is not None:
1546 os.chown(dns_dir, -1, paths.bind_gid)
1547 os.chown(paths.dns, -1, paths.bind_gid)
1548 # chmod needed to cope with umask
1549 os.chmod(dns_dir, 0775)
1550 os.chmod(paths.dns, 0664)
1552 message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1555 def create_named_conf(paths, setup_path, realm, dnsdomain,
1557 """Write out a file containing zone statements suitable for inclusion in a
1558 named.conf file (including GSS-TSIG configuration).
1560 :param paths: all paths
1561 :param setup_path: Setup path function.
1562 :param realm: Realm name
1563 :param dnsdomain: DNS Domain name
1564 :param private_dir: Path to private directory
1565 :param keytab_name: File name of DNS keytab file
1568 setup_file(setup_path("named.conf"), paths.namedconf, {
1569 "DNSDOMAIN": dnsdomain,
1571 "ZONE_FILE": paths.dns,
1572 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1573 "NAMED_CONF": paths.namedconf,
1574 "NAMED_CONF_UPDATE": paths.namedconf_update
1577 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1579 def create_named_txt(path, setup_path, realm, dnsdomain,
1580 private_dir, keytab_name):
1581 """Write out a file containing zone statements suitable for inclusion in a
1582 named.conf file (including GSS-TSIG configuration).
1584 :param path: Path of the new named.conf file.
1585 :param setup_path: Setup path function.
1586 :param realm: Realm name
1587 :param dnsdomain: DNS Domain name
1588 :param private_dir: Path to private directory
1589 :param keytab_name: File name of DNS keytab file
1592 setup_file(setup_path("named.txt"), path, {
1593 "DNSDOMAIN": dnsdomain,
1595 "DNS_KEYTAB": keytab_name,
1596 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1597 "PRIVATE_DIR": private_dir
1600 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1601 """Write out a file containing zone statements suitable for inclusion in a
1602 named.conf file (including GSS-TSIG configuration).
1604 :param path: Path of the new named.conf file.
1605 :param setup_path: Setup path function.
1606 :param dnsdomain: DNS Domain name
1607 :param hostname: Local hostname
1608 :param realm: Realm name
1611 setup_file(setup_path("krb5.conf"), path, {
1612 "DNSDOMAIN": dnsdomain,
1613 "HOSTNAME": hostname,