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
43 from auth import system_session, admin_session
44 from samba import version, Ldb, substitute_var, valid_netbios_name, setup_file
45 from samba import check_all_substituted, read_and_sub_file
46 from samba import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008
47 from samba.samdb import SamDB
48 from samba.idmap import IDmapDB
49 from samba.dcerpc import security
50 from samba.ndr import ndr_pack
52 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
53 from ms_display_specifiers import read_ms_ldif
54 from schema import Schema
55 from provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
56 from provisionexceptions import ProvisioningError, InvalidNetbiosName
57 from signal import SIGTERM
58 from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
60 __docformat__ = "restructuredText"
63 """Find the setup directory used by provision."""
64 dirname = os.path.dirname(__file__)
65 if "/site-packages/" in dirname:
66 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
67 for suffix in ["share/setup", "share/samba/setup", "setup"]:
68 ret = os.path.join(prefix, suffix)
69 if os.path.isdir(ret):
72 ret = os.path.join(dirname, "../../../setup")
73 if os.path.isdir(ret):
75 raise Exception("Unable to find setup directory.")
77 # descriptors of the naming contexts
78 # hard coded at this point, but will probably be changed when
79 # we enable different fsmo roles
81 def get_config_descriptor(domain_sid):
82 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
83 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
84 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
85 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
86 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
87 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
88 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
89 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
90 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
91 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
92 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
93 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
94 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
95 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
96 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
97 sec = security.descriptor.from_sddl(sddl, domain_sid)
98 return b64encode(ndr_pack(sec))
100 def get_domain_descriptor(domain_sid):
101 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
102 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
103 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
104 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
105 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
106 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
107 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
108 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
109 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
110 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
111 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-832762594-175224951-1765713900-498)" \
112 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
113 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
114 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
115 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
116 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
117 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
119 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
120 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
121 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
122 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)" \
123 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
124 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
125 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
126 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
127 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
129 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
130 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
131 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
132 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
133 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
134 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
135 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
136 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
137 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
138 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
139 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
142 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
144 "(A;;RPLCLORC;;;ED)" \
145 "(A;;RPLCLORC;;;AU)" \
146 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
147 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
148 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
149 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
150 sec = security.descriptor.from_sddl(sddl, domain_sid)
151 return b64encode(ndr_pack(sec))
153 DEFAULTSITE = "Default-First-Site-Name"
157 class ProvisionPaths(object):
159 self.shareconf = None
170 self.dns_keytab = None
173 self.private_dir = None
175 self.slapdconf = None
176 self.modulesconf = None
177 self.memberofconf = None
179 self.olmmrserveridsconf = None
180 self.olmmrsyncreplconf = None
183 self.olcseedldif = None
186 class ProvisionNames(object):
192 self.ldapmanagerdn = None
193 self.dnsdomain = None
195 self.netbiosname = None
202 class ProvisionResult(object):
209 def check_install(lp, session_info, credentials):
210 """Check whether the current install seems ok.
212 :param lp: Loadparm context
213 :param session_info: Session information
214 :param credentials: Credentials
216 if lp.get("realm") == "":
217 raise Exception("Realm empty")
218 ldb = Ldb(lp.get("sam database"), session_info=session_info,
219 credentials=credentials, lp=lp)
220 if len(ldb.search("(cn=Administrator)")) != 1:
221 raise ProvisioningError("No administrator account found")
224 def findnss(nssfn, names):
225 """Find a user or group from a list of possibilities.
227 :param nssfn: NSS Function to try (should raise KeyError if not found)
228 :param names: Names to check.
229 :return: Value return by first names list.
236 raise KeyError("Unable to find user/group %r" % names)
239 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
240 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
243 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
244 """Setup a ldb in the private dir.
246 :param ldb: LDB file to import data into
247 :param ldif_path: Path of the LDIF file to load
248 :param subst_vars: Optional variables to subsitute in LDIF.
249 :param nocontrols: Optional list of controls, can be None for no controls
251 assert isinstance(ldif_path, str)
252 data = read_and_sub_file(ldif_path, subst_vars)
253 ldb.add_ldif(data,controls)
256 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
257 """Modify a ldb in the private dir.
259 :param ldb: LDB object.
260 :param ldif_path: LDIF file path.
261 :param subst_vars: Optional dictionary with substitution variables.
263 data = read_and_sub_file(ldif_path, subst_vars)
265 ldb.modify_ldif(data)
268 def setup_ldb(ldb, ldif_path, subst_vars):
269 """Import a LDIF a file into a LDB handle, optionally substituting variables.
271 :note: Either all LDIF data will be added or none (using transactions).
273 :param ldb: LDB file to import into.
274 :param ldif_path: Path to the LDIF file.
275 :param subst_vars: Dictionary with substitution variables.
277 assert ldb is not None
278 ldb.transaction_start()
280 setup_add_ldif(ldb, ldif_path, subst_vars)
282 ldb.transaction_cancel()
284 ldb.transaction_commit()
287 def provision_paths_from_lp(lp, dnsdomain):
288 """Set the default paths for provisioning.
290 :param lp: Loadparm context.
291 :param dnsdomain: DNS Domain name
293 paths = ProvisionPaths()
294 paths.private_dir = lp.get("private dir")
295 paths.dns_keytab = "dns.keytab"
297 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
298 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
299 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
300 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
301 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
302 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
303 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
304 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
305 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
306 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
307 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
308 paths.phpldapadminconfig = os.path.join(paths.private_dir,
309 "phpldapadmin-config.php")
310 paths.ldapdir = os.path.join(paths.private_dir,
312 paths.slapdconf = os.path.join(paths.ldapdir,
314 paths.slapdpid = os.path.join(paths.ldapdir,
316 paths.modulesconf = os.path.join(paths.ldapdir,
318 paths.memberofconf = os.path.join(paths.ldapdir,
320 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
321 "mmr_serverids.conf")
322 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
324 paths.olcdir = os.path.join(paths.ldapdir,
326 paths.olcseedldif = os.path.join(paths.ldapdir,
328 paths.hklm = "hklm.ldb"
329 paths.hkcr = "hkcr.ldb"
330 paths.hkcu = "hkcu.ldb"
331 paths.hku = "hku.ldb"
332 paths.hkpd = "hkpd.ldb"
333 paths.hkpt = "hkpt.ldb"
335 paths.sysvol = lp.get("path", "sysvol")
337 paths.netlogon = lp.get("path", "netlogon")
339 paths.smbconf = lp.configfile
344 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
345 serverrole=None, rootdn=None, domaindn=None, configdn=None,
346 schemadn=None, serverdn=None, sitename=None):
347 """Guess configuration settings to use."""
350 hostname = socket.gethostname().split(".")[0]
352 netbiosname = lp.get("netbios name")
353 if netbiosname is None:
354 netbiosname = hostname
355 assert netbiosname is not None
356 netbiosname = netbiosname.upper()
357 if not valid_netbios_name(netbiosname):
358 raise InvalidNetbiosName(netbiosname)
360 if dnsdomain is None:
361 dnsdomain = lp.get("realm")
362 assert dnsdomain is not None
363 dnsdomain = dnsdomain.lower()
365 if serverrole is None:
366 serverrole = lp.get("server role")
367 assert serverrole is not None
368 serverrole = serverrole.lower()
370 realm = dnsdomain.upper()
372 if lp.get("realm").upper() != realm:
373 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
375 if serverrole == "domain controller":
377 domain = lp.get("workgroup")
378 assert domain is not None
379 domain = domain.upper()
381 if lp.get("workgroup").upper() != domain:
382 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
385 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
389 domaindn = "DC=" + netbiosname
391 if not valid_netbios_name(domain):
392 raise InvalidNetbiosName(domain)
394 if hostname.upper() == realm:
395 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
396 if netbiosname == realm:
397 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
399 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
405 configdn = "CN=Configuration," + rootdn
407 schemadn = "CN=Schema," + configdn
412 names = ProvisionNames()
413 names.rootdn = rootdn
414 names.domaindn = domaindn
415 names.configdn = configdn
416 names.schemadn = schemadn
417 names.ldapmanagerdn = "CN=Manager," + rootdn
418 names.dnsdomain = dnsdomain
419 names.domain = domain
421 names.netbiosname = netbiosname
422 names.hostname = hostname
423 names.sitename = sitename
424 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
429 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
430 targetdir, sid_generator):
431 """Create a new smb.conf file based on a couple of basic settings.
433 assert smbconf is not None
435 hostname = socket.gethostname().split(".")[0]
436 netbiosname = hostname.upper()
438 if serverrole is None:
439 serverrole = "standalone"
441 assert serverrole in ("domain controller", "member server", "standalone")
442 if serverrole == "domain controller":
444 elif serverrole == "member server":
445 smbconfsuffix = "member"
446 elif serverrole == "standalone":
447 smbconfsuffix = "standalone"
449 if sid_generator is None:
450 sid_generator = "internal"
452 assert domain is not None
453 domain = domain.upper()
455 assert realm is not None
456 realm = realm.upper()
458 default_lp = param.LoadParm()
459 #Load non-existant file
460 if os.path.exists(smbconf):
461 default_lp.load(smbconf)
463 if targetdir is not None:
464 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
465 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
467 default_lp.set("lock dir", os.path.abspath(targetdir))
472 if sid_generator == "internal":
473 sid_generator_line = ""
475 sid_generator_line = "sid generator = " + sid_generator
477 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
478 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
480 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
482 "NETBIOS_NAME": netbiosname,
485 "SERVERROLE": serverrole,
486 "NETLOGONPATH": netlogon,
487 "SYSVOLPATH": sysvol,
488 "SIDGENERATOR_LINE": sid_generator_line,
489 "PRIVATEDIR_LINE": privatedir_line,
490 "LOCKDIR_LINE": lockdir_line
494 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
495 users_gid, wheel_gid):
496 """setup reasonable name mappings for sam names to unix names.
498 :param samdb: SamDB object.
499 :param idmap: IDmap db object.
500 :param sid: The domain sid.
501 :param domaindn: The domain DN.
502 :param root_uid: uid of the UNIX root user.
503 :param nobody_uid: uid of the UNIX nobody user.
504 :param users_gid: gid of the UNIX users group.
505 :param wheel_gid: gid of the UNIX wheel group."""
507 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
508 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
510 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
511 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
513 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
514 provision_backend, names, schema,
517 """Setup the partitions for the SAM database.
519 Alternatively, provision() may call this, and then populate the database.
521 :note: This will wipe the Sam Database!
523 :note: This function always removes the local SAM LDB file. The erase
524 parameter controls whether to erase the existing data, which
525 may not be stored locally but in LDAP.
528 assert session_info is not None
530 old_partitions = None
531 new_partitions = None
533 # We use options=["modules:"] to stop the modules loading - we
534 # just want to wipe and re-initialise the database, not start it up
537 os.unlink(samdb_path)
541 samdb = Ldb(url=samdb_path, session_info=session_info,
542 lp=lp, options=["modules:"])
544 ldap_backend_line = "# No LDAP backend"
545 if provision_backend.type is not "ldb":
546 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
548 samdb.transaction_start()
550 message("Setting up sam.ldb partitions and settings")
551 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
552 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
553 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
554 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
555 "LDAP_BACKEND_LINE": ldap_backend_line,
559 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
560 "BACKEND_TYPE": provision_backend.type,
561 "SERVER_ROLE": serverrole
564 message("Setting up sam.ldb rootDSE")
565 setup_samdb_rootdse(samdb, setup_path, names)
568 samdb.transaction_cancel()
571 samdb.transaction_commit()
574 def secretsdb_self_join(secretsdb, domain,
575 netbiosname, domainsid, machinepass,
576 realm=None, dnsdomain=None,
578 key_version_number=1,
579 secure_channel_type=SEC_CHAN_WKSTA):
580 """Add domain join-specific bits to a secrets database.
582 :param secretsdb: Ldb Handle to the secrets database
583 :param machinepass: Machine password
585 attrs=["whenChanged",
593 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
594 msg["secureChannelType"] = str(secure_channel_type)
595 msg["flatname"] = [domain]
596 msg["objectClass"] = ["top", "primaryDomain"]
597 if realm is not None:
598 if dnsdomain is None:
599 dnsdomain = realm.lower()
600 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
602 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
603 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
604 msg["privateKeytab"] = ["secrets.keytab"];
607 msg["secret"] = [machinepass]
608 msg["samAccountName"] = ["%s$" % netbiosname]
609 msg["secureChannelType"] = [str(secure_channel_type)]
610 msg["objectSid"] = [ndr_pack(domainsid)]
612 res = secretsdb.search(base="cn=Primary Domains",
614 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
615 scope=SCOPE_ONELEVEL)
618 if del_msg.dn is not msg.dn:
619 secretsdb.delete(del_msg.dn)
621 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
624 msg["priorSecret"] = res[0]["secret"]
625 msg["priorWhenChanged"] = res[0]["whenChanged"]
627 if res["privateKeytab"] is not None:
628 msg["privateKeytab"] = res[0]["privateKeytab"]
630 if res["krb5Keytab"] is not None:
631 msg["krb5Keytab"] = res[0]["krb5Keytab"]
634 el.set_flags(ldb.FLAG_MOD_REPLACE)
635 secretsdb.modify(msg)
640 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
641 dns_keytab_path, dnspass):
642 """Add DNS specific bits to a secrets database.
644 :param secretsdb: Ldb Handle to the secrets database
645 :param setup_path: Setup path function
646 :param machinepass: Machine password
648 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
650 "DNSDOMAIN": dnsdomain,
651 "DNS_KEYTAB": dns_keytab_path,
652 "DNSPASS_B64": b64encode(dnspass),
656 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
657 """Setup the secrets database.
659 :param path: Path to the secrets database.
660 :param setup_path: Get the path to a setup file.
661 :param session_info: Session info.
662 :param credentials: Credentials
663 :param lp: Loadparm context
664 :return: LDB handle for the created secrets database
666 if os.path.exists(path):
668 secrets_ldb = Ldb(path, session_info=session_info,
671 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
672 secrets_ldb = Ldb(path, session_info=session_info,
674 secrets_ldb.transaction_start()
675 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
677 if backend_credentials is not None and backend_credentials.authentication_requested():
678 if backend_credentials.get_bind_dn() is not None:
679 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
680 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
681 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
684 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
685 "LDAPADMINUSER": backend_credentials.get_username(),
686 "LDAPADMINREALM": backend_credentials.get_realm(),
687 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
692 def setup_privileges(path, setup_path, session_info, lp):
693 """Setup the privileges database.
695 :param path: Path to the privileges database.
696 :param setup_path: Get the path to a setup file.
697 :param session_info: Session info.
698 :param credentials: Credentials
699 :param lp: Loadparm context
700 :return: LDB handle for the created secrets database
702 if os.path.exists(path):
704 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
705 privilege_ldb.erase()
706 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
709 def setup_registry(path, setup_path, session_info, lp):
710 """Setup the registry.
712 :param path: Path to the registry database
713 :param setup_path: Function that returns the path to a setup.
714 :param session_info: Session information
715 :param credentials: Credentials
716 :param lp: Loadparm context
718 reg = registry.Registry()
719 hive = registry.open_ldb(path, session_info=session_info,
721 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
722 provision_reg = setup_path("provision.reg")
723 assert os.path.exists(provision_reg)
724 reg.diff_apply(provision_reg)
727 def setup_idmapdb(path, setup_path, session_info, lp):
728 """Setup the idmap database.
730 :param path: path to the idmap database
731 :param setup_path: Function that returns a path to a setup file
732 :param session_info: Session information
733 :param credentials: Credentials
734 :param lp: Loadparm context
736 if os.path.exists(path):
739 idmap_ldb = IDmapDB(path, session_info=session_info,
743 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
747 def setup_samdb_rootdse(samdb, setup_path, names):
748 """Setup the SamDB rootdse.
750 :param samdb: Sam Database handle
751 :param setup_path: Obtain setup path
753 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
754 "SCHEMADN": names.schemadn,
755 "NETBIOSNAME": names.netbiosname,
756 "DNSDOMAIN": names.dnsdomain,
757 "REALM": names.realm,
758 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
759 "DOMAINDN": names.domaindn,
760 "ROOTDN": names.rootdn,
761 "CONFIGDN": names.configdn,
762 "SERVERDN": names.serverdn,
766 def setup_self_join(samdb, names,
767 machinepass, dnspass,
768 domainsid, invocationid, setup_path,
769 policyguid, policyguid_dc, domainControllerFunctionality,
771 """Join a host to its own domain."""
772 assert isinstance(invocationid, str)
773 if ntdsguid is not None:
774 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
777 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
778 "CONFIGDN": names.configdn,
779 "SCHEMADN": names.schemadn,
780 "DOMAINDN": names.domaindn,
781 "SERVERDN": names.serverdn,
782 "INVOCATIONID": invocationid,
783 "NETBIOSNAME": names.netbiosname,
784 "DEFAULTSITE": names.sitename,
785 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
786 "MACHINEPASS_B64": b64encode(machinepass),
787 "DNSPASS_B64": b64encode(dnspass),
788 "REALM": names.realm,
789 "DOMAIN": names.domain,
790 "DNSDOMAIN": names.dnsdomain,
791 "SAMBA_VERSION_STRING": version,
792 "NTDSGUID": ntdsguid_line,
793 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
795 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
796 "POLICYGUID": policyguid,
797 "POLICYGUID_DC": policyguid_dc,
798 "DNSDOMAIN": names.dnsdomain,
799 "DOMAINSID": str(domainsid),
800 "DOMAINDN": names.domaindn})
802 # add the NTDSGUID based SPNs
803 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
804 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
805 expression="", scope=SCOPE_BASE)
806 assert isinstance(names.ntdsguid, str)
808 # Setup fSMORoleOwner entries to point at the newly created DC entry
809 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
810 "DOMAIN": names.domain,
811 "DNSDOMAIN": names.dnsdomain,
812 "DOMAINDN": names.domaindn,
813 "CONFIGDN": names.configdn,
814 "SCHEMADN": names.schemadn,
815 "DEFAULTSITE": names.sitename,
816 "SERVERDN": names.serverdn,
817 "NETBIOSNAME": names.netbiosname,
818 "NTDSGUID": names.ntdsguid
822 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
824 domainsid, domainguid, policyguid, policyguid_dc,
825 fill, adminpass, krbtgtpass,
826 machinepass, invocationid, dnspass, ntdsguid,
827 serverrole, dom_for_fun_level=None,
829 """Setup a complete SAM Database.
831 :note: This will wipe the main SAM database file!
834 # ATTENTION: Do NOT change these default values without discussion with the
835 # team and/or release manager. They have a big impact on the whole program!
836 domainControllerFunctionality = DS_DC_FUNCTION_2008
838 if dom_for_fun_level is None:
839 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
840 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
841 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
843 if dom_for_fun_level > domainControllerFunctionality:
844 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!")
846 domainFunctionality = dom_for_fun_level
847 forestFunctionality = dom_for_fun_level
849 # Also wipes the database
850 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
851 provision_backend=provision_backend, session_info=session_info,
853 serverrole=serverrole, schema=schema)
856 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
858 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
859 samdb = Ldb(session_info=session_info,
860 credentials=provision_backend.credentials, lp=lp)
862 message("Pre-loading the Samba 4 and AD schema")
864 # Load the schema from the one we computed earlier
865 samdb.set_schema_from_ldb(schema.ldb)
867 # And now we can connect to the DB - the schema won't be loaded from the DB
873 samdb.transaction_start()
875 # Set the domain functionality levels onto the database.
876 # Various module (the password_hash module in particular) need
877 # to know what level of AD we are emulating.
879 # These will be fixed into the database via the database
880 # modifictions below, but we need them set from the start.
881 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
882 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
883 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
885 samdb.set_domain_sid(str(domainsid))
886 if serverrole == "domain controller":
887 samdb.set_invocation_id(invocationid)
889 message("Adding DomainDN: %s" % names.domaindn)
891 #impersonate domain admin
892 admin_session_info = admin_session(lp, str(domainsid))
893 samdb.set_session_info(admin_session_info)
894 if domainguid is not None:
895 domainguid_line = "objectGUID: %s\n-" % domainguid
899 descr = get_domain_descriptor(domainsid)
900 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
901 "DOMAINDN": names.domaindn,
902 "DOMAINGUID": domainguid_line,
907 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
908 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
909 "DOMAINSID": str(domainsid),
910 "SCHEMADN": names.schemadn,
911 "NETBIOSNAME": names.netbiosname,
912 "DEFAULTSITE": names.sitename,
913 "CONFIGDN": names.configdn,
914 "SERVERDN": names.serverdn,
915 "POLICYGUID": policyguid,
916 "DOMAINDN": names.domaindn,
917 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
918 "SAMBA_VERSION_STRING": version
921 message("Adding configuration container")
922 descr = get_config_descriptor(domainsid);
923 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
924 "CONFIGDN": names.configdn,
928 # The LDIF here was created when the Schema object was constructed
929 message("Setting up sam.ldb schema")
930 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
931 samdb.modify_ldif(schema.schema_dn_modify)
932 samdb.write_prefixes_from_schema()
933 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
934 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
935 {"SCHEMADN": names.schemadn})
937 message("Setting up sam.ldb configuration data")
938 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
939 "CONFIGDN": names.configdn,
940 "NETBIOSNAME": names.netbiosname,
941 "DEFAULTSITE": names.sitename,
942 "DNSDOMAIN": names.dnsdomain,
943 "DOMAIN": names.domain,
944 "SCHEMADN": names.schemadn,
945 "DOMAINDN": names.domaindn,
946 "SERVERDN": names.serverdn,
947 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
950 message("Setting up display specifiers")
951 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
952 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
953 check_all_substituted(display_specifiers_ldif)
954 samdb.add_ldif(display_specifiers_ldif)
956 message("Adding users container")
957 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
958 "DOMAINDN": names.domaindn})
959 message("Modifying users container")
960 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
961 "DOMAINDN": names.domaindn})
962 message("Adding computers container")
963 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
964 "DOMAINDN": names.domaindn})
965 message("Modifying computers container")
966 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
967 "DOMAINDN": names.domaindn})
968 message("Setting up sam.ldb data")
969 setup_add_ldif(samdb, setup_path("provision.ldif"), {
970 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
971 "DOMAINDN": names.domaindn,
972 "NETBIOSNAME": names.netbiosname,
973 "DEFAULTSITE": names.sitename,
974 "CONFIGDN": names.configdn,
975 "SERVERDN": names.serverdn,
976 "POLICYGUID_DC": policyguid_dc
979 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
980 "DOMAINDN": names.domaindn})
982 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
983 "CONFIGDN": names.configdn,
984 "SCHEMADN": names.schemadn})
985 if fill == FILL_FULL:
986 message("Setting up sam.ldb users and groups")
987 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
988 "DOMAINDN": names.domaindn,
989 "DOMAINSID": str(domainsid),
990 "CONFIGDN": names.configdn,
991 "ADMINPASS_B64": b64encode(adminpass),
992 "KRBTGTPASS_B64": b64encode(krbtgtpass),
995 if serverrole == "domain controller":
996 message("Setting up self join")
997 setup_self_join(samdb, names=names, invocationid=invocationid,
999 machinepass=machinepass,
1000 domainsid=domainsid, policyguid=policyguid,
1001 policyguid_dc=policyguid_dc,
1002 setup_path=setup_path,
1003 domainControllerFunctionality=domainControllerFunctionality,
1006 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1007 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1008 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1009 assert isinstance(names.ntdsguid, str)
1012 samdb.transaction_cancel()
1015 samdb.transaction_commit()
1020 FILL_NT4SYNC = "NT4SYNC"
1024 def provision(setup_dir, message, session_info,
1025 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1027 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1029 domain=None, hostname=None, hostip=None, hostip6=None,
1030 domainsid=None, adminpass=None, ldapadminpass=None,
1031 krbtgtpass=None, domainguid=None,
1032 policyguid=None, policyguid_dc=None, invocationid=None,
1033 machinepass=None, ntdsguid=None,
1034 dnspass=None, root=None, nobody=None, users=None,
1035 wheel=None, backup=None, aci=None, serverrole=None,
1036 dom_for_fun_level=None,
1037 ldap_backend_extra_port=None, backend_type=None,
1039 ol_mmr_urls=None, ol_olc=None,
1040 setup_ds_path=None, slapd_path=None, nosync=False,
1041 ldap_dryrun_mode=False):
1044 :note: caution, this wipes all existing data!
1047 def setup_path(file):
1048 return os.path.join(setup_dir, file)
1050 if domainsid is None:
1051 domainsid = security.random_sid()
1053 domainsid = security.dom_sid(domainsid)
1055 # create/adapt the group policy GUIDs
1056 if policyguid is None:
1057 policyguid = str(uuid.uuid4())
1058 policyguid = policyguid.upper()
1059 if policyguid_dc is None:
1060 policyguid_dc = str(uuid.uuid4())
1061 policyguid_dc = policyguid_dc.upper()
1063 if adminpass is None:
1064 adminpass = glue.generate_random_str(12)
1065 if krbtgtpass is None:
1066 krbtgtpass = glue.generate_random_str(12)
1067 if machinepass is None:
1068 machinepass = glue.generate_random_str(12)
1070 dnspass = glue.generate_random_str(12)
1071 if ldapadminpass is None:
1072 #Make a new, random password between Samba and it's LDAP server
1073 ldapadminpass=glue.generate_random_str(12)
1075 if backend_type is None:
1076 backend_type = "ldb"
1078 sid_generator = "internal"
1079 if backend_type == "fedora-ds":
1080 sid_generator = "backend"
1082 root_uid = findnss_uid([root or "root"])
1083 nobody_uid = findnss_uid([nobody or "nobody"])
1084 users_gid = findnss_gid([users or "users"])
1086 wheel_gid = findnss_gid(["wheel", "adm"])
1088 wheel_gid = findnss_gid([wheel])
1090 if targetdir is not None:
1091 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1092 os.makedirs(os.path.join(targetdir, "etc"))
1093 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1094 elif smbconf is None:
1095 smbconf = param.default_path()
1097 # only install a new smb.conf if there isn't one there already
1098 if not os.path.exists(smbconf):
1099 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1100 targetdir, sid_generator)
1102 lp = param.LoadParm()
1105 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1106 dnsdomain=realm, serverrole=serverrole,
1107 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1108 serverdn=serverdn, sitename=sitename)
1110 paths = provision_paths_from_lp(lp, names.dnsdomain)
1114 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1115 except socket.gaierror, (socket.EAI_NODATA, msg):
1120 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1121 except socket.gaierror, (socket.EAI_NODATA, msg):
1124 if serverrole is None:
1125 serverrole = lp.get("server role")
1127 assert serverrole in ("domain controller", "member server", "standalone")
1128 if invocationid is None and serverrole == "domain controller":
1129 invocationid = str(uuid.uuid4())
1131 if not os.path.exists(paths.private_dir):
1132 os.mkdir(paths.private_dir)
1133 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1134 os.mkdir(os.path.join(paths.private_dir,"tls"))
1136 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1138 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1140 if backend_type == "ldb":
1141 provision_backend = LDBBackend(backend_type,
1142 paths=paths, setup_path=setup_path,
1143 lp=lp, credentials=credentials,
1146 elif backend_type == "existing":
1147 provision_backend = ExistingBackend(backend_type,
1148 paths=paths, setup_path=setup_path,
1149 lp=lp, credentials=credentials,
1152 elif backend_type == "fedora-ds":
1153 provision_backend = FDSBackend(backend_type,
1154 paths=paths, setup_path=setup_path,
1155 lp=lp, credentials=credentials,
1158 domainsid=domainsid,
1161 ldapadminpass=ldapadminpass,
1162 slapd_path=slapd_path,
1163 ldap_backend_extra_port=ldap_backend_extra_port,
1164 ldap_dryrun_mode=ldap_dryrun_mode,
1166 setup_ds_path=setup_ds_path)
1167 elif backend_type == "openldap":
1168 provision_backend = OpenLDAPBackend(backend_type,
1169 paths=paths, setup_path=setup_path,
1170 lp=lp, credentials=credentials,
1173 domainsid=domainsid,
1176 ldapadminpass=ldapadminpass,
1177 slapd_path=slapd_path,
1178 ldap_backend_extra_port=ldap_backend_extra_port,
1179 ldap_dryrun_mode=ldap_dryrun_mode,
1180 ol_mmr_urls=ol_mmr_urls,
1183 raise ProvisioningError("Unknown LDAP backend type selected")
1185 provision_backend.init()
1186 provision_backend.start()
1188 # only install a new shares config db if there is none
1189 if not os.path.exists(paths.shareconf):
1190 message("Setting up share.ldb")
1191 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1193 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1196 message("Setting up secrets.ldb")
1197 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1198 session_info=session_info,
1199 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1201 message("Setting up the registry")
1202 setup_registry(paths.hklm, setup_path, session_info,
1205 message("Setting up the privileges database")
1206 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1208 message("Setting up idmap db")
1209 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1212 message("Setting up SAM db")
1213 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1214 provision_backend, lp, names,
1216 domainsid=domainsid,
1217 schema=schema, domainguid=domainguid,
1218 policyguid=policyguid, policyguid_dc=policyguid_dc,
1220 adminpass=adminpass, krbtgtpass=krbtgtpass,
1221 invocationid=invocationid,
1222 machinepass=machinepass, dnspass=dnspass,
1223 ntdsguid=ntdsguid, serverrole=serverrole,
1224 dom_for_fun_level=dom_for_fun_level)
1226 if serverrole == "domain controller":
1227 if paths.netlogon is None:
1228 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1229 message("Please either remove %s or see the template at %s" %
1230 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1231 assert(paths.netlogon is not None)
1233 if paths.sysvol is None:
1234 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1235 message("Please either remove %s or see the template at %s" %
1236 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1237 assert(paths.sysvol is not None)
1239 # Set up group policies (domain policy and domain controller policy)
1241 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1242 "{" + policyguid + "}")
1243 os.makedirs(policy_path, 0755)
1244 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1245 "[General]\r\nVersion=65543")
1246 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1247 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1249 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1250 "{" + policyguid_dc + "}")
1251 os.makedirs(policy_path_dc, 0755)
1252 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1253 "[General]\r\nVersion=2")
1254 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1255 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1257 if not os.path.isdir(paths.netlogon):
1258 os.makedirs(paths.netlogon, 0755)
1260 if samdb_fill == FILL_FULL:
1261 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1262 root_uid=root_uid, nobody_uid=nobody_uid,
1263 users_gid=users_gid, wheel_gid=wheel_gid)
1265 message("Setting up sam.ldb rootDSE marking as synchronized")
1266 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1268 # Only make a zone file on the first DC, it should be replicated with DNS replication
1269 if serverrole == "domain controller":
1270 secretsdb_self_join(secrets_ldb, domain=domain,
1272 dnsdomain=names.dnsdomain,
1273 netbiosname=names.netbiosname,
1274 domainsid=domainsid,
1275 machinepass=machinepass,
1276 secure_channel_type=SEC_CHAN_BDC)
1278 secretsdb_setup_dns(secrets_ldb, setup_path,
1279 realm=names.realm, dnsdomain=names.dnsdomain,
1280 dns_keytab_path=paths.dns_keytab,
1283 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1284 assert isinstance(domainguid, str)
1286 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1288 hostip6=hostip6, hostname=names.hostname,
1290 domainguid=domainguid, ntdsguid=names.ntdsguid)
1292 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1293 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1295 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1296 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1297 keytab_name=paths.dns_keytab)
1298 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1299 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1301 create_krb5_conf(paths.krb5conf, setup_path,
1302 dnsdomain=names.dnsdomain, hostname=names.hostname,
1304 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1306 provision_backend.post_setup()
1307 provision_backend.shutdown()
1309 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1312 #Now commit the secrets.ldb to disk
1313 secrets_ldb.transaction_commit()
1315 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1317 message("Once the above files are installed, your Samba4 server will be ready to use")
1318 message("Server Role: %s" % serverrole)
1319 message("Hostname: %s" % names.hostname)
1320 message("NetBIOS Domain: %s" % names.domain)
1321 message("DNS Domain: %s" % names.dnsdomain)
1322 message("DOMAIN SID: %s" % str(domainsid))
1323 if samdb_fill == FILL_FULL:
1324 message("Admin password: %s" % adminpass)
1325 if provision_backend.type is not "ldb":
1326 if provision_backend.credentials.get_bind_dn() is not None:
1327 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1329 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1331 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1333 if provision_backend.slapd_command_escaped is not None:
1334 # now display slapd_command_file.txt to show how slapd must be started next time
1335 message("Use later the following commandline to start slapd, then Samba:")
1336 message(provision_backend.slapd_command_escaped)
1337 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1340 result = ProvisionResult()
1341 result.domaindn = domaindn
1342 result.paths = paths
1344 result.samdb = samdb
1349 def provision_become_dc(setup_dir=None,
1350 smbconf=None, targetdir=None, realm=None,
1351 rootdn=None, domaindn=None, schemadn=None,
1352 configdn=None, serverdn=None,
1353 domain=None, hostname=None, domainsid=None,
1354 adminpass=None, krbtgtpass=None, domainguid=None,
1355 policyguid=None, policyguid_dc=None, invocationid=None,
1357 dnspass=None, root=None, nobody=None, users=None,
1358 wheel=None, backup=None, serverrole=None,
1359 ldap_backend=None, ldap_backend_type=None,
1360 sitename=None, debuglevel=1):
1363 """print a message if quiet is not set."""
1366 glue.set_debug_level(debuglevel)
1368 return provision(setup_dir, message, system_session(), None,
1369 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1370 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1371 configdn=configdn, serverdn=serverdn, domain=domain,
1372 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1373 machinepass=machinepass, serverrole="domain controller",
1377 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1378 """Create a PHP LDAP admin configuration file.
1380 :param path: Path to write the configuration to.
1381 :param setup_path: Function to generate setup paths.
1383 setup_file(setup_path("phpldapadmin-config.php"), path,
1384 {"S4_LDAPI_URI": ldapi_uri})
1387 def create_zone_file(path, setup_path, dnsdomain,
1388 hostip, hostip6, hostname, realm, domainguid,
1390 """Write out a DNS zone file, from the info in the current database.
1392 :param path: Path of the new zone file.
1393 :param setup_path: Setup path function.
1394 :param dnsdomain: DNS Domain name
1395 :param domaindn: DN of the Domain
1396 :param hostip: Local IPv4 IP
1397 :param hostip6: Local IPv6 IP
1398 :param hostname: Local hostname
1399 :param realm: Realm name
1400 :param domainguid: GUID of the domain.
1401 :param ntdsguid: GUID of the hosts nTDSDSA record.
1403 assert isinstance(domainguid, str)
1405 if hostip6 is not None:
1406 hostip6_base_line = " IN AAAA " + hostip6
1407 hostip6_host_line = hostname + " IN AAAA " + hostip6
1409 hostip6_base_line = ""
1410 hostip6_host_line = ""
1412 if hostip is not None:
1413 hostip_base_line = " IN A " + hostip
1414 hostip_host_line = hostname + " IN A " + hostip
1416 hostip_base_line = ""
1417 hostip_host_line = ""
1419 setup_file(setup_path("provision.zone"), path, {
1420 "HOSTNAME": hostname,
1421 "DNSDOMAIN": dnsdomain,
1423 "HOSTIP_BASE_LINE": hostip_base_line,
1424 "HOSTIP_HOST_LINE": hostip_host_line,
1425 "DOMAINGUID": domainguid,
1426 "DATESTRING": time.strftime("%Y%m%d%H"),
1427 "DEFAULTSITE": DEFAULTSITE,
1428 "NTDSGUID": ntdsguid,
1429 "HOSTIP6_BASE_LINE": hostip6_base_line,
1430 "HOSTIP6_HOST_LINE": hostip6_host_line,
1434 def create_named_conf(path, setup_path, realm, dnsdomain,
1436 """Write out a file containing zone statements suitable for inclusion in a
1437 named.conf file (including GSS-TSIG configuration).
1439 :param path: Path of the new named.conf file.
1440 :param setup_path: Setup path function.
1441 :param realm: Realm name
1442 :param dnsdomain: DNS Domain name
1443 :param private_dir: Path to private directory
1444 :param keytab_name: File name of DNS keytab file
1447 setup_file(setup_path("named.conf"), path, {
1448 "DNSDOMAIN": dnsdomain,
1450 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1451 "PRIVATE_DIR": private_dir
1454 def create_named_txt(path, setup_path, realm, dnsdomain,
1455 private_dir, keytab_name):
1456 """Write out a file containing zone statements suitable for inclusion in a
1457 named.conf file (including GSS-TSIG configuration).
1459 :param path: Path of the new named.conf file.
1460 :param setup_path: Setup path function.
1461 :param realm: Realm name
1462 :param dnsdomain: DNS Domain name
1463 :param private_dir: Path to private directory
1464 :param keytab_name: File name of DNS keytab file
1467 setup_file(setup_path("named.txt"), path, {
1468 "DNSDOMAIN": dnsdomain,
1470 "DNS_KEYTAB": keytab_name,
1471 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1472 "PRIVATE_DIR": private_dir
1475 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1476 """Write out a file containing zone statements suitable for inclusion in a
1477 named.conf file (including GSS-TSIG configuration).
1479 :param path: Path of the new named.conf file.
1480 :param setup_path: Setup path function.
1481 :param dnsdomain: DNS Domain name
1482 :param hostname: Local hostname
1483 :param realm: Realm name
1486 setup_file(setup_path("krb5.conf"), path, {
1487 "DNSDOMAIN": dnsdomain,
1488 "HOSTNAME": hostname,