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
859 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
860 logger.warning("Running SAMBA 4 on a domain and forest function level"
861 " lower than Windows 2003 (Native) is not recommended.")
863 if dom_for_fun_level > domainControllerFunctionality:
864 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!")
866 domainFunctionality = dom_for_fun_level
867 forestFunctionality = dom_for_fun_level
869 # Also wipes the database
870 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
871 provision_backend=provision_backend, session_info=session_info,
872 names=names, serverrole=serverrole, schema=schema)
875 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
878 # Load the database, but don's load the global schema and don't connect quite yet
879 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
880 credentials=provision_backend.credentials, lp=lp, global_schema=False,
883 logger.info("Pre-loading the Samba 4 and AD schema")
885 # Load the schema from the one we computed earlier
886 samdb.set_schema_from_ldb(schema.ldb)
888 # And now we can connect to the DB - the schema won't be loaded from the DB
894 samdb.transaction_start()
896 # Set the domain functionality levels onto the database.
897 # Various module (the password_hash module in particular) need
898 # to know what level of AD we are emulating.
900 # These will be fixed into the database via the database
901 # modifictions below, but we need them set from the start.
902 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
903 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
904 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
906 samdb.set_domain_sid(str(domainsid))
907 samdb.set_invocation_id(invocationid)
908 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
910 logger.info("Adding DomainDN: %s" % names.domaindn)
912 #impersonate domain admin
913 admin_session_info = admin_session(lp, str(domainsid))
914 samdb.set_session_info(admin_session_info)
915 if domainguid is not None:
916 domainguid_line = "objectGUID: %s\n-" % domainguid
920 descr = b64encode(get_domain_descriptor(domainsid))
921 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
922 "DOMAINDN": names.domaindn,
923 "DOMAINGUID": domainguid_line,
928 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
929 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
930 "DOMAINSID": str(domainsid),
931 "SCHEMADN": names.schemadn,
932 "NETBIOSNAME": names.netbiosname,
933 "DEFAULTSITE": names.sitename,
934 "CONFIGDN": names.configdn,
935 "SERVERDN": names.serverdn,
936 "POLICYGUID": policyguid,
937 "DOMAINDN": names.domaindn,
938 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
939 "SAMBA_VERSION_STRING": version
942 logger.info("Adding configuration container")
943 descr = b64encode(get_config_descriptor(domainsid))
944 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
945 "CONFIGDN": names.configdn,
949 # The LDIF here was created when the Schema object was constructed
950 logger.info("Setting up sam.ldb schema")
951 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
952 samdb.modify_ldif(schema.schema_dn_modify)
953 samdb.write_prefixes_from_schema()
954 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
955 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
956 {"SCHEMADN": names.schemadn})
958 logger.info("Reopening sam.ldb with new schema")
960 samdb.transaction_cancel()
963 samdb.transaction_commit()
965 samdb = SamDB(session_info=admin_session_info,
966 credentials=provision_backend.credentials, lp=lp,
967 global_schema=False, am_rodc=am_rodc)
969 samdb.transaction_start()
971 samdb.invocation_id = invocationid
973 logger.info("Setting up sam.ldb configuration data")
974 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
975 "CONFIGDN": names.configdn,
976 "NETBIOSNAME": names.netbiosname,
977 "DEFAULTSITE": names.sitename,
978 "DNSDOMAIN": names.dnsdomain,
979 "DOMAIN": names.domain,
980 "SCHEMADN": names.schemadn,
981 "DOMAINDN": names.domaindn,
982 "SERVERDN": names.serverdn,
983 "FOREST_FUNCTIONALITY": str(forestFunctionality),
984 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
987 logger.info("Setting up display specifiers")
988 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
989 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
990 check_all_substituted(display_specifiers_ldif)
991 samdb.add_ldif(display_specifiers_ldif)
993 logger.info("Adding users container")
994 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
995 "DOMAINDN": names.domaindn})
996 logger.info("Modifying users container")
997 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
998 "DOMAINDN": names.domaindn})
999 logger.info("Adding computers container")
1000 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1001 "DOMAINDN": names.domaindn})
1002 logger.info("Modifying computers container")
1003 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1004 "DOMAINDN": names.domaindn})
1005 logger.info("Setting up sam.ldb data")
1006 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1007 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1008 "DOMAINDN": names.domaindn,
1009 "NETBIOSNAME": names.netbiosname,
1010 "DEFAULTSITE": names.sitename,
1011 "CONFIGDN": names.configdn,
1012 "SERVERDN": names.serverdn,
1013 "POLICYGUID_DC": policyguid_dc
1016 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1017 "DOMAINDN": names.domaindn})
1019 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1020 "CONFIGDN": names.configdn,
1021 "SCHEMADN": names.schemadn})
1022 if fill == FILL_FULL:
1023 logger.info("Setting up sam.ldb users and groups")
1024 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1025 "DOMAINDN": names.domaindn,
1026 "DOMAINSID": str(domainsid),
1027 "CONFIGDN": names.configdn,
1028 "ADMINPASS_B64": b64encode(adminpass),
1029 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1032 logger.info("Setting up self join")
1033 setup_self_join(samdb, names=names, invocationid=invocationid,
1035 machinepass=machinepass,
1036 domainsid=domainsid, policyguid=policyguid,
1037 policyguid_dc=policyguid_dc,
1038 setup_path=setup_path,
1039 domainControllerFunctionality=domainControllerFunctionality,
1042 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1043 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1044 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1045 assert isinstance(names.ntdsguid, str)
1047 samdb.transaction_cancel()
1050 samdb.transaction_commit()
1055 FILL_NT4SYNC = "NT4SYNC"
1057 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1058 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)"
1060 def set_dir_acl(path, acl, lp, domsid):
1061 setntacl(lp, path, acl, domsid)
1062 for root, dirs, files in os.walk(path, topdown=False):
1064 setntacl(lp, os.path.join(root, name), acl, domsid)
1066 setntacl(lp, os.path.join(root, name), acl, domsid)
1069 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1071 policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1072 set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
1074 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1075 attrs=["cn","nTSecurityDescriptor"],
1076 expression="", scope=ldb.SCOPE_ONELEVEL)
1078 acl = ndr_unpack(security.descriptor,
1079 str(policy["nTSecurityDescriptor"])).as_sddl()
1080 policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1081 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1084 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1087 os.chown(sysvol,-1,gid)
1093 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1094 for root, dirs, files in os.walk(sysvol, topdown=False):
1097 os.chown(os.path.join(root, name),-1,gid)
1098 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1101 os.chown(os.path.join(root, name),-1,gid)
1102 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1103 set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1106 def provision(setup_dir, logger, session_info,
1107 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1109 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1111 domain=None, hostname=None, hostip=None, hostip6=None,
1112 domainsid=None, adminpass=None, ldapadminpass=None,
1113 krbtgtpass=None, domainguid=None,
1114 policyguid=None, policyguid_dc=None, invocationid=None,
1115 machinepass=None, ntdsguid=None,
1116 dnspass=None, root=None, nobody=None, users=None,
1117 wheel=None, backup=None, aci=None, serverrole=None,
1118 dom_for_fun_level=None,
1119 ldap_backend_extra_port=None, backend_type=None,
1121 ol_mmr_urls=None, ol_olc=None,
1122 setup_ds_path=None, slapd_path=None, nosync=False,
1123 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1126 :note: caution, this wipes all existing data!
1129 def setup_path(file):
1130 return os.path.join(setup_dir, file)
1132 if domainsid is None:
1133 domainsid = security.random_sid()
1135 domainsid = security.dom_sid(domainsid)
1137 # create/adapt the group policy GUIDs
1138 if policyguid is None:
1139 policyguid = str(uuid.uuid4())
1140 policyguid = policyguid.upper()
1141 if policyguid_dc is None:
1142 policyguid_dc = str(uuid.uuid4())
1143 policyguid_dc = policyguid_dc.upper()
1145 if adminpass is None:
1146 adminpass = samba.generate_random_password(12, 32)
1147 if krbtgtpass is None:
1148 krbtgtpass = samba.generate_random_password(128, 255)
1149 if machinepass is None:
1150 machinepass = samba.generate_random_password(128, 255)
1152 dnspass = samba.generate_random_password(128, 255)
1153 if ldapadminpass is None:
1154 #Make a new, random password between Samba and it's LDAP server
1155 ldapadminpass=samba.generate_random_password(128, 255)
1157 if backend_type is None:
1158 backend_type = "ldb"
1160 sid_generator = "internal"
1161 if backend_type == "fedora-ds":
1162 sid_generator = "backend"
1164 root_uid = findnss_uid([root or "root"])
1165 nobody_uid = findnss_uid([nobody or "nobody"])
1166 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1168 wheel_gid = findnss_gid(["wheel", "adm"])
1170 wheel_gid = findnss_gid([wheel])
1172 bind_gid = findnss_gid(["bind", "named"])
1176 if targetdir is not None:
1177 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1178 elif smbconf is None:
1179 smbconf = samba.param.default_path()
1180 if not os.path.exists(os.path.dirname(smbconf)):
1181 os.makedirs(os.path.dirname(smbconf))
1183 # only install a new smb.conf if there isn't one there already
1184 if os.path.exists(smbconf):
1185 # if Samba Team members can't figure out the weird errors
1186 # loading an empty smb.conf gives, then we need to be smarter.
1187 # Pretend it just didn't exist --abartlet
1188 data = open(smbconf, 'r').read()
1189 data = data.lstrip()
1190 if data is None or data == "":
1191 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1192 serverrole, targetdir, sid_generator, useeadb)
1194 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1195 targetdir, sid_generator, useeadb)
1197 lp = samba.param.LoadParm()
1200 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1201 dnsdomain=realm, serverrole=serverrole,
1202 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1203 serverdn=serverdn, sitename=sitename)
1205 paths = provision_paths_from_lp(lp, names.dnsdomain)
1207 paths.bind_gid = bind_gid
1210 hostips = samba.interface_ips(lp, False)
1211 if len(hostips) == 0:
1212 logger.warning("No external IPv4 address has been found. Using loopback.")
1213 hostip = '127.0.0.1'
1216 if len(hostips) > 1:
1217 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1221 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1224 if hostip6 == '::1' and ip[-1][0] != '::1':
1226 except socket.gaierror, (socket.EAI_NODATA, msg):
1229 if serverrole is None:
1230 serverrole = lp.get("server role")
1232 assert serverrole in ("domain controller", "member server", "standalone")
1233 if invocationid is None:
1234 invocationid = str(uuid.uuid4())
1236 if not os.path.exists(paths.private_dir):
1237 os.mkdir(paths.private_dir)
1238 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1239 os.mkdir(os.path.join(paths.private_dir,"tls"))
1241 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1243 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1244 serverdn=names.serverdn, am_rodc=am_rodc)
1246 if backend_type == "ldb":
1247 provision_backend = LDBBackend(backend_type,
1248 paths=paths, setup_path=setup_path,
1249 lp=lp, credentials=credentials,
1252 elif backend_type == "existing":
1253 provision_backend = ExistingBackend(backend_type,
1254 paths=paths, setup_path=setup_path,
1255 lp=lp, credentials=credentials,
1258 ldapi_url=ldapi_url)
1259 elif backend_type == "fedora-ds":
1260 provision_backend = FDSBackend(backend_type,
1261 paths=paths, setup_path=setup_path,
1262 lp=lp, credentials=credentials,
1265 domainsid=domainsid,
1268 ldapadminpass=ldapadminpass,
1269 slapd_path=slapd_path,
1270 ldap_backend_extra_port=ldap_backend_extra_port,
1271 ldap_dryrun_mode=ldap_dryrun_mode,
1273 setup_ds_path=setup_ds_path)
1274 elif backend_type == "openldap":
1275 provision_backend = OpenLDAPBackend(backend_type,
1276 paths=paths, setup_path=setup_path,
1277 lp=lp, credentials=credentials,
1280 domainsid=domainsid,
1283 ldapadminpass=ldapadminpass,
1284 slapd_path=slapd_path,
1285 ldap_backend_extra_port=ldap_backend_extra_port,
1286 ldap_dryrun_mode=ldap_dryrun_mode,
1287 ol_mmr_urls=ol_mmr_urls,
1290 raise ValueError("Unknown LDAP backend type selected")
1292 provision_backend.init()
1293 provision_backend.start()
1295 # only install a new shares config db if there is none
1296 if not os.path.exists(paths.shareconf):
1297 logger.info("Setting up share.ldb")
1298 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1300 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1303 logger.info("Setting up secrets.ldb")
1304 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1305 session_info=session_info,
1306 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1308 logger.info("Setting up the registry")
1309 setup_registry(paths.hklm, setup_path, session_info,
1312 logger.info("Setting up the privileges database")
1313 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1315 logger.info("Setting up idmap db")
1316 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1319 logger.info("Setting up SAM db")
1320 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1321 provision_backend, lp, names,
1323 domainsid=domainsid,
1324 schema=schema, domainguid=domainguid,
1325 policyguid=policyguid, policyguid_dc=policyguid_dc,
1327 adminpass=adminpass, krbtgtpass=krbtgtpass,
1328 invocationid=invocationid,
1329 machinepass=machinepass, dnspass=dnspass,
1330 ntdsguid=ntdsguid, serverrole=serverrole,
1331 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc)
1333 if serverrole == "domain controller":
1334 if paths.netlogon is None:
1335 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1336 logger.info("Please either remove %s or see the template at %s" %
1337 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1338 assert paths.netlogon is not None
1340 if paths.sysvol is None:
1341 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1342 " are configuring a DC.")
1343 logger.info("Please either remove %s or see the template at %s" %
1344 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1345 assert paths.sysvol is not None
1347 if not os.path.isdir(paths.netlogon):
1348 os.makedirs(paths.netlogon, 0755)
1350 if samdb_fill == FILL_FULL:
1351 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1352 root_uid=root_uid, nobody_uid=nobody_uid,
1353 users_gid=users_gid, wheel_gid=wheel_gid)
1355 if serverrole == "domain controller":
1356 # Set up group policies (domain policy and domain controller policy)
1357 setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1358 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1359 domainsid, names.dnsdomain, names.domaindn, lp)
1361 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1362 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1364 secretsdb_self_join(secrets_ldb, domain=names.domain,
1366 dnsdomain=names.dnsdomain,
1367 netbiosname=names.netbiosname,
1368 domainsid=domainsid,
1369 machinepass=machinepass,
1370 secure_channel_type=SEC_CHAN_BDC)
1372 if serverrole == "domain controller":
1373 secretsdb_setup_dns(secrets_ldb, setup_path,
1375 realm=names.realm, dnsdomain=names.dnsdomain,
1376 dns_keytab_path=paths.dns_keytab,
1379 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1380 assert isinstance(domainguid, str)
1382 # Only make a zone file on the first DC, it should be replicated
1383 # with DNS replication
1384 create_zone_file(lp, logger, paths, targetdir, setup_path,
1385 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1386 hostname=names.hostname, realm=names.realm,
1387 domainguid=domainguid, ntdsguid=names.ntdsguid)
1389 create_named_conf(paths, setup_path, realm=names.realm,
1390 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1392 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1393 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1394 keytab_name=paths.dns_keytab)
1395 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1396 logger.info("and %s for further documentation required for secure DNS "
1397 "updates", paths.namedtxt)
1399 create_krb5_conf(paths.krb5conf, setup_path,
1400 dnsdomain=names.dnsdomain, hostname=names.hostname,
1402 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1403 "generated at %s", paths.krb5conf)
1405 if serverrole == "domain controller":
1406 create_dns_update_list(lp, logger, paths, setup_path)
1408 provision_backend.post_setup()
1409 provision_backend.shutdown()
1411 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1414 #Now commit the secrets.ldb to disk
1415 secrets_ldb.transaction_commit()
1417 # the commit creates the dns.keytab, now chown it
1418 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1419 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1421 os.chmod(dns_keytab_path, 0640)
1422 os.chown(dns_keytab_path, -1, paths.bind_gid)
1424 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1428 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1429 paths.phpldapadminconfig)
1431 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1432 logger.info("Server Role: %s" % serverrole)
1433 logger.info("Hostname: %s" % names.hostname)
1434 logger.info("NetBIOS Domain: %s" % names.domain)
1435 logger.info("DNS Domain: %s" % names.dnsdomain)
1436 logger.info("DOMAIN SID: %s" % str(domainsid))
1437 if samdb_fill == FILL_FULL:
1438 logger.info("Admin password: %s" % adminpass)
1439 if provision_backend.type is not "ldb":
1440 if provision_backend.credentials.get_bind_dn() is not None:
1441 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1443 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1445 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1447 if provision_backend.slapd_command_escaped is not None:
1448 # now display slapd_command_file.txt to show how slapd must be started next time
1449 logger.info("Use later the following commandline to start slapd, then Samba:")
1450 logger.info(provision_backend.slapd_command_escaped)
1451 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1452 provision_backend.ldapdir)
1455 result = ProvisionResult()
1456 result.domaindn = domaindn
1457 result.paths = paths
1459 result.samdb = samdb
1463 def provision_become_dc(setup_dir=None,
1464 smbconf=None, targetdir=None, realm=None,
1465 rootdn=None, domaindn=None, schemadn=None,
1466 configdn=None, serverdn=None,
1467 domain=None, hostname=None, domainsid=None,
1468 adminpass=None, krbtgtpass=None, domainguid=None,
1469 policyguid=None, policyguid_dc=None, invocationid=None,
1471 dnspass=None, root=None, nobody=None, users=None,
1472 wheel=None, backup=None, serverrole=None,
1473 ldap_backend=None, ldap_backend_type=None,
1474 sitename=None, debuglevel=1):
1476 logger = logging.getLogger("provision")
1477 samba.set_debug_level(debuglevel)
1479 return provision(setup_dir, logger, system_session(), None,
1480 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1481 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1482 configdn=configdn, serverdn=serverdn, domain=domain,
1483 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1484 machinepass=machinepass, serverrole="domain controller",
1488 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1489 """Create a PHP LDAP admin configuration file.
1491 :param path: Path to write the configuration to.
1492 :param setup_path: Function to generate setup paths.
1494 setup_file(setup_path("phpldapadmin-config.php"), path,
1495 {"S4_LDAPI_URI": ldapi_uri})
1498 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1499 hostip, hostip6, hostname, realm, domainguid,
1501 """Write out a DNS zone file, from the info in the current database.
1503 :param paths: paths object
1504 :param setup_path: Setup path function.
1505 :param dnsdomain: DNS Domain name
1506 :param domaindn: DN of the Domain
1507 :param hostip: Local IPv4 IP
1508 :param hostip6: Local IPv6 IP
1509 :param hostname: Local hostname
1510 :param realm: Realm name
1511 :param domainguid: GUID of the domain.
1512 :param ntdsguid: GUID of the hosts nTDSDSA record.
1514 assert isinstance(domainguid, str)
1516 if hostip6 is not None:
1517 hostip6_base_line = " IN AAAA " + hostip6
1518 hostip6_host_line = hostname + " IN AAAA " + hostip6
1519 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1521 hostip6_base_line = ""
1522 hostip6_host_line = ""
1523 gc_msdcs_ip6_line = ""
1525 if hostip is not None:
1526 hostip_base_line = " IN A " + hostip
1527 hostip_host_line = hostname + " IN A " + hostip
1528 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1530 hostip_base_line = ""
1531 hostip_host_line = ""
1532 gc_msdcs_ip_line = ""
1534 dns_dir = os.path.dirname(paths.dns)
1537 shutil.rmtree(dns_dir, True)
1541 os.mkdir(dns_dir, 0775)
1543 # we need to freeze the zone while we update the contents
1544 if targetdir is None:
1545 rndc = ' '.join(lp.get("rndc command"))
1546 os.system(rndc + " freeze " + lp.get("realm"))
1548 setup_file(setup_path("provision.zone"), paths.dns, {
1549 "HOSTNAME": hostname,
1550 "DNSDOMAIN": dnsdomain,
1552 "HOSTIP_BASE_LINE": hostip_base_line,
1553 "HOSTIP_HOST_LINE": hostip_host_line,
1554 "DOMAINGUID": domainguid,
1555 "DATESTRING": time.strftime("%Y%m%d%H"),
1556 "DEFAULTSITE": DEFAULTSITE,
1557 "NTDSGUID": ntdsguid,
1558 "HOSTIP6_BASE_LINE": hostip6_base_line,
1559 "HOSTIP6_HOST_LINE": hostip6_host_line,
1560 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1561 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1564 # note that we use no variable substitution on this file
1565 # the substitution is done at runtime by samba_dnsupdate
1566 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1568 # and the SPN update list
1569 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1571 if paths.bind_gid is not None:
1573 os.chown(dns_dir, -1, paths.bind_gid)
1574 os.chown(paths.dns, -1, paths.bind_gid)
1575 # chmod needed to cope with umask
1576 os.chmod(dns_dir, 0775)
1577 os.chmod(paths.dns, 0664)
1579 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1581 if targetdir is None:
1582 os.system(rndc + " unfreeze " + lp.get("realm"))
1585 def create_dns_update_list(lp, logger, paths, setup_path):
1586 """Write out a dns_update_list file"""
1587 # note that we use no variable substitution on this file
1588 # the substitution is done at runtime by samba_dnsupdate
1589 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1590 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1593 def create_named_conf(paths, setup_path, realm, dnsdomain,
1595 """Write out a file containing zone statements suitable for inclusion in a
1596 named.conf file (including GSS-TSIG configuration).
1598 :param paths: all paths
1599 :param setup_path: Setup path function.
1600 :param realm: Realm name
1601 :param dnsdomain: DNS Domain name
1602 :param private_dir: Path to private directory
1603 :param keytab_name: File name of DNS keytab file
1606 setup_file(setup_path("named.conf"), paths.namedconf, {
1607 "DNSDOMAIN": dnsdomain,
1609 "ZONE_FILE": paths.dns,
1610 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1611 "NAMED_CONF": paths.namedconf,
1612 "NAMED_CONF_UPDATE": paths.namedconf_update
1615 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1617 def create_named_txt(path, setup_path, realm, dnsdomain,
1618 private_dir, keytab_name):
1619 """Write out a file containing zone statements suitable for inclusion in a
1620 named.conf file (including GSS-TSIG configuration).
1622 :param path: Path of the new named.conf file.
1623 :param setup_path: Setup path function.
1624 :param realm: Realm name
1625 :param dnsdomain: DNS Domain name
1626 :param private_dir: Path to private directory
1627 :param keytab_name: File name of DNS keytab file
1630 setup_file(setup_path("named.txt"), path, {
1631 "DNSDOMAIN": dnsdomain,
1633 "DNS_KEYTAB": keytab_name,
1634 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1635 "PRIVATE_DIR": private_dir
1638 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1639 """Write out a file containing zone statements suitable for inclusion in a
1640 named.conf file (including GSS-TSIG configuration).
1642 :param path: Path of the new named.conf file.
1643 :param setup_path: Setup path function.
1644 :param dnsdomain: DNS Domain name
1645 :param hostname: Local hostname
1646 :param realm: Realm name
1648 setup_file(setup_path("krb5.conf"), path, {
1649 "DNSDOMAIN": dnsdomain,
1650 "HOSTNAME": hostname,
1655 class ProvisioningError(Exception):
1656 """A generic provision error."""
1658 def __init__(self, value):
1662 return "ProvisioningError: " + self.value
1665 class InvalidNetbiosName(Exception):
1666 """A specified name was not a valid NetBIOS name."""
1667 def __init__(self, name):
1668 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)