2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
42 from samba.auth import system_session, admin_session
43 from samba import glue, version, Ldb, substitute_var, valid_netbios_name
44 from samba import check_all_substituted, read_and_sub_file, setup_file
45 from samba import DS_DOMAIN_FUNCTION_2003, DS_DC_FUNCTION_2008
46 from samba.dcerpc import security
47 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
48 from samba.idmap import IDmapDB
49 from samba.ntacls import setntacl, dsacl2fsacl
50 from samba.ndr import ndr_pack,ndr_unpack
51 from samba.schema import Schema
52 from ms_display_specifiers import read_ms_ldif
53 from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
54 from provisionexceptions import ProvisioningError, InvalidNetbiosName
56 __docformat__ = "restructuredText"
59 """Find the setup directory used by provision."""
60 dirname = os.path.dirname(__file__)
61 if "/site-packages/" in dirname:
62 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
63 for suffix in ["share/setup", "share/samba/setup", "setup"]:
64 ret = os.path.join(prefix, suffix)
65 if os.path.isdir(ret):
68 ret = os.path.join(dirname, "../../../setup")
69 if os.path.isdir(ret):
71 raise Exception("Unable to find setup directory.")
73 # descriptors of the naming contexts
74 # hard coded at this point, but will probably be changed when
75 # we enable different fsmo roles
77 def get_config_descriptor(domain_sid):
78 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
79 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
81 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
84 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
85 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
86 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
87 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
88 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
89 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
90 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
91 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
92 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
93 sec = security.descriptor.from_sddl(sddl, domain_sid)
96 def get_domain_descriptor(domain_sid):
97 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
98 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
99 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
100 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
101 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
102 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
103 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
104 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
105 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
106 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
107 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
108 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
109 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
110 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
111 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
112 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
113 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
114 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
119 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
120 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
121 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
122 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
123 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
125 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
126 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
127 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
128 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
129 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
133 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
134 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
135 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
138 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
140 "(A;;RPLCLORC;;;ED)" \
141 "(A;;RPLCLORC;;;AU)" \
142 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
143 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
144 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
145 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
146 sec = security.descriptor.from_sddl(sddl, domain_sid)
149 DEFAULTSITE = "Default-First-Site-Name"
153 class ProvisionPaths(object):
155 self.shareconf = None
166 self.dns_keytab = None
169 self.private_dir = None
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 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(os.path.join(targetdir, "private"),"eadb.tdb"))
472 if targetdir is not None:
473 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
474 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
476 default_lp.set("lock dir", os.path.abspath(targetdir))
481 if sid_generator == "internal":
482 sid_generator_line = ""
484 sid_generator_line = "sid generator = " + sid_generator
486 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
487 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
489 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
491 "NETBIOS_NAME": netbiosname,
494 "SERVERROLE": serverrole,
495 "NETLOGONPATH": netlogon,
496 "SYSVOLPATH": sysvol,
497 "SIDGENERATOR_LINE": sid_generator_line,
498 "PRIVATEDIR_LINE": privatedir_line,
499 "LOCKDIR_LINE": lockdir_line,
500 "POSIXEADB_LINE": posixeadb_line
504 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
505 users_gid, wheel_gid):
506 """setup reasonable name mappings for sam names to unix names.
508 :param samdb: SamDB object.
509 :param idmap: IDmap db object.
510 :param sid: The domain sid.
511 :param domaindn: The domain DN.
512 :param root_uid: uid of the UNIX root user.
513 :param nobody_uid: uid of the UNIX nobody user.
514 :param users_gid: gid of the UNIX users group.
515 :param wheel_gid: gid of the UNIX wheel group."""
517 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
518 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
520 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
521 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
523 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
524 provision_backend, names, schema,
527 """Setup the partitions for the SAM database.
529 Alternatively, provision() may call this, and then populate the database.
531 :note: This will wipe the Sam Database!
533 :note: This function always removes the local SAM LDB file. The erase
534 parameter controls whether to erase the existing data, which
535 may not be stored locally but in LDAP.
538 assert session_info is not None
540 old_partitions = None
541 new_partitions = None
543 # We use options=["modules:"] to stop the modules loading - we
544 # just want to wipe and re-initialise the database, not start it up
547 os.unlink(samdb_path)
551 samdb = Ldb(url=samdb_path, session_info=session_info,
552 lp=lp, options=["modules:"])
554 ldap_backend_line = "# No LDAP backend"
555 if provision_backend.type is not "ldb":
556 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
558 samdb.transaction_start()
560 message("Setting up sam.ldb partitions and settings")
561 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
562 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
563 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
564 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
565 "LDAP_BACKEND_LINE": ldap_backend_line,
569 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
570 "BACKEND_TYPE": provision_backend.type,
571 "SERVER_ROLE": serverrole
574 message("Setting up sam.ldb rootDSE")
575 setup_samdb_rootdse(samdb, setup_path, names)
578 samdb.transaction_cancel()
581 samdb.transaction_commit()
584 def secretsdb_self_join(secretsdb, domain,
585 netbiosname, machinepass, domainsid=None,
586 realm=None, dnsdomain=None,
588 key_version_number=1,
589 secure_channel_type=SEC_CHAN_WKSTA):
590 """Add domain join-specific bits to a secrets database.
592 :param secretsdb: Ldb Handle to the secrets database
593 :param machinepass: Machine password
595 attrs=["whenChanged",
603 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
604 msg["secureChannelType"] = str(secure_channel_type)
605 msg["flatname"] = [domain]
606 msg["objectClass"] = ["top", "primaryDomain"]
607 if realm is not None:
608 if dnsdomain is None:
609 dnsdomain = realm.lower()
610 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
612 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
613 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
614 msg["privateKeytab"] = ["secrets.keytab"];
617 msg["secret"] = [machinepass]
618 msg["samAccountName"] = ["%s$" % netbiosname]
619 msg["secureChannelType"] = [str(secure_channel_type)]
620 if domainsid is not None:
621 msg["objectSid"] = [ndr_pack(domainsid)]
623 res = secretsdb.search(base="cn=Primary Domains",
625 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
626 scope=ldb.SCOPE_ONELEVEL)
629 if del_msg.dn is not msg.dn:
630 secretsdb.delete(del_msg.dn)
632 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
635 msg["priorSecret"] = res[0]["secret"]
636 msg["priorWhenChanged"] = res[0]["whenChanged"]
638 if res["privateKeytab"] is not None:
639 msg["privateKeytab"] = res[0]["privateKeytab"]
641 if res["krb5Keytab"] is not None:
642 msg["krb5Keytab"] = res[0]["krb5Keytab"]
645 el.set_flags(ldb.FLAG_MOD_REPLACE)
646 secretsdb.modify(msg)
651 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
653 dns_keytab_path, dnspass):
654 """Add DNS specific bits to a secrets database.
656 :param secretsdb: Ldb Handle to the secrets database
657 :param setup_path: Setup path function
658 :param machinepass: Machine password
661 os.unlink(os.path.join(private_dir, dns_keytab_path))
665 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
667 "DNSDOMAIN": dnsdomain,
668 "DNS_KEYTAB": dns_keytab_path,
669 "DNSPASS_B64": b64encode(dnspass),
673 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
674 """Setup the secrets database.
676 :param path: Path to the secrets database.
677 :param setup_path: Get the path to a setup file.
678 :param session_info: Session info.
679 :param credentials: Credentials
680 :param lp: Loadparm context
681 :return: LDB handle for the created secrets database
683 if os.path.exists(path):
685 secrets_ldb = Ldb(path, session_info=session_info,
688 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
689 secrets_ldb = Ldb(path, session_info=session_info,
691 secrets_ldb.transaction_start()
692 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
694 if backend_credentials is not None and backend_credentials.authentication_requested():
695 if backend_credentials.get_bind_dn() is not None:
696 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
697 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
698 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
701 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
702 "LDAPADMINUSER": backend_credentials.get_username(),
703 "LDAPADMINREALM": backend_credentials.get_realm(),
704 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
709 def setup_privileges(path, setup_path, session_info, lp):
710 """Setup the privileges database.
712 :param path: Path to the privileges database.
713 :param setup_path: Get the path to a setup file.
714 :param session_info: Session info.
715 :param credentials: Credentials
716 :param lp: Loadparm context
717 :return: LDB handle for the created secrets database
719 if os.path.exists(path):
721 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
722 privilege_ldb.erase()
723 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
726 def setup_registry(path, setup_path, session_info, lp):
727 """Setup the registry.
729 :param path: Path to the registry database
730 :param setup_path: Function that returns the path to a setup.
731 :param session_info: Session information
732 :param credentials: Credentials
733 :param lp: Loadparm context
735 reg = registry.Registry()
736 hive = registry.open_ldb(path, session_info=session_info,
738 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
739 provision_reg = setup_path("provision.reg")
740 assert os.path.exists(provision_reg)
741 reg.diff_apply(provision_reg)
744 def setup_idmapdb(path, setup_path, session_info, lp):
745 """Setup the idmap database.
747 :param path: path to the idmap database
748 :param setup_path: Function that returns a path to a setup file
749 :param session_info: Session information
750 :param credentials: Credentials
751 :param lp: Loadparm context
753 if os.path.exists(path):
756 idmap_ldb = IDmapDB(path, session_info=session_info,
760 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
764 def setup_samdb_rootdse(samdb, setup_path, names):
765 """Setup the SamDB rootdse.
767 :param samdb: Sam Database handle
768 :param setup_path: Obtain setup path
770 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
771 "SCHEMADN": names.schemadn,
772 "NETBIOSNAME": names.netbiosname,
773 "DNSDOMAIN": names.dnsdomain,
774 "REALM": names.realm,
775 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
776 "DOMAINDN": names.domaindn,
777 "ROOTDN": names.rootdn,
778 "CONFIGDN": names.configdn,
779 "SERVERDN": names.serverdn,
783 def setup_self_join(samdb, names,
784 machinepass, dnspass,
785 domainsid, invocationid, setup_path,
786 policyguid, policyguid_dc, domainControllerFunctionality,
788 """Join a host to its own domain."""
789 assert isinstance(invocationid, str)
790 if ntdsguid is not None:
791 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
794 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
795 "CONFIGDN": names.configdn,
796 "SCHEMADN": names.schemadn,
797 "DOMAINDN": names.domaindn,
798 "SERVERDN": names.serverdn,
799 "INVOCATIONID": invocationid,
800 "NETBIOSNAME": names.netbiosname,
801 "DEFAULTSITE": names.sitename,
802 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
803 "MACHINEPASS_B64": b64encode(machinepass),
804 "REALM": names.realm,
805 "DOMAIN": names.domain,
806 "DOMAINSID": str(domainsid),
807 "DNSDOMAIN": names.dnsdomain,
808 "SAMBA_VERSION_STRING": version,
809 "NTDSGUID": ntdsguid_line,
810 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
812 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
813 "POLICYGUID": policyguid,
814 "POLICYGUID_DC": policyguid_dc,
815 "DNSDOMAIN": names.dnsdomain,
816 "DOMAINSID": str(domainsid),
817 "DOMAINDN": names.domaindn})
819 # add the NTDSGUID based SPNs
820 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
821 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
822 expression="", scope=ldb.SCOPE_BASE)
823 assert isinstance(names.ntdsguid, str)
825 # Setup fSMORoleOwner entries to point at the newly created DC entry
826 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
827 "DOMAIN": names.domain,
828 "DNSDOMAIN": names.dnsdomain,
829 "DOMAINDN": names.domaindn,
830 "CONFIGDN": names.configdn,
831 "SCHEMADN": names.schemadn,
832 "DEFAULTSITE": names.sitename,
833 "SERVERDN": names.serverdn,
834 "NETBIOSNAME": names.netbiosname,
835 "NTDSGUID": names.ntdsguid,
836 "DNSPASS_B64": b64encode(dnspass),
840 def setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid):
841 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
842 "{" + policyguid + "}")
843 os.makedirs(policy_path, 0755)
844 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
845 "[General]\r\nVersion=65543")
846 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
847 os.makedirs(os.path.join(policy_path, "USER"), 0755)
849 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
850 "{" + policyguid_dc + "}")
851 os.makedirs(policy_path_dc, 0755)
852 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
853 "[General]\r\nVersion=2")
854 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
855 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
858 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
860 domainsid, domainguid, policyguid, policyguid_dc,
861 fill, adminpass, krbtgtpass,
862 machinepass, invocationid, dnspass, ntdsguid,
863 serverrole, dom_for_fun_level=None,
865 """Setup a complete SAM Database.
867 :note: This will wipe the main SAM database file!
870 # ATTENTION: Do NOT change these default values without discussion with the
871 # team and/or release manager. They have a big impact on the whole program!
872 domainControllerFunctionality = DS_DC_FUNCTION_2008
874 if dom_for_fun_level is None:
875 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
876 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
877 message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
879 if dom_for_fun_level > domainControllerFunctionality:
880 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!")
882 domainFunctionality = dom_for_fun_level
883 forestFunctionality = dom_for_fun_level
885 # Also wipes the database
886 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
887 provision_backend=provision_backend, session_info=session_info,
889 serverrole=serverrole, schema=schema)
892 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
894 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
895 samdb = Ldb(session_info=session_info,
896 credentials=provision_backend.credentials, lp=lp)
898 message("Pre-loading the Samba 4 and AD schema")
900 # Load the schema from the one we computed earlier
901 samdb.set_schema_from_ldb(schema.ldb)
903 # And now we can connect to the DB - the schema won't be loaded from the DB
909 samdb.transaction_start()
911 # Set the domain functionality levels onto the database.
912 # Various module (the password_hash module in particular) need
913 # to know what level of AD we are emulating.
915 # These will be fixed into the database via the database
916 # modifictions below, but we need them set from the start.
917 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
918 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
919 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
921 samdb.set_domain_sid(str(domainsid))
922 samdb.set_invocation_id(invocationid)
924 message("Adding DomainDN: %s" % names.domaindn)
926 #impersonate domain admin
927 admin_session_info = admin_session(lp, str(domainsid))
928 samdb.set_session_info(admin_session_info)
929 if domainguid is not None:
930 domainguid_line = "objectGUID: %s\n-" % domainguid
934 descr = b64encode(get_domain_descriptor(domainsid))
935 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
936 "DOMAINDN": names.domaindn,
937 "DOMAINGUID": domainguid_line,
942 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
943 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
944 "DOMAINSID": str(domainsid),
945 "SCHEMADN": names.schemadn,
946 "NETBIOSNAME": names.netbiosname,
947 "DEFAULTSITE": names.sitename,
948 "CONFIGDN": names.configdn,
949 "SERVERDN": names.serverdn,
950 "POLICYGUID": policyguid,
951 "DOMAINDN": names.domaindn,
952 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
953 "SAMBA_VERSION_STRING": version
956 message("Adding configuration container")
957 descr = b64encode(get_config_descriptor(domainsid))
958 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
959 "CONFIGDN": names.configdn,
963 # The LDIF here was created when the Schema object was constructed
964 message("Setting up sam.ldb schema")
965 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
966 samdb.modify_ldif(schema.schema_dn_modify)
967 samdb.write_prefixes_from_schema()
968 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
969 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
970 {"SCHEMADN": names.schemadn})
972 message("Reopening sam.ldb with new schema");
973 samdb.transaction_commit()
974 samdb = Ldb(session_info=admin_session_info,
975 credentials=provision_backend.credentials, lp=lp)
977 samdb.transaction_start()
978 samdb.set_invocation_id(invocationid)
980 message("Setting up sam.ldb configuration data")
981 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
982 "CONFIGDN": names.configdn,
983 "NETBIOSNAME": names.netbiosname,
984 "DEFAULTSITE": names.sitename,
985 "DNSDOMAIN": names.dnsdomain,
986 "DOMAIN": names.domain,
987 "SCHEMADN": names.schemadn,
988 "DOMAINDN": names.domaindn,
989 "SERVERDN": names.serverdn,
990 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
993 message("Setting up display specifiers")
994 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
995 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
996 check_all_substituted(display_specifiers_ldif)
997 samdb.add_ldif(display_specifiers_ldif)
999 message("Adding users container")
1000 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1001 "DOMAINDN": names.domaindn})
1002 message("Modifying users container")
1003 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1004 "DOMAINDN": names.domaindn})
1005 message("Adding computers container")
1006 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1007 "DOMAINDN": names.domaindn})
1008 message("Modifying computers container")
1009 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1010 "DOMAINDN": names.domaindn})
1011 message("Setting up sam.ldb data")
1012 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1013 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1014 "DOMAINDN": names.domaindn,
1015 "NETBIOSNAME": names.netbiosname,
1016 "DEFAULTSITE": names.sitename,
1017 "CONFIGDN": names.configdn,
1018 "SERVERDN": names.serverdn,
1019 "POLICYGUID_DC": policyguid_dc
1022 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1023 "DOMAINDN": names.domaindn})
1025 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1026 "CONFIGDN": names.configdn,
1027 "SCHEMADN": names.schemadn})
1028 if fill == FILL_FULL:
1029 message("Setting up sam.ldb users and groups")
1030 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1031 "DOMAINDN": names.domaindn,
1032 "DOMAINSID": str(domainsid),
1033 "CONFIGDN": names.configdn,
1034 "ADMINPASS_B64": b64encode(adminpass),
1035 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1038 message("Setting up self join")
1039 setup_self_join(samdb, names=names, invocationid=invocationid,
1041 machinepass=machinepass,
1042 domainsid=domainsid, policyguid=policyguid,
1043 policyguid_dc=policyguid_dc,
1044 setup_path=setup_path,
1045 domainControllerFunctionality=domainControllerFunctionality,
1048 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1049 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1050 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1051 assert isinstance(names.ntdsguid, str)
1054 samdb.transaction_cancel()
1057 samdb.transaction_commit()
1062 FILL_NT4SYNC = "NT4SYNC"
1064 SYSVOL_ACL = "O:${DOMAINSID}-500G:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;S-1-5-32-549)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1065 POLICIES_ACL = "O:${DOMAINSID}-500G:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;S-1-5-32-549)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;${DOMAINSID}-520)"
1067 def set_gpo_acl(path,acl,lp,domsid):
1068 setntacl(lp,path,acl,domsid)
1069 for root, dirs, files in os.walk(path, topdown=False):
1071 setntacl(lp,os.path.join(root, name),acl,domsid)
1073 setntacl(lp,os.path.join(root, name),acl,domsid)
1075 def setsysvolacl(samdb,names,netlogon,sysvol,gid,domainsid,lp):
1077 acl = SYSVOL_ACL.replace("${DOMAINSID}",str(domainsid))
1079 os.chown(sysvol,-1,gid)
1083 setntacl(lp,sysvol,acl,str(domainsid))
1084 for root, dirs, files in os.walk(sysvol, topdown=False):
1087 os.chown(os.path.join(root, name),-1,gid)
1088 setntacl(lp,os.path.join(root, name),acl,str(domainsid))
1091 os.chown(os.path.join(root, name),-1,gid)
1092 setntacl(lp,os.path.join(root, name),acl,str(domainsid))
1095 policy_path = os.path.join(sysvol, names.dnsdomain, "Policies")
1096 acl = POLICIES_ACL.replace("${DOMAINSID}",str(domainsid))
1097 set_gpo_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
1098 res = samdb.search(base="CN=Policies,CN=System,%s"%(names.domaindn),
1099 attrs=["cn","nTSecurityDescriptor"],
1100 expression="", scope=ldb.SCOPE_ONELEVEL)
1102 acl = ndr_unpack(security.descriptor,str(policy["nTSecurityDescriptor"])).as_sddl()
1103 policy_path = os.path.join(sysvol, names.dnsdomain, "Policies",
1105 set_gpo_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
1109 def provision(setup_dir, message, session_info,
1110 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1112 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1114 domain=None, hostname=None, hostip=None, hostip6=None,
1115 domainsid=None, adminpass=None, ldapadminpass=None,
1116 krbtgtpass=None, domainguid=None,
1117 policyguid=None, policyguid_dc=None, invocationid=None,
1118 machinepass=None, ntdsguid=None,
1119 dnspass=None, root=None, nobody=None, users=None,
1120 wheel=None, backup=None, aci=None, serverrole=None,
1121 dom_for_fun_level=None,
1122 ldap_backend_extra_port=None, backend_type=None,
1124 ol_mmr_urls=None, ol_olc=None,
1125 setup_ds_path=None, slapd_path=None, nosync=False,
1126 ldap_dryrun_mode=False,useeadb=False):
1129 :note: caution, this wipes all existing data!
1132 def setup_path(file):
1133 return os.path.join(setup_dir, file)
1135 if domainsid is None:
1136 domainsid = security.random_sid()
1138 domainsid = security.dom_sid(domainsid)
1140 # create/adapt the group policy GUIDs
1141 if policyguid is None:
1142 policyguid = str(uuid.uuid4())
1143 policyguid = policyguid.upper()
1144 if policyguid_dc is None:
1145 policyguid_dc = str(uuid.uuid4())
1146 policyguid_dc = policyguid_dc.upper()
1148 if adminpass is None:
1149 adminpass = glue.generate_random_str(12)
1150 if krbtgtpass is None:
1151 krbtgtpass = glue.generate_random_str(12)
1152 if machinepass is None:
1153 machinepass = glue.generate_random_str(12)
1155 dnspass = glue.generate_random_str(12)
1156 if ldapadminpass is None:
1157 #Make a new, random password between Samba and it's LDAP server
1158 ldapadminpass=glue.generate_random_str(12)
1160 if backend_type is None:
1161 backend_type = "ldb"
1163 sid_generator = "internal"
1164 if backend_type == "fedora-ds":
1165 sid_generator = "backend"
1167 root_uid = findnss_uid([root or "root"])
1168 nobody_uid = findnss_uid([nobody or "nobody"])
1169 users_gid = findnss_gid([users or "users"])
1171 wheel_gid = findnss_gid(["wheel", "adm"])
1173 wheel_gid = findnss_gid([wheel])
1175 bind_gid = findnss_gid(["bind", "named"])
1179 if targetdir is not None:
1180 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1181 os.makedirs(os.path.join(targetdir, "etc"))
1182 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1183 elif smbconf is None:
1184 smbconf = param.default_path()
1186 # only install a new smb.conf if there isn't one there already
1187 if os.path.exists(smbconf):
1188 # if Samba Team members can't figure out the weird errors
1189 # loading an empty smb.conf gives, then we need to be smarter.
1190 # Pretend it just didn't exist --abartlet
1191 data = open(smbconf, 'r').read()
1192 data = data.lstrip()
1193 if data is None or data == "":
1194 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1195 targetdir, sid_generator, useeadb)
1197 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1198 targetdir, sid_generator, useeadb)
1200 lp = param.LoadParm()
1203 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1204 dnsdomain=realm, serverrole=serverrole,
1205 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1206 serverdn=serverdn, sitename=sitename)
1208 paths = provision_paths_from_lp(lp, names.dnsdomain)
1210 paths.bind_gid = bind_gid
1214 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1215 except socket.gaierror, (socket.EAI_NODATA, msg):
1220 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
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, schemadn=names.schemadn, serverdn=names.serverdn)
1240 if backend_type == "ldb":
1241 provision_backend = LDBBackend(backend_type,
1242 paths=paths, setup_path=setup_path,
1243 lp=lp, credentials=credentials,
1246 elif backend_type == "existing":
1247 provision_backend = ExistingBackend(backend_type,
1248 paths=paths, setup_path=setup_path,
1249 lp=lp, credentials=credentials,
1252 elif backend_type == "fedora-ds":
1253 provision_backend = FDSBackend(backend_type,
1254 paths=paths, setup_path=setup_path,
1255 lp=lp, credentials=credentials,
1258 domainsid=domainsid,
1261 ldapadminpass=ldapadminpass,
1262 slapd_path=slapd_path,
1263 ldap_backend_extra_port=ldap_backend_extra_port,
1264 ldap_dryrun_mode=ldap_dryrun_mode,
1266 setup_ds_path=setup_ds_path)
1267 elif backend_type == "openldap":
1268 provision_backend = OpenLDAPBackend(backend_type,
1269 paths=paths, setup_path=setup_path,
1270 lp=lp, credentials=credentials,
1273 domainsid=domainsid,
1276 ldapadminpass=ldapadminpass,
1277 slapd_path=slapd_path,
1278 ldap_backend_extra_port=ldap_backend_extra_port,
1279 ldap_dryrun_mode=ldap_dryrun_mode,
1280 ol_mmr_urls=ol_mmr_urls,
1283 raise ProvisioningError("Unknown LDAP backend type selected")
1285 provision_backend.init()
1286 provision_backend.start()
1288 # only install a new shares config db if there is none
1289 if not os.path.exists(paths.shareconf):
1290 message("Setting up share.ldb")
1291 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1293 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1296 message("Setting up secrets.ldb")
1297 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1298 session_info=session_info,
1299 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1301 message("Setting up the registry")
1302 setup_registry(paths.hklm, setup_path, session_info,
1305 message("Setting up the privileges database")
1306 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1308 message("Setting up idmap db")
1309 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1312 message("Setting up SAM db")
1313 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1314 provision_backend, lp, names,
1316 domainsid=domainsid,
1317 schema=schema, domainguid=domainguid,
1318 policyguid=policyguid, policyguid_dc=policyguid_dc,
1320 adminpass=adminpass, krbtgtpass=krbtgtpass,
1321 invocationid=invocationid,
1322 machinepass=machinepass, dnspass=dnspass,
1323 ntdsguid=ntdsguid, serverrole=serverrole,
1324 dom_for_fun_level=dom_for_fun_level)
1326 if serverrole == "domain controller":
1327 if paths.netlogon is None:
1328 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1329 message("Please either remove %s or see the template at %s" %
1330 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1331 assert(paths.netlogon is not None)
1333 if paths.sysvol is None:
1334 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1335 message("Please either remove %s or see the template at %s" %
1336 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1337 assert(paths.sysvol is not None)
1340 if not os.path.isdir(paths.netlogon):
1341 os.makedirs(paths.netlogon, 0755)
1343 if samdb_fill == FILL_FULL:
1344 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1345 root_uid=root_uid, nobody_uid=nobody_uid,
1346 users_gid=users_gid, wheel_gid=wheel_gid)
1348 if serverrole == "domain controller":
1349 # Set up group policies (domain policy and domain controller policy)
1350 setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid)
1351 setsysvolacl(samdb,names,paths.netlogon,paths.sysvol,wheel_gid,domainsid,lp)
1353 message("Setting up sam.ldb rootDSE marking as synchronized")
1354 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1356 secretsdb_self_join(secrets_ldb, domain=names.domain,
1358 dnsdomain=names.dnsdomain,
1359 netbiosname=names.netbiosname,
1360 domainsid=domainsid,
1361 machinepass=machinepass,
1362 secure_channel_type=SEC_CHAN_BDC)
1364 if serverrole == "domain controller":
1365 secretsdb_setup_dns(secrets_ldb, setup_path,
1367 realm=names.realm, dnsdomain=names.dnsdomain,
1368 dns_keytab_path=paths.dns_keytab,
1371 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1372 assert isinstance(domainguid, str)
1374 # Only make a zone file on the first DC, it should be replicated
1375 # with DNS replication
1376 create_zone_file(message, paths, setup_path, dnsdomain=names.dnsdomain,
1378 hostip6=hostip6, hostname=names.hostname,
1380 domainguid=domainguid, ntdsguid=names.ntdsguid)
1382 create_named_conf(paths, setup_path, realm=names.realm,
1383 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1385 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1386 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1387 keytab_name=paths.dns_keytab)
1388 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1389 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1391 create_krb5_conf(paths.krb5conf, setup_path,
1392 dnsdomain=names.dnsdomain, hostname=names.hostname,
1394 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1396 provision_backend.post_setup()
1397 provision_backend.shutdown()
1399 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1402 #Now commit the secrets.ldb to disk
1403 secrets_ldb.transaction_commit()
1405 # the commit creates the dns.keytab, now chown it
1406 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1407 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1409 os.chmod(dns_keytab_path, 0640)
1410 os.chown(dns_keytab_path, -1, paths.bind_gid)
1412 message("Failed to chown %s to bind gid %u" % (dns_keytab_path, paths.bind_gid))
1415 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1417 message("Once the above files are installed, your Samba4 server will be ready to use")
1418 message("Server Role: %s" % serverrole)
1419 message("Hostname: %s" % names.hostname)
1420 message("NetBIOS Domain: %s" % names.domain)
1421 message("DNS Domain: %s" % names.dnsdomain)
1422 message("DOMAIN SID: %s" % str(domainsid))
1423 if samdb_fill == FILL_FULL:
1424 message("Admin password: %s" % adminpass)
1425 if provision_backend.type is not "ldb":
1426 if provision_backend.credentials.get_bind_dn() is not None:
1427 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1429 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1431 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1433 if provision_backend.slapd_command_escaped is not None:
1434 # now display slapd_command_file.txt to show how slapd must be started next time
1435 message("Use later the following commandline to start slapd, then Samba:")
1436 message(provision_backend.slapd_command_escaped)
1437 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1440 result = ProvisionResult()
1441 result.domaindn = domaindn
1442 result.paths = paths
1444 result.samdb = samdb
1449 def provision_become_dc(setup_dir=None,
1450 smbconf=None, targetdir=None, realm=None,
1451 rootdn=None, domaindn=None, schemadn=None,
1452 configdn=None, serverdn=None,
1453 domain=None, hostname=None, domainsid=None,
1454 adminpass=None, krbtgtpass=None, domainguid=None,
1455 policyguid=None, policyguid_dc=None, invocationid=None,
1457 dnspass=None, root=None, nobody=None, users=None,
1458 wheel=None, backup=None, serverrole=None,
1459 ldap_backend=None, ldap_backend_type=None,
1460 sitename=None, debuglevel=1):
1463 """print a message if quiet is not set."""
1466 glue.set_debug_level(debuglevel)
1468 return provision(setup_dir, message, system_session(), None,
1469 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1470 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1471 configdn=configdn, serverdn=serverdn, domain=domain,
1472 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1473 machinepass=machinepass, serverrole="domain controller",
1477 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1478 """Create a PHP LDAP admin configuration file.
1480 :param path: Path to write the configuration to.
1481 :param setup_path: Function to generate setup paths.
1483 setup_file(setup_path("phpldapadmin-config.php"), path,
1484 {"S4_LDAPI_URI": ldapi_uri})
1487 def create_zone_file(message, paths, setup_path, dnsdomain,
1488 hostip, hostip6, hostname, realm, domainguid,
1490 """Write out a DNS zone file, from the info in the current database.
1492 :param paths: paths object
1493 :param setup_path: Setup path function.
1494 :param dnsdomain: DNS Domain name
1495 :param domaindn: DN of the Domain
1496 :param hostip: Local IPv4 IP
1497 :param hostip6: Local IPv6 IP
1498 :param hostname: Local hostname
1499 :param realm: Realm name
1500 :param domainguid: GUID of the domain.
1501 :param ntdsguid: GUID of the hosts nTDSDSA record.
1503 assert isinstance(domainguid, str)
1505 if hostip6 is not None:
1506 hostip6_base_line = " IN AAAA " + hostip6
1507 hostip6_host_line = hostname + " IN AAAA " + hostip6
1509 hostip6_base_line = ""
1510 hostip6_host_line = ""
1512 if hostip is not None:
1513 hostip_base_line = " IN A " + hostip
1514 hostip_host_line = hostname + " IN A " + hostip
1516 hostip_base_line = ""
1517 hostip_host_line = ""
1519 dns_dir = os.path.dirname(paths.dns)
1522 shutil.rmtree(dns_dir, True)
1526 os.mkdir(dns_dir, 0770)
1527 # chmod needed to cope with umask
1528 os.chmod(dns_dir, 0770)
1530 if paths.bind_gid is not None:
1532 os.chown(dns_dir, -1, paths.bind_gid)
1534 message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1536 setup_file(setup_path("provision.zone"), paths.dns, {
1537 "HOSTNAME": hostname,
1538 "DNSDOMAIN": dnsdomain,
1540 "HOSTIP_BASE_LINE": hostip_base_line,
1541 "HOSTIP_HOST_LINE": hostip_host_line,
1542 "DOMAINGUID": domainguid,
1543 "DATESTRING": time.strftime("%Y%m%d%H"),
1544 "DEFAULTSITE": DEFAULTSITE,
1545 "NTDSGUID": ntdsguid,
1546 "HOSTIP6_BASE_LINE": hostip6_base_line,
1547 "HOSTIP6_HOST_LINE": hostip6_host_line,
1551 def create_named_conf(paths, setup_path, realm, dnsdomain,
1553 """Write out a file containing zone statements suitable for inclusion in a
1554 named.conf file (including GSS-TSIG configuration).
1556 :param paths: all paths
1557 :param setup_path: Setup path function.
1558 :param realm: Realm name
1559 :param dnsdomain: DNS Domain name
1560 :param private_dir: Path to private directory
1561 :param keytab_name: File name of DNS keytab file
1564 setup_file(setup_path("named.conf"), paths.namedconf, {
1565 "DNSDOMAIN": dnsdomain,
1567 "ZONE_FILE": paths.dns,
1568 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1569 "NAMED_CONF": paths.namedconf,
1570 "NAMED_CONF_UPDATE": paths.namedconf_update
1573 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1575 def create_named_txt(path, setup_path, realm, dnsdomain,
1576 private_dir, keytab_name):
1577 """Write out a file containing zone statements suitable for inclusion in a
1578 named.conf file (including GSS-TSIG configuration).
1580 :param path: Path of the new named.conf file.
1581 :param setup_path: Setup path function.
1582 :param realm: Realm name
1583 :param dnsdomain: DNS Domain name
1584 :param private_dir: Path to private directory
1585 :param keytab_name: File name of DNS keytab file
1588 setup_file(setup_path("named.txt"), path, {
1589 "DNSDOMAIN": dnsdomain,
1591 "DNS_KEYTAB": keytab_name,
1592 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1593 "PRIVATE_DIR": private_dir
1596 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1597 """Write out a file containing zone statements suitable for inclusion in a
1598 named.conf file (including GSS-TSIG configuration).
1600 :param path: Path of the new named.conf file.
1601 :param setup_path: Setup path function.
1602 :param dnsdomain: DNS Domain name
1603 :param hostname: Local hostname
1604 :param realm: Realm name
1607 setup_file(setup_path("krb5.conf"), path, {
1608 "DNSDOMAIN": dnsdomain,
1609 "HOSTNAME": hostname,