2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
41 from samba.auth import system_session, admin_session
43 from samba import version, Ldb, substitute_var, valid_netbios_name
44 from samba import check_all_substituted, read_and_sub_file, setup_file
45 from samba.dsdb 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.ms_display_specifiers import read_ms_ldif
50 from samba.ntacls import setntacl, dsacl2fsacl
51 from samba.ndr import ndr_pack,ndr_unpack
52 from samba.provisionbackend import (
60 from samba.schema import Schema
61 from samba.samdb import SamDB
63 __docformat__ = "restructuredText"
66 """Find the setup directory used by provision."""
67 dirname = os.path.dirname(__file__)
68 if "/site-packages/" in dirname:
69 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
70 for suffix in ["share/setup", "share/samba/setup", "setup"]:
71 ret = os.path.join(prefix, suffix)
72 if os.path.isdir(ret):
75 ret = os.path.join(dirname, "../../../setup")
76 if os.path.isdir(ret):
78 raise Exception("Unable to find setup directory.")
80 # descriptors of the naming contexts
81 # hard coded at this point, but will probably be changed when
82 # we enable different fsmo roles
84 def get_config_descriptor(domain_sid):
85 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
86 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
87 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
88 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
89 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
90 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
91 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
92 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
93 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
94 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
95 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
96 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
97 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
98 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
99 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
100 sec = security.descriptor.from_sddl(sddl, domain_sid)
103 def get_domain_descriptor(domain_sid):
104 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
105 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
106 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
107 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
108 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
109 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
110 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
111 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
112 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
113 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
114 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
115 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
116 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
117 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
118 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
119 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
120 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
121 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
122 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
123 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
124 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
125 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
126 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
127 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
128 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
129 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
130 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
131 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
132 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
133 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
134 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
135 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
136 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
137 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
138 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
139 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
140 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
141 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
142 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
145 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
147 "(A;;RPLCLORC;;;ED)" \
148 "(A;;RPLCLORC;;;AU)" \
149 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
150 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
151 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
152 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
153 sec = security.descriptor.from_sddl(sddl, domain_sid)
156 DEFAULTSITE = "Default-First-Site-Name"
158 class ProvisionPaths(object):
161 self.shareconf = None
172 self.dns_keytab = None
175 self.private_dir = None
178 class ProvisionNames(object):
185 self.ldapmanagerdn = None
186 self.dnsdomain = None
188 self.netbiosname = None
195 class ProvisionResult(object):
204 def check_install(lp, session_info, credentials):
205 """Check whether the current install seems ok.
207 :param lp: Loadparm context
208 :param session_info: Session information
209 :param credentials: Credentials
211 if lp.get("realm") == "":
212 raise Exception("Realm empty")
213 samdb = Ldb(lp.get("sam database"), session_info=session_info,
214 credentials=credentials, lp=lp)
215 if len(samdb.search("(cn=Administrator)")) != 1:
216 raise ProvisioningError("No administrator account found")
219 def findnss(nssfn, names):
220 """Find a user or group from a list of possibilities.
222 :param nssfn: NSS Function to try (should raise KeyError if not found)
223 :param names: Names to check.
224 :return: Value return by first names list.
231 raise KeyError("Unable to find user/group in %r" % names)
234 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
235 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
238 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
239 """Setup a ldb in the private dir.
241 :param ldb: LDB file to import data into
242 :param ldif_path: Path of the LDIF file to load
243 :param subst_vars: Optional variables to subsitute in LDIF.
244 :param nocontrols: Optional list of controls, can be None for no controls
246 assert isinstance(ldif_path, str)
247 data = read_and_sub_file(ldif_path, subst_vars)
248 ldb.add_ldif(data, controls)
251 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
252 """Modify a ldb in the private dir.
254 :param ldb: LDB object.
255 :param ldif_path: LDIF file path.
256 :param subst_vars: Optional dictionary with substitution variables.
258 data = read_and_sub_file(ldif_path, subst_vars)
259 ldb.modify_ldif(data)
262 def setup_ldb(ldb, ldif_path, subst_vars):
263 """Import a LDIF a file into a LDB handle, optionally substituting variables.
265 :note: Either all LDIF data will be added or none (using transactions).
267 :param ldb: LDB file to import into.
268 :param ldif_path: Path to the LDIF file.
269 :param subst_vars: Dictionary with substitution variables.
271 assert ldb is not None
272 ldb.transaction_start()
274 setup_add_ldif(ldb, ldif_path, subst_vars)
276 ldb.transaction_cancel()
279 ldb.transaction_commit()
282 def provision_paths_from_lp(lp, dnsdomain):
283 """Set the default paths for provisioning.
285 :param lp: Loadparm context.
286 :param dnsdomain: DNS Domain name
288 paths = ProvisionPaths()
289 paths.private_dir = lp.get("private dir")
291 # This is stored without path prefix for the "privateKeytab" attribute in
292 # "secrets_dns.ldif".
293 paths.dns_keytab = "dns.keytab"
295 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
296 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
297 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
298 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
299 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
300 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
301 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
302 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
303 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
304 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
305 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
306 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
307 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
308 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
309 paths.phpldapadminconfig = os.path.join(paths.private_dir,
310 "phpldapadmin-config.php")
311 paths.hklm = "hklm.ldb"
312 paths.hkcr = "hkcr.ldb"
313 paths.hkcu = "hkcu.ldb"
314 paths.hku = "hku.ldb"
315 paths.hkpd = "hkpd.ldb"
316 paths.hkpt = "hkpt.ldb"
317 paths.sysvol = lp.get("path", "sysvol")
318 paths.netlogon = lp.get("path", "netlogon")
319 paths.smbconf = lp.configfile
323 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
324 serverrole=None, rootdn=None, domaindn=None, configdn=None,
325 schemadn=None, serverdn=None, sitename=None):
326 """Guess configuration settings to use."""
329 hostname = socket.gethostname().split(".")[0]
331 netbiosname = lp.get("netbios name")
332 if netbiosname is None:
333 netbiosname = hostname
334 assert netbiosname is not None
335 netbiosname = netbiosname.upper()
336 if not valid_netbios_name(netbiosname):
337 raise InvalidNetbiosName(netbiosname)
339 if dnsdomain is None:
340 dnsdomain = lp.get("realm")
341 if dnsdomain is None or dnsdomain == "":
342 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
344 dnsdomain = dnsdomain.lower()
346 if serverrole is None:
347 serverrole = lp.get("server role")
348 if serverrole is None:
349 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
351 serverrole = serverrole.lower()
353 realm = dnsdomain.upper()
355 if lp.get("realm") == "":
356 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
358 if lp.get("realm").upper() != realm:
359 raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
361 if lp.get("server role").lower() != serverrole:
362 raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role").upper(), serverrole, lp.configfile))
364 if serverrole == "domain controller":
366 # This will, for better or worse, default to 'WORKGROUP'
367 domain = lp.get("workgroup")
368 domain = domain.upper()
370 if lp.get("workgroup").upper() != domain:
371 raise ProvisioningError("guess_names: Workgroup '%s' in %s must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
374 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
378 domaindn = "DC=" + netbiosname
380 if not valid_netbios_name(domain):
381 raise InvalidNetbiosName(domain)
383 if hostname.upper() == realm:
384 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
385 if netbiosname == realm:
386 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
388 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
394 configdn = "CN=Configuration," + rootdn
396 schemadn = "CN=Schema," + configdn
401 names = ProvisionNames()
402 names.rootdn = rootdn
403 names.domaindn = domaindn
404 names.configdn = configdn
405 names.schemadn = schemadn
406 names.ldapmanagerdn = "CN=Manager," + rootdn
407 names.dnsdomain = dnsdomain
408 names.domain = domain
410 names.netbiosname = netbiosname
411 names.hostname = hostname
412 names.sitename = sitename
413 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
418 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
419 targetdir, sid_generator="internal", eadb=False):
420 """Create a new smb.conf file based on a couple of basic settings.
422 assert smbconf is not None
424 hostname = socket.gethostname().split(".")[0]
425 netbiosname = hostname.upper()
427 if serverrole is None:
428 serverrole = "standalone"
430 assert serverrole in ("domain controller", "member server", "standalone")
431 if serverrole == "domain controller":
433 elif serverrole == "member server":
434 smbconfsuffix = "member"
435 elif serverrole == "standalone":
436 smbconfsuffix = "standalone"
438 if sid_generator is None:
439 sid_generator = "internal"
441 assert domain is not None
442 domain = domain.upper()
444 assert realm is not None
445 realm = realm.upper()
447 default_lp = samba.param.LoadParm()
448 #Load non-existant file
449 if os.path.exists(smbconf):
450 default_lp.load(smbconf)
452 if targetdir is not None:
453 privdir = os.path.join(targetdir, "private")
455 privdir = default_lp.get("private dir")
456 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
460 if targetdir is not None:
461 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
462 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
464 default_lp.set("lock dir", os.path.abspath(targetdir))
469 if sid_generator == "internal":
470 sid_generator_line = ""
472 sid_generator_line = "sid generator = " + sid_generator
474 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
475 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
477 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
479 "NETBIOS_NAME": netbiosname,
482 "SERVERROLE": serverrole,
483 "NETLOGONPATH": netlogon,
484 "SYSVOLPATH": sysvol,
485 "SIDGENERATOR_LINE": sid_generator_line,
486 "PRIVATEDIR_LINE": privatedir_line,
487 "LOCKDIR_LINE": lockdir_line,
488 "POSIXEADB_LINE": posixeadb_line
492 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
493 users_gid, wheel_gid):
494 """setup reasonable name mappings for sam names to unix names.
496 :param samdb: SamDB object.
497 :param idmap: IDmap db object.
498 :param sid: The domain sid.
499 :param domaindn: The domain DN.
500 :param root_uid: uid of the UNIX root user.
501 :param nobody_uid: uid of the UNIX nobody user.
502 :param users_gid: gid of the UNIX users group.
503 :param wheel_gid: gid of the UNIX wheel group."""
504 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
505 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
507 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
508 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
511 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
512 provision_backend, names, schema, serverrole,
514 """Setup the partitions for the SAM database.
516 Alternatively, provision() may call this, and then populate the database.
518 :note: This will wipe the Sam Database!
520 :note: This function always removes the local SAM LDB file. The erase
521 parameter controls whether to erase the existing data, which
522 may not be stored locally but in LDAP.
525 assert session_info is not None
527 # We use options=["modules:"] to stop the modules loading - we
528 # just want to wipe and re-initialise the database, not start it up
531 os.unlink(samdb_path)
535 samdb = Ldb(url=samdb_path, session_info=session_info,
536 lp=lp, options=["modules:"])
538 ldap_backend_line = "# No LDAP backend"
539 if provision_backend.type is not "ldb":
540 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
542 samdb.transaction_start()
544 logger.info("Setting up sam.ldb partitions and settings")
545 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
546 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
547 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
548 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
549 "LDAP_BACKEND_LINE": ldap_backend_line,
553 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
554 "BACKEND_TYPE": provision_backend.type,
555 "SERVER_ROLE": serverrole
558 logger.info("Setting up sam.ldb rootDSE")
559 setup_samdb_rootdse(samdb, setup_path, names)
561 samdb.transaction_cancel()
564 samdb.transaction_commit()
567 def secretsdb_self_join(secretsdb, domain,
568 netbiosname, machinepass, domainsid=None,
569 realm=None, dnsdomain=None,
571 key_version_number=1,
572 secure_channel_type=SEC_CHAN_WKSTA):
573 """Add domain join-specific bits to a secrets database.
575 :param secretsdb: Ldb Handle to the secrets database
576 :param machinepass: Machine password
578 attrs=["whenChanged",
586 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
587 msg["secureChannelType"] = str(secure_channel_type)
588 msg["flatname"] = [domain]
589 msg["objectClass"] = ["top", "primaryDomain"]
590 if realm is not None:
591 if dnsdomain is None:
592 dnsdomain = realm.lower()
593 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
595 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
596 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
597 msg["privateKeytab"] = ["secrets.keytab"]
600 msg["secret"] = [machinepass]
601 msg["samAccountName"] = ["%s$" % netbiosname]
602 msg["secureChannelType"] = [str(secure_channel_type)]
603 if domainsid is not None:
604 msg["objectSid"] = [ndr_pack(domainsid)]
606 res = secretsdb.search(base="cn=Primary Domains",
608 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
609 scope=ldb.SCOPE_ONELEVEL)
612 if del_msg.dn is not msg.dn:
613 secretsdb.delete(del_msg.dn)
615 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
618 msg["priorSecret"] = res[0]["secret"]
619 msg["priorWhenChanged"] = res[0]["whenChanged"]
621 if res["privateKeytab"] is not None:
622 msg["privateKeytab"] = res[0]["privateKeytab"]
624 if res["krb5Keytab"] is not None:
625 msg["krb5Keytab"] = res[0]["krb5Keytab"]
628 el.set_flags(ldb.FLAG_MOD_REPLACE)
629 secretsdb.modify(msg)
634 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
636 dns_keytab_path, dnspass):
637 """Add DNS specific bits to a secrets database.
639 :param secretsdb: Ldb Handle to the secrets database
640 :param setup_path: Setup path function
641 :param machinepass: Machine password
644 os.unlink(os.path.join(private_dir, dns_keytab_path))
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 = samba.registry.Registry()
719 hive = samba.registry.open_ldb(path, session_info=session_info,
721 reg.mount_hive(hive, samba.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 "REALM": names.realm,
788 "DOMAIN": names.domain,
789 "DOMAINSID": str(domainsid),
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=ldb.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,
819 "DNSPASS_B64": b64encode(dnspass),
822 def getpolicypath(sysvolpath, dnsdomain, guid):
825 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
828 def create_gpo_struct(policy_path):
829 os.makedirs(policy_path, 0755)
830 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
831 "[General]\r\nVersion=65543")
832 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
833 os.makedirs(os.path.join(policy_path, "USER"), 0755)
836 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
837 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
838 create_gpo_struct(policy_path)
840 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
841 create_gpo_struct(policy_path)
844 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
845 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
846 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
847 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None):
848 """Setup a complete SAM Database.
850 :note: This will wipe the main SAM database file!
853 # ATTENTION: Do NOT change these default values without discussion with the
854 # team and/or release manager. They have a big impact on the whole program!
855 domainControllerFunctionality = DS_DC_FUNCTION_2008
857 if dom_for_fun_level is None:
858 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
860 if dom_for_fun_level > domainControllerFunctionality:
861 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!")
863 domainFunctionality = dom_for_fun_level
864 forestFunctionality = dom_for_fun_level
866 # Also wipes the database
867 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
868 provision_backend=provision_backend, session_info=session_info,
869 names=names, serverrole=serverrole, schema=schema)
872 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
875 # Load the database, but don's load the global schema and don't connect quite yet
876 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
877 credentials=provision_backend.credentials, lp=lp, global_schema=False,
880 logger.info("Pre-loading the Samba 4 and AD schema")
882 # Load the schema from the one we computed earlier
883 samdb.set_schema_from_ldb(schema.ldb)
885 # And now we can connect to the DB - the schema won't be loaded from the DB
891 samdb.transaction_start()
893 # Set the domain functionality levels onto the database.
894 # Various module (the password_hash module in particular) need
895 # to know what level of AD we are emulating.
897 # These will be fixed into the database via the database
898 # modifictions below, but we need them set from the start.
899 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
900 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
901 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
903 samdb.set_domain_sid(str(domainsid))
904 samdb.set_invocation_id(invocationid)
905 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
907 logger.info("Adding DomainDN: %s" % names.domaindn)
909 #impersonate domain admin
910 admin_session_info = admin_session(lp, str(domainsid))
911 samdb.set_session_info(admin_session_info)
912 if domainguid is not None:
913 domainguid_line = "objectGUID: %s\n-" % domainguid
917 descr = b64encode(get_domain_descriptor(domainsid))
918 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
919 "DOMAINDN": names.domaindn,
920 "DOMAINGUID": domainguid_line,
925 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
926 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
927 "DOMAINSID": str(domainsid),
928 "SCHEMADN": names.schemadn,
929 "NETBIOSNAME": names.netbiosname,
930 "DEFAULTSITE": names.sitename,
931 "CONFIGDN": names.configdn,
932 "SERVERDN": names.serverdn,
933 "POLICYGUID": policyguid,
934 "DOMAINDN": names.domaindn,
935 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
936 "SAMBA_VERSION_STRING": version
939 logger.info("Adding configuration container")
940 descr = b64encode(get_config_descriptor(domainsid))
941 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
942 "CONFIGDN": names.configdn,
946 # The LDIF here was created when the Schema object was constructed
947 logger.info("Setting up sam.ldb schema")
948 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
949 samdb.modify_ldif(schema.schema_dn_modify)
950 samdb.write_prefixes_from_schema()
951 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
952 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
953 {"SCHEMADN": names.schemadn})
955 logger.info("Reopening sam.ldb with new schema")
957 samdb.transaction_cancel()
960 samdb.transaction_commit()
962 samdb = SamDB(session_info=admin_session_info,
963 credentials=provision_backend.credentials, lp=lp,
964 global_schema=False, am_rodc=am_rodc)
966 samdb.transaction_start()
968 samdb.invocation_id = invocationid
970 logger.info("Setting up sam.ldb configuration data")
971 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
972 "CONFIGDN": names.configdn,
973 "NETBIOSNAME": names.netbiosname,
974 "DEFAULTSITE": names.sitename,
975 "DNSDOMAIN": names.dnsdomain,
976 "DOMAIN": names.domain,
977 "SCHEMADN": names.schemadn,
978 "DOMAINDN": names.domaindn,
979 "SERVERDN": names.serverdn,
980 "FOREST_FUNCTIONALITY": str(forestFunctionality),
981 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
984 logger.info("Setting up display specifiers")
985 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
986 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
987 check_all_substituted(display_specifiers_ldif)
988 samdb.add_ldif(display_specifiers_ldif)
990 logger.info("Adding users container")
991 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
992 "DOMAINDN": names.domaindn})
993 logger.info("Modifying users container")
994 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
995 "DOMAINDN": names.domaindn})
996 logger.info("Adding computers container")
997 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
998 "DOMAINDN": names.domaindn})
999 logger.info("Modifying computers container")
1000 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1001 "DOMAINDN": names.domaindn})
1002 logger.info("Setting up sam.ldb data")
1003 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1004 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1005 "DOMAINDN": names.domaindn,
1006 "NETBIOSNAME": names.netbiosname,
1007 "DEFAULTSITE": names.sitename,
1008 "CONFIGDN": names.configdn,
1009 "SERVERDN": names.serverdn,
1010 "POLICYGUID_DC": policyguid_dc
1013 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1014 "DOMAINDN": names.domaindn})
1016 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1017 "CONFIGDN": names.configdn,
1018 "SCHEMADN": names.schemadn})
1019 if fill == FILL_FULL:
1020 logger.info("Setting up sam.ldb users and groups")
1021 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1022 "DOMAINDN": names.domaindn,
1023 "DOMAINSID": str(domainsid),
1024 "CONFIGDN": names.configdn,
1025 "ADMINPASS_B64": b64encode(adminpass),
1026 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1029 logger.info("Setting up self join")
1030 setup_self_join(samdb, names=names, invocationid=invocationid,
1032 machinepass=machinepass,
1033 domainsid=domainsid, policyguid=policyguid,
1034 policyguid_dc=policyguid_dc,
1035 setup_path=setup_path,
1036 domainControllerFunctionality=domainControllerFunctionality,
1039 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1040 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1041 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1042 assert isinstance(names.ntdsguid, str)
1044 samdb.transaction_cancel()
1047 samdb.transaction_commit()
1052 FILL_NT4SYNC = "NT4SYNC"
1054 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1055 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1057 def set_dir_acl(path, acl, lp, domsid):
1058 setntacl(lp, path, acl, domsid)
1059 for root, dirs, files in os.walk(path, topdown=False):
1061 setntacl(lp, os.path.join(root, name), acl, domsid)
1063 setntacl(lp, os.path.join(root, name), acl, domsid)
1066 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1068 policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1069 set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
1071 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1072 attrs=["cn","nTSecurityDescriptor"],
1073 expression="", scope=ldb.SCOPE_ONELEVEL)
1075 acl = ndr_unpack(security.descriptor,
1076 str(policy["nTSecurityDescriptor"])).as_sddl()
1077 policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1078 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1081 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1084 os.chown(sysvol,-1,gid)
1090 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1091 for root, dirs, files in os.walk(sysvol, topdown=False):
1094 os.chown(os.path.join(root, name),-1,gid)
1095 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1098 os.chown(os.path.join(root, name),-1,gid)
1099 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1100 set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1103 def provision(setup_dir, logger, session_info,
1104 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1106 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1108 domain=None, hostname=None, hostip=None, hostip6=None,
1109 domainsid=None, adminpass=None, ldapadminpass=None,
1110 krbtgtpass=None, domainguid=None,
1111 policyguid=None, policyguid_dc=None, invocationid=None,
1112 machinepass=None, ntdsguid=None,
1113 dnspass=None, root=None, nobody=None, users=None,
1114 wheel=None, backup=None, aci=None, serverrole=None,
1115 dom_for_fun_level=None,
1116 ldap_backend_extra_port=None, backend_type=None,
1118 ol_mmr_urls=None, ol_olc=None,
1119 setup_ds_path=None, slapd_path=None, nosync=False,
1120 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1123 :note: caution, this wipes all existing data!
1126 def setup_path(file):
1127 return os.path.join(setup_dir, file)
1129 if domainsid is None:
1130 domainsid = security.random_sid()
1132 domainsid = security.dom_sid(domainsid)
1134 # create/adapt the group policy GUIDs
1135 if policyguid is None:
1136 policyguid = str(uuid.uuid4())
1137 policyguid = policyguid.upper()
1138 if policyguid_dc is None:
1139 policyguid_dc = str(uuid.uuid4())
1140 policyguid_dc = policyguid_dc.upper()
1142 if adminpass is None:
1143 adminpass = samba.generate_random_password(12, 32)
1144 if krbtgtpass is None:
1145 krbtgtpass = samba.generate_random_password(128, 255)
1146 if machinepass is None:
1147 machinepass = samba.generate_random_password(128, 255)
1149 dnspass = samba.generate_random_password(128, 255)
1150 if ldapadminpass is None:
1151 #Make a new, random password between Samba and it's LDAP server
1152 ldapadminpass=samba.generate_random_password(128, 255)
1154 if backend_type is None:
1155 backend_type = "ldb"
1157 sid_generator = "internal"
1158 if backend_type == "fedora-ds":
1159 sid_generator = "backend"
1161 root_uid = findnss_uid([root or "root"])
1162 nobody_uid = findnss_uid([nobody or "nobody"])
1163 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1165 wheel_gid = findnss_gid(["wheel", "adm"])
1167 wheel_gid = findnss_gid([wheel])
1169 bind_gid = findnss_gid(["bind", "named"])
1173 if targetdir is not None:
1174 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1175 elif smbconf is None:
1176 smbconf = samba.param.default_path()
1177 if not os.path.exists(os.path.dirname(smbconf)):
1178 os.makedirs(os.path.dirname(smbconf))
1180 # only install a new smb.conf if there isn't one there already
1181 if os.path.exists(smbconf):
1182 # if Samba Team members can't figure out the weird errors
1183 # loading an empty smb.conf gives, then we need to be smarter.
1184 # Pretend it just didn't exist --abartlet
1185 data = open(smbconf, 'r').read()
1186 data = data.lstrip()
1187 if data is None or data == "":
1188 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1189 serverrole, targetdir, sid_generator, useeadb)
1191 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1192 targetdir, sid_generator, useeadb)
1194 lp = samba.param.LoadParm()
1197 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1198 dnsdomain=realm, serverrole=serverrole,
1199 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1200 serverdn=serverdn, sitename=sitename)
1202 paths = provision_paths_from_lp(lp, names.dnsdomain)
1204 paths.bind_gid = bind_gid
1207 hostips = samba.interface_ips(lp, False)
1208 if len(hostips) == 0:
1209 logger.warning("No external IPv4 address has been found. Using loopback.")
1210 hostip = '127.0.0.1'
1213 if len(hostips) > 1:
1214 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1218 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1221 if hostip6 == '::1' and ip[-1][0] != '::1':
1223 except socket.gaierror, (socket.EAI_NODATA, msg):
1226 if serverrole is None:
1227 serverrole = lp.get("server role")
1229 assert serverrole in ("domain controller", "member server", "standalone")
1230 if invocationid is None:
1231 invocationid = str(uuid.uuid4())
1233 if not os.path.exists(paths.private_dir):
1234 os.mkdir(paths.private_dir)
1235 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1236 os.mkdir(os.path.join(paths.private_dir,"tls"))
1238 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1240 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1241 serverdn=names.serverdn, am_rodc=am_rodc)
1243 if backend_type == "ldb":
1244 provision_backend = LDBBackend(backend_type,
1245 paths=paths, setup_path=setup_path,
1246 lp=lp, credentials=credentials,
1249 elif backend_type == "existing":
1250 provision_backend = ExistingBackend(backend_type,
1251 paths=paths, setup_path=setup_path,
1252 lp=lp, credentials=credentials,
1255 ldapi_url=ldapi_url)
1256 elif backend_type == "fedora-ds":
1257 provision_backend = FDSBackend(backend_type,
1258 paths=paths, setup_path=setup_path,
1259 lp=lp, credentials=credentials,
1262 domainsid=domainsid,
1265 ldapadminpass=ldapadminpass,
1266 slapd_path=slapd_path,
1267 ldap_backend_extra_port=ldap_backend_extra_port,
1268 ldap_dryrun_mode=ldap_dryrun_mode,
1270 setup_ds_path=setup_ds_path)
1271 elif backend_type == "openldap":
1272 provision_backend = OpenLDAPBackend(backend_type,
1273 paths=paths, setup_path=setup_path,
1274 lp=lp, credentials=credentials,
1277 domainsid=domainsid,
1280 ldapadminpass=ldapadminpass,
1281 slapd_path=slapd_path,
1282 ldap_backend_extra_port=ldap_backend_extra_port,
1283 ldap_dryrun_mode=ldap_dryrun_mode,
1284 ol_mmr_urls=ol_mmr_urls,
1287 raise ValueError("Unknown LDAP backend type selected")
1289 provision_backend.init()
1290 provision_backend.start()
1292 # only install a new shares config db if there is none
1293 if not os.path.exists(paths.shareconf):
1294 logger.info("Setting up share.ldb")
1295 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1297 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1300 logger.info("Setting up secrets.ldb")
1301 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1302 session_info=session_info,
1303 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1305 logger.info("Setting up the registry")
1306 setup_registry(paths.hklm, setup_path, session_info,
1309 logger.info("Setting up the privileges database")
1310 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1312 logger.info("Setting up idmap db")
1313 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1316 logger.info("Setting up SAM db")
1317 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1318 provision_backend, lp, names,
1320 domainsid=domainsid,
1321 schema=schema, domainguid=domainguid,
1322 policyguid=policyguid, policyguid_dc=policyguid_dc,
1324 adminpass=adminpass, krbtgtpass=krbtgtpass,
1325 invocationid=invocationid,
1326 machinepass=machinepass, dnspass=dnspass,
1327 ntdsguid=ntdsguid, serverrole=serverrole,
1328 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc)
1330 if serverrole == "domain controller":
1331 if paths.netlogon is None:
1332 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1333 logger.info("Please either remove %s or see the template at %s" %
1334 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1335 assert paths.netlogon is not None
1337 if paths.sysvol is None:
1338 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1339 " are configuring a DC.")
1340 logger.info("Please either remove %s or see the template at %s" %
1341 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1342 assert paths.sysvol is not None
1344 if not os.path.isdir(paths.netlogon):
1345 os.makedirs(paths.netlogon, 0755)
1347 if samdb_fill == FILL_FULL:
1348 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1349 root_uid=root_uid, nobody_uid=nobody_uid,
1350 users_gid=users_gid, wheel_gid=wheel_gid)
1352 if serverrole == "domain controller":
1353 # Set up group policies (domain policy and domain controller policy)
1354 setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1355 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1356 domainsid, names.dnsdomain, names.domaindn, lp)
1358 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1359 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1361 secretsdb_self_join(secrets_ldb, domain=names.domain,
1363 dnsdomain=names.dnsdomain,
1364 netbiosname=names.netbiosname,
1365 domainsid=domainsid,
1366 machinepass=machinepass,
1367 secure_channel_type=SEC_CHAN_BDC)
1369 if serverrole == "domain controller":
1370 secretsdb_setup_dns(secrets_ldb, setup_path,
1372 realm=names.realm, dnsdomain=names.dnsdomain,
1373 dns_keytab_path=paths.dns_keytab,
1376 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1377 assert isinstance(domainguid, str)
1379 # Only make a zone file on the first DC, it should be replicated
1380 # with DNS replication
1381 create_zone_file(lp, logger, paths, targetdir, setup_path,
1382 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1383 hostname=names.hostname, realm=names.realm,
1384 domainguid=domainguid, ntdsguid=names.ntdsguid)
1386 create_named_conf(paths, setup_path, realm=names.realm,
1387 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1389 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1390 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1391 keytab_name=paths.dns_keytab)
1392 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1393 logger.info("and %s for further documentation required for secure DNS "
1394 "updates", paths.namedtxt)
1396 create_krb5_conf(paths.krb5conf, setup_path,
1397 dnsdomain=names.dnsdomain, hostname=names.hostname,
1399 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1400 "generated at %s", paths.krb5conf)
1402 if serverrole == "domain controller":
1403 create_dns_update_list(lp, logger, paths, setup_path)
1405 provision_backend.post_setup()
1406 provision_backend.shutdown()
1408 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1411 #Now commit the secrets.ldb to disk
1412 secrets_ldb.transaction_commit()
1414 # the commit creates the dns.keytab, now chown it
1415 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1416 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1418 os.chmod(dns_keytab_path, 0640)
1419 os.chown(dns_keytab_path, -1, paths.bind_gid)
1421 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1425 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1426 paths.phpldapadminconfig)
1428 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1429 logger.info("Server Role: %s" % serverrole)
1430 logger.info("Hostname: %s" % names.hostname)
1431 logger.info("NetBIOS Domain: %s" % names.domain)
1432 logger.info("DNS Domain: %s" % names.dnsdomain)
1433 logger.info("DOMAIN SID: %s" % str(domainsid))
1434 if samdb_fill == FILL_FULL:
1435 logger.info("Admin password: %s" % adminpass)
1436 if provision_backend.type is not "ldb":
1437 if provision_backend.credentials.get_bind_dn() is not None:
1438 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1440 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1442 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1444 if provision_backend.slapd_command_escaped is not None:
1445 # now display slapd_command_file.txt to show how slapd must be started next time
1446 logger.info("Use later the following commandline to start slapd, then Samba:")
1447 logger.info(provision_backend.slapd_command_escaped)
1448 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1449 provision_backend.ldapdir)
1452 result = ProvisionResult()
1453 result.domaindn = domaindn
1454 result.paths = paths
1456 result.samdb = samdb
1460 def provision_become_dc(setup_dir=None,
1461 smbconf=None, targetdir=None, realm=None,
1462 rootdn=None, domaindn=None, schemadn=None,
1463 configdn=None, serverdn=None,
1464 domain=None, hostname=None, domainsid=None,
1465 adminpass=None, krbtgtpass=None, domainguid=None,
1466 policyguid=None, policyguid_dc=None, invocationid=None,
1468 dnspass=None, root=None, nobody=None, users=None,
1469 wheel=None, backup=None, serverrole=None,
1470 ldap_backend=None, ldap_backend_type=None,
1471 sitename=None, debuglevel=1):
1473 logger = logging.getLogger("provision")
1474 samba.set_debug_level(debuglevel)
1476 return provision(setup_dir, logger, system_session(), None,
1477 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1478 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1479 configdn=configdn, serverdn=serverdn, domain=domain,
1480 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1481 machinepass=machinepass, serverrole="domain controller",
1485 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1486 """Create a PHP LDAP admin configuration file.
1488 :param path: Path to write the configuration to.
1489 :param setup_path: Function to generate setup paths.
1491 setup_file(setup_path("phpldapadmin-config.php"), path,
1492 {"S4_LDAPI_URI": ldapi_uri})
1495 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1496 hostip, hostip6, hostname, realm, domainguid,
1498 """Write out a DNS zone file, from the info in the current database.
1500 :param paths: paths object
1501 :param setup_path: Setup path function.
1502 :param dnsdomain: DNS Domain name
1503 :param domaindn: DN of the Domain
1504 :param hostip: Local IPv4 IP
1505 :param hostip6: Local IPv6 IP
1506 :param hostname: Local hostname
1507 :param realm: Realm name
1508 :param domainguid: GUID of the domain.
1509 :param ntdsguid: GUID of the hosts nTDSDSA record.
1511 assert isinstance(domainguid, str)
1513 if hostip6 is not None:
1514 hostip6_base_line = " IN AAAA " + hostip6
1515 hostip6_host_line = hostname + " IN AAAA " + hostip6
1516 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1518 hostip6_base_line = ""
1519 hostip6_host_line = ""
1520 gc_msdcs_ip6_line = ""
1522 if hostip is not None:
1523 hostip_base_line = " IN A " + hostip
1524 hostip_host_line = hostname + " IN A " + hostip
1525 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1527 hostip_base_line = ""
1528 hostip_host_line = ""
1529 gc_msdcs_ip_line = ""
1531 dns_dir = os.path.dirname(paths.dns)
1534 shutil.rmtree(dns_dir, True)
1538 os.mkdir(dns_dir, 0775)
1540 # we need to freeze the zone while we update the contents
1541 if targetdir is None:
1542 rndc = ' '.join(lp.get("rndc command"))
1543 os.system(rndc + " freeze " + lp.get("realm"))
1545 setup_file(setup_path("provision.zone"), paths.dns, {
1546 "HOSTNAME": hostname,
1547 "DNSDOMAIN": dnsdomain,
1549 "HOSTIP_BASE_LINE": hostip_base_line,
1550 "HOSTIP_HOST_LINE": hostip_host_line,
1551 "DOMAINGUID": domainguid,
1552 "DATESTRING": time.strftime("%Y%m%d%H"),
1553 "DEFAULTSITE": DEFAULTSITE,
1554 "NTDSGUID": ntdsguid,
1555 "HOSTIP6_BASE_LINE": hostip6_base_line,
1556 "HOSTIP6_HOST_LINE": hostip6_host_line,
1557 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1558 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1561 # note that we use no variable substitution on this file
1562 # the substitution is done at runtime by samba_dnsupdate
1563 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1565 # and the SPN update list
1566 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1568 if paths.bind_gid is not None:
1570 os.chown(dns_dir, -1, paths.bind_gid)
1571 os.chown(paths.dns, -1, paths.bind_gid)
1572 # chmod needed to cope with umask
1573 os.chmod(dns_dir, 0775)
1574 os.chmod(paths.dns, 0664)
1576 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1578 if targetdir is None:
1579 os.system(rndc + " unfreeze " + lp.get("realm"))
1582 def create_dns_update_list(lp, logger, paths, setup_path):
1583 """Write out a dns_update_list file"""
1584 # note that we use no variable substitution on this file
1585 # the substitution is done at runtime by samba_dnsupdate
1586 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1587 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1590 def create_named_conf(paths, setup_path, realm, dnsdomain,
1592 """Write out a file containing zone statements suitable for inclusion in a
1593 named.conf file (including GSS-TSIG configuration).
1595 :param paths: all paths
1596 :param setup_path: Setup path function.
1597 :param realm: Realm name
1598 :param dnsdomain: DNS Domain name
1599 :param private_dir: Path to private directory
1600 :param keytab_name: File name of DNS keytab file
1603 setup_file(setup_path("named.conf"), paths.namedconf, {
1604 "DNSDOMAIN": dnsdomain,
1606 "ZONE_FILE": paths.dns,
1607 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1608 "NAMED_CONF": paths.namedconf,
1609 "NAMED_CONF_UPDATE": paths.namedconf_update
1612 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1614 def create_named_txt(path, setup_path, realm, dnsdomain,
1615 private_dir, keytab_name):
1616 """Write out a file containing zone statements suitable for inclusion in a
1617 named.conf file (including GSS-TSIG configuration).
1619 :param path: Path of the new named.conf file.
1620 :param setup_path: Setup path function.
1621 :param realm: Realm name
1622 :param dnsdomain: DNS Domain name
1623 :param private_dir: Path to private directory
1624 :param keytab_name: File name of DNS keytab file
1627 setup_file(setup_path("named.txt"), path, {
1628 "DNSDOMAIN": dnsdomain,
1630 "DNS_KEYTAB": keytab_name,
1631 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1632 "PRIVATE_DIR": private_dir
1635 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1636 """Write out a file containing zone statements suitable for inclusion in a
1637 named.conf file (including GSS-TSIG configuration).
1639 :param path: Path of the new named.conf file.
1640 :param setup_path: Setup path function.
1641 :param dnsdomain: DNS Domain name
1642 :param hostname: Local hostname
1643 :param realm: Realm name
1645 setup_file(setup_path("krb5.conf"), path, {
1646 "DNSDOMAIN": dnsdomain,
1647 "HOSTNAME": hostname,
1652 class ProvisioningError(Exception):
1653 """A generic provision error."""
1655 def __init__(self, value):
1659 return "ProvisioningError: " + self.value
1662 class InvalidNetbiosName(Exception):
1663 """A specified name was not a valid NetBIOS name."""
1664 def __init__(self, name):
1665 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)