2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
42 from samba.auth import system_session, admin_session
44 from samba import version, Ldb, substitute_var, valid_netbios_name
45 from samba import check_all_substituted, read_and_sub_file, setup_file
46 from samba.dsdb import DS_DOMAIN_FUNCTION_2003, DS_DC_FUNCTION_2008
47 from samba.dcerpc import security
48 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
49 from samba.idmap import IDmapDB
50 from samba.ntacls import setntacl, dsacl2fsacl
51 from samba.ndr import ndr_pack,ndr_unpack
52 from samba.schema import Schema
53 from ms_display_specifiers import read_ms_ldif
54 from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
55 from provisionexceptions import ProvisioningError, InvalidNetbiosName
57 __docformat__ = "restructuredText"
60 """Find the setup directory used by provision."""
61 dirname = os.path.dirname(__file__)
62 if "/site-packages/" in dirname:
63 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
64 for suffix in ["share/setup", "share/samba/setup", "setup"]:
65 ret = os.path.join(prefix, suffix)
66 if os.path.isdir(ret):
69 ret = os.path.join(dirname, "../../../setup")
70 if os.path.isdir(ret):
72 raise Exception("Unable to find setup directory.")
74 # descriptors of the naming contexts
75 # hard coded at this point, but will probably be changed when
76 # we enable different fsmo roles
78 def get_config_descriptor(domain_sid):
79 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
81 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
82 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
84 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
85 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
86 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
87 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
88 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
89 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
90 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
91 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
92 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
93 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
94 sec = security.descriptor.from_sddl(sddl, domain_sid)
97 def get_domain_descriptor(domain_sid):
98 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
99 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
100 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
101 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
102 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
103 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
104 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
105 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
106 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
107 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
108 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
109 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
110 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
111 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
112 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
113 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
114 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
119 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
120 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
121 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
122 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
125 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
126 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
127 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
128 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
129 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
133 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
134 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
135 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
136 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
139 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
141 "(A;;RPLCLORC;;;ED)" \
142 "(A;;RPLCLORC;;;AU)" \
143 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
144 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
145 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
146 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
147 sec = security.descriptor.from_sddl(sddl, domain_sid)
150 DEFAULTSITE = "Default-First-Site-Name"
154 class ProvisionPaths(object):
156 self.shareconf = None
167 self.dns_keytab = None
170 self.private_dir = None
173 class ProvisionNames(object):
179 self.ldapmanagerdn = None
180 self.dnsdomain = None
182 self.netbiosname = None
189 class ProvisionResult(object):
196 def check_install(lp, session_info, credentials):
197 """Check whether the current install seems ok.
199 :param lp: Loadparm context
200 :param session_info: Session information
201 :param credentials: Credentials
203 if lp.get("realm") == "":
204 raise Exception("Realm empty")
205 ldb = Ldb(lp.get("sam database"), session_info=session_info,
206 credentials=credentials, lp=lp)
207 if len(ldb.search("(cn=Administrator)")) != 1:
208 raise ProvisioningError("No administrator account found")
211 def findnss(nssfn, names):
212 """Find a user or group from a list of possibilities.
214 :param nssfn: NSS Function to try (should raise KeyError if not found)
215 :param names: Names to check.
216 :return: Value return by first names list.
223 raise KeyError("Unable to find user/group in %r" % names)
226 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
227 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
230 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
231 """Setup a ldb in the private dir.
233 :param ldb: LDB file to import data into
234 :param ldif_path: Path of the LDIF file to load
235 :param subst_vars: Optional variables to subsitute in LDIF.
236 :param nocontrols: Optional list of controls, can be None for no controls
238 assert isinstance(ldif_path, str)
239 data = read_and_sub_file(ldif_path, subst_vars)
240 ldb.add_ldif(data, controls)
243 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
244 """Modify a ldb in the private dir.
246 :param ldb: LDB object.
247 :param ldif_path: LDIF file path.
248 :param subst_vars: Optional dictionary with substitution variables.
250 data = read_and_sub_file(ldif_path, subst_vars)
251 ldb.modify_ldif(data)
254 def setup_ldb(ldb, ldif_path, subst_vars):
255 """Import a LDIF a file into a LDB handle, optionally substituting variables.
257 :note: Either all LDIF data will be added or none (using transactions).
259 :param ldb: LDB file to import into.
260 :param ldif_path: Path to the LDIF file.
261 :param subst_vars: Dictionary with substitution variables.
263 assert ldb is not None
264 ldb.transaction_start()
266 setup_add_ldif(ldb, ldif_path, subst_vars)
268 ldb.transaction_cancel()
270 ldb.transaction_commit()
273 def provision_paths_from_lp(lp, dnsdomain):
274 """Set the default paths for provisioning.
276 :param lp: Loadparm context.
277 :param dnsdomain: DNS Domain name
279 paths = ProvisionPaths()
280 paths.private_dir = lp.get("private dir")
282 # This is stored without path prefix for the "privateKeytab" attribute in
283 # "secrets_dns.ldif".
284 paths.dns_keytab = "dns.keytab"
286 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
287 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
288 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
289 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
290 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
291 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
292 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
293 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
294 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
295 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
296 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
297 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
298 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
299 paths.phpldapadminconfig = os.path.join(paths.private_dir,
300 "phpldapadmin-config.php")
301 paths.hklm = "hklm.ldb"
302 paths.hkcr = "hkcr.ldb"
303 paths.hkcu = "hkcu.ldb"
304 paths.hku = "hku.ldb"
305 paths.hkpd = "hkpd.ldb"
306 paths.hkpt = "hkpt.ldb"
307 paths.sysvol = lp.get("path", "sysvol")
308 paths.netlogon = lp.get("path", "netlogon")
309 paths.smbconf = lp.configfile
313 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
314 serverrole=None, rootdn=None, domaindn=None, configdn=None,
315 schemadn=None, serverdn=None, sitename=None):
316 """Guess configuration settings to use."""
319 hostname = socket.gethostname().split(".")[0]
321 netbiosname = lp.get("netbios name")
322 if netbiosname is None:
323 netbiosname = hostname
324 assert netbiosname is not None
325 netbiosname = netbiosname.upper()
326 if not valid_netbios_name(netbiosname):
327 raise InvalidNetbiosName(netbiosname)
329 if dnsdomain is None:
330 dnsdomain = lp.get("realm")
331 if dnsdomain is None or dnsdomain == "":
332 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
334 dnsdomain = dnsdomain.lower()
336 if serverrole is None:
337 serverrole = lp.get("server role")
338 if serverrole is None:
339 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
341 serverrole = serverrole.lower()
343 realm = dnsdomain.upper()
345 if lp.get("realm") == "":
346 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
348 if lp.get("realm").upper() != realm:
349 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))
351 if lp.get("server role").lower() != serverrole:
352 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))
354 if serverrole == "domain controller":
356 # This will, for better or worse, default to 'WORKGROUP'
357 domain = lp.get("workgroup")
358 domain = domain.upper()
360 if lp.get("workgroup").upper() != domain:
361 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))
364 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
368 domaindn = "DC=" + netbiosname
370 if not valid_netbios_name(domain):
371 raise InvalidNetbiosName(domain)
373 if hostname.upper() == realm:
374 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
375 if netbiosname == realm:
376 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
378 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
384 configdn = "CN=Configuration," + rootdn
386 schemadn = "CN=Schema," + configdn
391 names = ProvisionNames()
392 names.rootdn = rootdn
393 names.domaindn = domaindn
394 names.configdn = configdn
395 names.schemadn = schemadn
396 names.ldapmanagerdn = "CN=Manager," + rootdn
397 names.dnsdomain = dnsdomain
398 names.domain = domain
400 names.netbiosname = netbiosname
401 names.hostname = hostname
402 names.sitename = sitename
403 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
408 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
409 targetdir, sid_generator,eadb):
410 """Create a new smb.conf file based on a couple of basic settings.
412 assert smbconf is not None
414 hostname = socket.gethostname().split(".")[0]
415 netbiosname = hostname.upper()
417 if serverrole is None:
418 serverrole = "standalone"
420 assert serverrole in ("domain controller", "member server", "standalone")
421 if serverrole == "domain controller":
423 elif serverrole == "member server":
424 smbconfsuffix = "member"
425 elif serverrole == "standalone":
426 smbconfsuffix = "standalone"
428 if sid_generator is None:
429 sid_generator = "internal"
431 assert domain is not None
432 domain = domain.upper()
434 assert realm is not None
435 realm = realm.upper()
437 default_lp = param.LoadParm()
438 #Load non-existant file
439 if os.path.exists(smbconf):
440 default_lp.load(smbconf)
442 if targetdir is not None:
443 privdir = os.path.join(targetdir, "private")
445 privdir = default_lp.get("private dir")
446 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
450 if targetdir is not None:
451 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
452 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
454 default_lp.set("lock dir", os.path.abspath(targetdir))
459 if sid_generator == "internal":
460 sid_generator_line = ""
462 sid_generator_line = "sid generator = " + sid_generator
464 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
465 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
467 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
469 "NETBIOS_NAME": netbiosname,
472 "SERVERROLE": serverrole,
473 "NETLOGONPATH": netlogon,
474 "SYSVOLPATH": sysvol,
475 "SIDGENERATOR_LINE": sid_generator_line,
476 "PRIVATEDIR_LINE": privatedir_line,
477 "LOCKDIR_LINE": lockdir_line,
478 "POSIXEADB_LINE": posixeadb_line
482 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
483 users_gid, wheel_gid):
484 """setup reasonable name mappings for sam names to unix names.
486 :param samdb: SamDB object.
487 :param idmap: IDmap db object.
488 :param sid: The domain sid.
489 :param domaindn: The domain DN.
490 :param root_uid: uid of the UNIX root user.
491 :param nobody_uid: uid of the UNIX nobody user.
492 :param users_gid: gid of the UNIX users group.
493 :param wheel_gid: gid of the UNIX wheel group."""
495 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
496 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
498 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
499 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
501 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
502 provision_backend, names, schema,
505 """Setup the partitions for the SAM database.
507 Alternatively, provision() may call this, and then populate the database.
509 :note: This will wipe the Sam Database!
511 :note: This function always removes the local SAM LDB file. The erase
512 parameter controls whether to erase the existing data, which
513 may not be stored locally but in LDAP.
516 assert session_info is not None
518 # We use options=["modules:"] to stop the modules loading - we
519 # just want to wipe and re-initialise the database, not start it up
522 os.unlink(samdb_path)
526 samdb = Ldb(url=samdb_path, session_info=session_info,
527 lp=lp, options=["modules:"])
529 ldap_backend_line = "# No LDAP backend"
530 if provision_backend.type is not "ldb":
531 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
533 samdb.transaction_start()
535 message("Setting up sam.ldb partitions and settings")
536 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
537 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
538 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
539 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
540 "LDAP_BACKEND_LINE": ldap_backend_line,
544 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
545 "BACKEND_TYPE": provision_backend.type,
546 "SERVER_ROLE": serverrole
549 message("Setting up sam.ldb rootDSE")
550 setup_samdb_rootdse(samdb, setup_path, names)
553 samdb.transaction_cancel()
556 samdb.transaction_commit()
559 def secretsdb_self_join(secretsdb, domain,
560 netbiosname, machinepass, domainsid=None,
561 realm=None, dnsdomain=None,
563 key_version_number=1,
564 secure_channel_type=SEC_CHAN_WKSTA):
565 """Add domain join-specific bits to a secrets database.
567 :param secretsdb: Ldb Handle to the secrets database
568 :param machinepass: Machine password
570 attrs=["whenChanged",
578 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
579 msg["secureChannelType"] = str(secure_channel_type)
580 msg["flatname"] = [domain]
581 msg["objectClass"] = ["top", "primaryDomain"]
582 if realm is not None:
583 if dnsdomain is None:
584 dnsdomain = realm.lower()
585 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
587 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
588 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
589 msg["privateKeytab"] = ["secrets.keytab"]
592 msg["secret"] = [machinepass]
593 msg["samAccountName"] = ["%s$" % netbiosname]
594 msg["secureChannelType"] = [str(secure_channel_type)]
595 if domainsid is not None:
596 msg["objectSid"] = [ndr_pack(domainsid)]
598 res = secretsdb.search(base="cn=Primary Domains",
600 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
601 scope=ldb.SCOPE_ONELEVEL)
604 if del_msg.dn is not msg.dn:
605 secretsdb.delete(del_msg.dn)
607 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
610 msg["priorSecret"] = res[0]["secret"]
611 msg["priorWhenChanged"] = res[0]["whenChanged"]
613 if res["privateKeytab"] is not None:
614 msg["privateKeytab"] = res[0]["privateKeytab"]
616 if res["krb5Keytab"] is not None:
617 msg["krb5Keytab"] = res[0]["krb5Keytab"]
620 el.set_flags(ldb.FLAG_MOD_REPLACE)
621 secretsdb.modify(msg)
626 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
628 dns_keytab_path, dnspass):
629 """Add DNS specific bits to a secrets database.
631 :param secretsdb: Ldb Handle to the secrets database
632 :param setup_path: Setup path function
633 :param machinepass: Machine password
636 os.unlink(os.path.join(private_dir, dns_keytab_path))
640 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
642 "DNSDOMAIN": dnsdomain,
643 "DNS_KEYTAB": dns_keytab_path,
644 "DNSPASS_B64": b64encode(dnspass),
648 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
649 """Setup the secrets database.
651 :param path: Path to the secrets database.
652 :param setup_path: Get the path to a setup file.
653 :param session_info: Session info.
654 :param credentials: Credentials
655 :param lp: Loadparm context
656 :return: LDB handle for the created secrets database
658 if os.path.exists(path):
660 secrets_ldb = Ldb(path, session_info=session_info,
663 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
664 secrets_ldb = Ldb(path, session_info=session_info,
666 secrets_ldb.transaction_start()
667 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
669 if backend_credentials is not None and backend_credentials.authentication_requested():
670 if backend_credentials.get_bind_dn() is not None:
671 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
672 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
673 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
676 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
677 "LDAPADMINUSER": backend_credentials.get_username(),
678 "LDAPADMINREALM": backend_credentials.get_realm(),
679 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
684 def setup_privileges(path, setup_path, session_info, lp):
685 """Setup the privileges database.
687 :param path: Path to the privileges database.
688 :param setup_path: Get the path to a setup file.
689 :param session_info: Session info.
690 :param credentials: Credentials
691 :param lp: Loadparm context
692 :return: LDB handle for the created secrets database
694 if os.path.exists(path):
696 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
697 privilege_ldb.erase()
698 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
701 def setup_registry(path, setup_path, session_info, lp):
702 """Setup the registry.
704 :param path: Path to the registry database
705 :param setup_path: Function that returns the path to a setup.
706 :param session_info: Session information
707 :param credentials: Credentials
708 :param lp: Loadparm context
710 reg = registry.Registry()
711 hive = registry.open_ldb(path, session_info=session_info,
713 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
714 provision_reg = setup_path("provision.reg")
715 assert os.path.exists(provision_reg)
716 reg.diff_apply(provision_reg)
719 def setup_idmapdb(path, setup_path, session_info, lp):
720 """Setup the idmap database.
722 :param path: path to the idmap database
723 :param setup_path: Function that returns a path to a setup file
724 :param session_info: Session information
725 :param credentials: Credentials
726 :param lp: Loadparm context
728 if os.path.exists(path):
731 idmap_ldb = IDmapDB(path, session_info=session_info,
735 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
739 def setup_samdb_rootdse(samdb, setup_path, names):
740 """Setup the SamDB rootdse.
742 :param samdb: Sam Database handle
743 :param setup_path: Obtain setup path
745 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
746 "SCHEMADN": names.schemadn,
747 "NETBIOSNAME": names.netbiosname,
748 "DNSDOMAIN": names.dnsdomain,
749 "REALM": names.realm,
750 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
751 "DOMAINDN": names.domaindn,
752 "ROOTDN": names.rootdn,
753 "CONFIGDN": names.configdn,
754 "SERVERDN": names.serverdn,
758 def setup_self_join(samdb, names,
759 machinepass, dnspass,
760 domainsid, invocationid, setup_path,
761 policyguid, policyguid_dc, domainControllerFunctionality,
763 """Join a host to its own domain."""
764 assert isinstance(invocationid, str)
765 if ntdsguid is not None:
766 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
769 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
770 "CONFIGDN": names.configdn,
771 "SCHEMADN": names.schemadn,
772 "DOMAINDN": names.domaindn,
773 "SERVERDN": names.serverdn,
774 "INVOCATIONID": invocationid,
775 "NETBIOSNAME": names.netbiosname,
776 "DEFAULTSITE": names.sitename,
777 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
778 "MACHINEPASS_B64": b64encode(machinepass),
779 "REALM": names.realm,
780 "DOMAIN": names.domain,
781 "DOMAINSID": str(domainsid),
782 "DNSDOMAIN": names.dnsdomain,
783 "SAMBA_VERSION_STRING": version,
784 "NTDSGUID": ntdsguid_line,
785 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
787 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
788 "POLICYGUID": policyguid,
789 "POLICYGUID_DC": policyguid_dc,
790 "DNSDOMAIN": names.dnsdomain,
791 "DOMAINSID": str(domainsid),
792 "DOMAINDN": names.domaindn})
794 # add the NTDSGUID based SPNs
795 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
796 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
797 expression="", scope=ldb.SCOPE_BASE)
798 assert isinstance(names.ntdsguid, str)
800 # Setup fSMORoleOwner entries to point at the newly created DC entry
801 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
802 "DOMAIN": names.domain,
803 "DNSDOMAIN": names.dnsdomain,
804 "DOMAINDN": names.domaindn,
805 "CONFIGDN": names.configdn,
806 "SCHEMADN": names.schemadn,
807 "DEFAULTSITE": names.sitename,
808 "SERVERDN": names.serverdn,
809 "NETBIOSNAME": names.netbiosname,
810 "NTDSGUID": names.ntdsguid,
811 "DNSPASS_B64": b64encode(dnspass),
814 def getpolicypath(sysvolpath, dnsdomain, guid):
817 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
820 def create_gpo_struct(policy_path):
821 os.makedirs(policy_path, 0755)
822 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
823 "[General]\r\nVersion=65543")
824 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
825 os.makedirs(os.path.join(policy_path, "USER"), 0755)
827 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
828 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
829 create_gpo_struct(policy_path)
831 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
832 create_gpo_struct(policy_path)
834 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
836 domainsid, domainguid, policyguid, policyguid_dc,
837 fill, adminpass, krbtgtpass,
838 machinepass, invocationid, dnspass, ntdsguid,
839 serverrole, dom_for_fun_level=None,
841 """Setup a complete SAM Database.
843 :note: This will wipe the main SAM database file!
846 # ATTENTION: Do NOT change these default values without discussion with the
847 # team and/or release manager. They have a big impact on the whole program!
848 domainControllerFunctionality = DS_DC_FUNCTION_2008
850 if dom_for_fun_level is None:
851 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
852 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
853 message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
855 if dom_for_fun_level > domainControllerFunctionality:
856 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!")
858 domainFunctionality = dom_for_fun_level
859 forestFunctionality = dom_for_fun_level
861 # Also wipes the database
862 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
863 provision_backend=provision_backend, session_info=session_info,
864 names=names, serverrole=serverrole, schema=schema)
867 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
869 # Load the database, but importantly, use Ldb not SamDB as we don't want to
870 # load the global schema
871 samdb = Ldb(session_info=session_info,
872 credentials=provision_backend.credentials, lp=lp)
874 message("Pre-loading the Samba 4 and AD schema")
876 # Load the schema from the one we computed earlier
877 samdb.set_schema_from_ldb(schema.ldb)
879 # And now we can connect to the DB - the schema won't be loaded from the DB
885 samdb.transaction_start()
887 # Set the domain functionality levels onto the database.
888 # Various module (the password_hash module in particular) need
889 # to know what level of AD we are emulating.
891 # These will be fixed into the database via the database
892 # modifictions below, but we need them set from the start.
893 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
894 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
895 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
897 samdb.domain_sid = str(domainsid)
898 samdb.invocation_id = invocationid
900 message("Adding DomainDN: %s" % names.domaindn)
902 #impersonate domain admin
903 admin_session_info = admin_session(lp, str(domainsid))
904 samdb.set_session_info(admin_session_info)
905 if domainguid is not None:
906 domainguid_line = "objectGUID: %s\n-" % domainguid
910 descr = b64encode(get_domain_descriptor(domainsid))
911 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
912 "DOMAINDN": names.domaindn,
913 "DOMAINGUID": domainguid_line,
918 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
919 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
920 "DOMAINSID": str(domainsid),
921 "SCHEMADN": names.schemadn,
922 "NETBIOSNAME": names.netbiosname,
923 "DEFAULTSITE": names.sitename,
924 "CONFIGDN": names.configdn,
925 "SERVERDN": names.serverdn,
926 "POLICYGUID": policyguid,
927 "DOMAINDN": names.domaindn,
928 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
929 "SAMBA_VERSION_STRING": version
932 message("Adding configuration container")
933 descr = b64encode(get_config_descriptor(domainsid))
934 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
935 "CONFIGDN": names.configdn,
939 # The LDIF here was created when the Schema object was constructed
940 message("Setting up sam.ldb schema")
941 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
942 samdb.modify_ldif(schema.schema_dn_modify)
943 samdb.write_prefixes_from_schema()
944 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
945 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
946 {"SCHEMADN": names.schemadn})
948 message("Reopening sam.ldb with new schema")
949 samdb.transaction_commit()
950 samdb = SamDB(session_info=admin_session_info,
951 credentials=provision_backend.credentials, lp=lp,
954 samdb.transaction_start()
955 samdb.invocation_id = invocationid
957 message("Setting up sam.ldb configuration data")
958 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
959 "CONFIGDN": names.configdn,
960 "NETBIOSNAME": names.netbiosname,
961 "DEFAULTSITE": names.sitename,
962 "DNSDOMAIN": names.dnsdomain,
963 "DOMAIN": names.domain,
964 "SCHEMADN": names.schemadn,
965 "DOMAINDN": names.domaindn,
966 "SERVERDN": names.serverdn,
967 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
970 message("Setting up display specifiers")
971 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
972 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
973 check_all_substituted(display_specifiers_ldif)
974 samdb.add_ldif(display_specifiers_ldif)
976 message("Adding users container")
977 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
978 "DOMAINDN": names.domaindn})
979 message("Modifying users container")
980 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
981 "DOMAINDN": names.domaindn})
982 message("Adding computers container")
983 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
984 "DOMAINDN": names.domaindn})
985 message("Modifying computers container")
986 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
987 "DOMAINDN": names.domaindn})
988 message("Setting up sam.ldb data")
989 setup_add_ldif(samdb, setup_path("provision.ldif"), {
990 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
991 "DOMAINDN": names.domaindn,
992 "NETBIOSNAME": names.netbiosname,
993 "DEFAULTSITE": names.sitename,
994 "CONFIGDN": names.configdn,
995 "SERVERDN": names.serverdn,
996 "POLICYGUID_DC": policyguid_dc
999 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1000 "DOMAINDN": names.domaindn})
1002 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1003 "CONFIGDN": names.configdn,
1004 "SCHEMADN": names.schemadn})
1005 if fill == FILL_FULL:
1006 message("Setting up sam.ldb users and groups")
1007 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1008 "DOMAINDN": names.domaindn,
1009 "DOMAINSID": str(domainsid),
1010 "CONFIGDN": names.configdn,
1011 "ADMINPASS_B64": b64encode(adminpass),
1012 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1015 message("Setting up self join")
1016 setup_self_join(samdb, names=names, invocationid=invocationid,
1018 machinepass=machinepass,
1019 domainsid=domainsid, policyguid=policyguid,
1020 policyguid_dc=policyguid_dc,
1021 setup_path=setup_path,
1022 domainControllerFunctionality=domainControllerFunctionality,
1025 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1026 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1027 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1028 assert isinstance(names.ntdsguid, str)
1031 samdb.transaction_cancel()
1034 samdb.transaction_commit()
1039 FILL_NT4SYNC = "NT4SYNC"
1041 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1042 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)"
1044 def set_dir_acl(path, acl, lp, domsid):
1045 setntacl(lp, path, acl, domsid)
1046 for root, dirs, files in os.walk(path, topdown=False):
1048 setntacl(lp, os.path.join(root, name), acl, domsid)
1050 setntacl(lp, os.path.join(root, name), acl, domsid)
1053 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1055 policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1056 set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
1058 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1059 attrs=["cn","nTSecurityDescriptor"],
1060 expression="", scope=ldb.SCOPE_ONELEVEL)
1062 acl = ndr_unpack(security.descriptor,str(policy["nTSecurityDescriptor"])).as_sddl()
1063 policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1064 set_dir_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
1066 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1069 os.chown(sysvol,-1,gid)
1075 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1076 for root, dirs, files in os.walk(sysvol, topdown=False):
1079 os.chown(os.path.join(root, name),-1,gid)
1080 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1083 os.chown(os.path.join(root, name),-1,gid)
1084 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1085 set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1088 def provision(setup_dir, message, session_info,
1089 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1091 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1093 domain=None, hostname=None, hostip=None, hostip6=None,
1094 domainsid=None, adminpass=None, ldapadminpass=None,
1095 krbtgtpass=None, domainguid=None,
1096 policyguid=None, policyguid_dc=None, invocationid=None,
1097 machinepass=None, ntdsguid=None,
1098 dnspass=None, root=None, nobody=None, users=None,
1099 wheel=None, backup=None, aci=None, serverrole=None,
1100 dom_for_fun_level=None,
1101 ldap_backend_extra_port=None, backend_type=None,
1103 ol_mmr_urls=None, ol_olc=None,
1104 setup_ds_path=None, slapd_path=None, nosync=False,
1105 ldap_dryrun_mode=False,useeadb=False):
1108 :note: caution, this wipes all existing data!
1111 def setup_path(file):
1112 return os.path.join(setup_dir, file)
1114 if domainsid is None:
1115 domainsid = security.random_sid()
1117 domainsid = security.dom_sid(domainsid)
1119 # create/adapt the group policy GUIDs
1120 if policyguid is None:
1121 policyguid = str(uuid.uuid4())
1122 policyguid = policyguid.upper()
1123 if policyguid_dc is None:
1124 policyguid_dc = str(uuid.uuid4())
1125 policyguid_dc = policyguid_dc.upper()
1127 if adminpass is None:
1128 adminpass = samba.generate_random_password(12, 32)
1129 if krbtgtpass is None:
1130 krbtgtpass = samba.generate_random_password(128, 255)
1131 if machinepass is None:
1132 machinepass = samba.generate_random_password(128, 255)
1134 dnspass = samba.generate_random_password(128, 255)
1135 if ldapadminpass is None:
1136 #Make a new, random password between Samba and it's LDAP server
1137 ldapadminpass=samba.generate_random_password(128, 255)
1139 if backend_type is None:
1140 backend_type = "ldb"
1142 sid_generator = "internal"
1143 if backend_type == "fedora-ds":
1144 sid_generator = "backend"
1146 root_uid = findnss_uid([root or "root"])
1147 nobody_uid = findnss_uid([nobody or "nobody"])
1148 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1150 wheel_gid = findnss_gid(["wheel", "adm"])
1152 wheel_gid = findnss_gid([wheel])
1154 bind_gid = findnss_gid(["bind", "named"])
1158 if targetdir is not None:
1159 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1160 os.makedirs(os.path.join(targetdir, "etc"))
1161 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1162 elif smbconf is None:
1163 smbconf = param.default_path()
1165 # only install a new smb.conf if there isn't one there already
1166 if os.path.exists(smbconf):
1167 # if Samba Team members can't figure out the weird errors
1168 # loading an empty smb.conf gives, then we need to be smarter.
1169 # Pretend it just didn't exist --abartlet
1170 data = open(smbconf, 'r').read()
1171 data = data.lstrip()
1172 if data is None or data == "":
1173 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1174 targetdir, sid_generator, useeadb)
1176 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1177 targetdir, sid_generator, useeadb)
1179 lp = param.LoadParm()
1182 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1183 dnsdomain=realm, serverrole=serverrole,
1184 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1185 serverdn=serverdn, sitename=sitename)
1187 paths = provision_paths_from_lp(lp, names.dnsdomain)
1189 paths.bind_gid = bind_gid
1192 hostips = samba.interface_ips(lp, False)
1193 if len(hostips) == 0:
1194 message("No external IPv4 address has been found: I use the loopback.")
1195 hostip = '127.0.0.1'
1198 if len(hostips) > 1:
1199 message("More than one IPv4 address found: I use " + hostip + ".")
1203 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1206 if hostip6 == '::1' and ip[-1][0] != '::1':
1208 except socket.gaierror, (socket.EAI_NODATA, msg):
1211 if serverrole is None:
1212 serverrole = lp.get("server role")
1214 assert serverrole in ("domain controller", "member server", "standalone")
1215 if invocationid is None:
1216 invocationid = str(uuid.uuid4())
1218 if not os.path.exists(paths.private_dir):
1219 os.mkdir(paths.private_dir)
1220 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1221 os.mkdir(os.path.join(paths.private_dir,"tls"))
1223 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1225 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1227 if backend_type == "ldb":
1228 provision_backend = LDBBackend(backend_type,
1229 paths=paths, setup_path=setup_path,
1230 lp=lp, credentials=credentials,
1233 elif backend_type == "existing":
1234 provision_backend = ExistingBackend(backend_type,
1235 paths=paths, setup_path=setup_path,
1236 lp=lp, credentials=credentials,
1239 ldapi_url=ldapi_url)
1240 elif backend_type == "fedora-ds":
1241 provision_backend = FDSBackend(backend_type,
1242 paths=paths, setup_path=setup_path,
1243 lp=lp, credentials=credentials,
1246 domainsid=domainsid,
1249 ldapadminpass=ldapadminpass,
1250 slapd_path=slapd_path,
1251 ldap_backend_extra_port=ldap_backend_extra_port,
1252 ldap_dryrun_mode=ldap_dryrun_mode,
1254 setup_ds_path=setup_ds_path)
1255 elif backend_type == "openldap":
1256 provision_backend = OpenLDAPBackend(backend_type,
1257 paths=paths, setup_path=setup_path,
1258 lp=lp, credentials=credentials,
1261 domainsid=domainsid,
1264 ldapadminpass=ldapadminpass,
1265 slapd_path=slapd_path,
1266 ldap_backend_extra_port=ldap_backend_extra_port,
1267 ldap_dryrun_mode=ldap_dryrun_mode,
1268 ol_mmr_urls=ol_mmr_urls,
1271 raise ProvisioningError("Unknown LDAP backend type selected")
1273 provision_backend.init()
1274 provision_backend.start()
1276 # only install a new shares config db if there is none
1277 if not os.path.exists(paths.shareconf):
1278 message("Setting up share.ldb")
1279 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1281 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1284 message("Setting up secrets.ldb")
1285 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1286 session_info=session_info,
1287 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1289 message("Setting up the registry")
1290 setup_registry(paths.hklm, setup_path, session_info,
1293 message("Setting up the privileges database")
1294 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1296 message("Setting up idmap db")
1297 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1300 message("Setting up SAM db")
1301 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1302 provision_backend, lp, names,
1304 domainsid=domainsid,
1305 schema=schema, domainguid=domainguid,
1306 policyguid=policyguid, policyguid_dc=policyguid_dc,
1308 adminpass=adminpass, krbtgtpass=krbtgtpass,
1309 invocationid=invocationid,
1310 machinepass=machinepass, dnspass=dnspass,
1311 ntdsguid=ntdsguid, serverrole=serverrole,
1312 dom_for_fun_level=dom_for_fun_level)
1314 if serverrole == "domain controller":
1315 if paths.netlogon is None:
1316 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1317 message("Please either remove %s or see the template at %s" %
1318 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1319 assert(paths.netlogon is not None)
1321 if paths.sysvol is None:
1322 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1323 message("Please either remove %s or see the template at %s" %
1324 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1325 assert(paths.sysvol is not None)
1328 if not os.path.isdir(paths.netlogon):
1329 os.makedirs(paths.netlogon, 0755)
1331 if samdb_fill == FILL_FULL:
1332 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1333 root_uid=root_uid, nobody_uid=nobody_uid,
1334 users_gid=users_gid, wheel_gid=wheel_gid)
1336 if serverrole == "domain controller":
1337 # Set up group policies (domain policy and domain controller policy)
1338 setup_gpo(paths.sysvol,names.dnsdomain,policyguid,policyguid_dc)
1339 setsysvolacl(samdb,paths.netlogon,paths.sysvol,wheel_gid,domainsid,names.dnsdomain,names.domaindn,lp)
1341 message("Setting up sam.ldb rootDSE marking as synchronized")
1342 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1344 secretsdb_self_join(secrets_ldb, domain=names.domain,
1346 dnsdomain=names.dnsdomain,
1347 netbiosname=names.netbiosname,
1348 domainsid=domainsid,
1349 machinepass=machinepass,
1350 secure_channel_type=SEC_CHAN_BDC)
1352 if serverrole == "domain controller":
1353 secretsdb_setup_dns(secrets_ldb, setup_path,
1355 realm=names.realm, dnsdomain=names.dnsdomain,
1356 dns_keytab_path=paths.dns_keytab,
1359 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1360 assert isinstance(domainguid, str)
1362 # Only make a zone file on the first DC, it should be replicated
1363 # with DNS replication
1364 create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain=names.dnsdomain,
1366 hostip6=hostip6, hostname=names.hostname,
1368 domainguid=domainguid, ntdsguid=names.ntdsguid)
1370 create_named_conf(paths, setup_path, realm=names.realm,
1371 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1373 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1374 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1375 keytab_name=paths.dns_keytab)
1376 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1377 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1379 create_krb5_conf(paths.krb5conf, setup_path,
1380 dnsdomain=names.dnsdomain, hostname=names.hostname,
1382 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1384 if serverrole == "domain controller":
1385 create_dns_update_list(lp, message, paths, setup_path)
1387 provision_backend.post_setup()
1388 provision_backend.shutdown()
1390 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1393 #Now commit the secrets.ldb to disk
1394 secrets_ldb.transaction_commit()
1396 # the commit creates the dns.keytab, now chown it
1397 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1398 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1400 os.chmod(dns_keytab_path, 0640)
1401 os.chown(dns_keytab_path, -1, paths.bind_gid)
1403 message("Failed to chown %s to bind gid %u" % (dns_keytab_path, paths.bind_gid))
1406 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1408 message("Once the above files are installed, your Samba4 server will be ready to use")
1409 message("Server Role: %s" % serverrole)
1410 message("Hostname: %s" % names.hostname)
1411 message("NetBIOS Domain: %s" % names.domain)
1412 message("DNS Domain: %s" % names.dnsdomain)
1413 message("DOMAIN SID: %s" % str(domainsid))
1414 if samdb_fill == FILL_FULL:
1415 message("Admin password: %s" % adminpass)
1416 if provision_backend.type is not "ldb":
1417 if provision_backend.credentials.get_bind_dn() is not None:
1418 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1420 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1422 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1424 if provision_backend.slapd_command_escaped is not None:
1425 # now display slapd_command_file.txt to show how slapd must be started next time
1426 message("Use later the following commandline to start slapd, then Samba:")
1427 message(provision_backend.slapd_command_escaped)
1428 message("This slapd-Commandline is also stored under: " + provision_backend.ldapdir + "/ldap_backend_startup.sh")
1431 result = ProvisionResult()
1432 result.domaindn = domaindn
1433 result.paths = paths
1435 result.samdb = samdb
1440 def provision_become_dc(setup_dir=None,
1441 smbconf=None, targetdir=None, realm=None,
1442 rootdn=None, domaindn=None, schemadn=None,
1443 configdn=None, serverdn=None,
1444 domain=None, hostname=None, domainsid=None,
1445 adminpass=None, krbtgtpass=None, domainguid=None,
1446 policyguid=None, policyguid_dc=None, invocationid=None,
1448 dnspass=None, root=None, nobody=None, users=None,
1449 wheel=None, backup=None, serverrole=None,
1450 ldap_backend=None, ldap_backend_type=None,
1451 sitename=None, debuglevel=1):
1454 """print a message if quiet is not set."""
1457 samba.set_debug_level(debuglevel)
1459 return provision(setup_dir, message, system_session(), None,
1460 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1461 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1462 configdn=configdn, serverdn=serverdn, domain=domain,
1463 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1464 machinepass=machinepass, serverrole="domain controller",
1468 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1469 """Create a PHP LDAP admin configuration file.
1471 :param path: Path to write the configuration to.
1472 :param setup_path: Function to generate setup paths.
1474 setup_file(setup_path("phpldapadmin-config.php"), path,
1475 {"S4_LDAPI_URI": ldapi_uri})
1478 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1479 hostip, hostip6, hostname, realm, domainguid,
1481 """Write out a DNS zone file, from the info in the current database.
1483 :param paths: paths object
1484 :param setup_path: Setup path function.
1485 :param dnsdomain: DNS Domain name
1486 :param domaindn: DN of the Domain
1487 :param hostip: Local IPv4 IP
1488 :param hostip6: Local IPv6 IP
1489 :param hostname: Local hostname
1490 :param realm: Realm name
1491 :param domainguid: GUID of the domain.
1492 :param ntdsguid: GUID of the hosts nTDSDSA record.
1494 assert isinstance(domainguid, str)
1496 if hostip6 is not None:
1497 hostip6_base_line = " IN AAAA " + hostip6
1498 hostip6_host_line = hostname + " IN AAAA " + hostip6
1500 hostip6_base_line = ""
1501 hostip6_host_line = ""
1503 if hostip is not None:
1504 hostip_base_line = " IN A " + hostip
1505 hostip_host_line = hostname + " IN A " + hostip
1507 hostip_base_line = ""
1508 hostip_host_line = ""
1510 dns_dir = os.path.dirname(paths.dns)
1513 shutil.rmtree(dns_dir, True)
1517 os.mkdir(dns_dir, 0775)
1519 # we need to freeze the zone while we update the contents
1520 if targetdir is None:
1521 rndc = ' '.join(lp.get("rndc command"))
1522 os.system(rndc + " freeze " + lp.get("realm"))
1524 setup_file(setup_path("provision.zone"), paths.dns, {
1525 "HOSTNAME": hostname,
1526 "DNSDOMAIN": dnsdomain,
1528 "HOSTIP_BASE_LINE": hostip_base_line,
1529 "HOSTIP_HOST_LINE": hostip_host_line,
1530 "DOMAINGUID": domainguid,
1531 "DATESTRING": time.strftime("%Y%m%d%H"),
1532 "DEFAULTSITE": DEFAULTSITE,
1533 "NTDSGUID": ntdsguid,
1534 "HOSTIP6_BASE_LINE": hostip6_base_line,
1535 "HOSTIP6_HOST_LINE": hostip6_host_line,
1538 # note that we use no variable substitution on this file
1539 # the substitution is done at runtime by samba_dnsupdate
1540 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1542 if paths.bind_gid is not None:
1544 os.chown(dns_dir, -1, paths.bind_gid)
1545 os.chown(paths.dns, -1, paths.bind_gid)
1546 # chmod needed to cope with umask
1547 os.chmod(dns_dir, 0775)
1548 os.chmod(paths.dns, 0664)
1550 message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1552 if targetdir is None:
1553 os.system(rndc + " unfreeze " + lp.get("realm"))
1556 def create_dns_update_list(lp, message, paths, setup_path):
1557 """Write out a dns_update_list file"""
1558 # note that we use no variable substitution on this file
1559 # the substitution is done at runtime by samba_dnsupdate
1560 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1563 def create_named_conf(paths, setup_path, realm, dnsdomain,
1565 """Write out a file containing zone statements suitable for inclusion in a
1566 named.conf file (including GSS-TSIG configuration).
1568 :param paths: all paths
1569 :param setup_path: Setup path function.
1570 :param realm: Realm name
1571 :param dnsdomain: DNS Domain name
1572 :param private_dir: Path to private directory
1573 :param keytab_name: File name of DNS keytab file
1576 setup_file(setup_path("named.conf"), paths.namedconf, {
1577 "DNSDOMAIN": dnsdomain,
1579 "ZONE_FILE": paths.dns,
1580 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1581 "NAMED_CONF": paths.namedconf,
1582 "NAMED_CONF_UPDATE": paths.namedconf_update
1585 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1587 def create_named_txt(path, setup_path, realm, dnsdomain,
1588 private_dir, keytab_name):
1589 """Write out a file containing zone statements suitable for inclusion in a
1590 named.conf file (including GSS-TSIG configuration).
1592 :param path: Path of the new named.conf file.
1593 :param setup_path: Setup path function.
1594 :param realm: Realm name
1595 :param dnsdomain: DNS Domain name
1596 :param private_dir: Path to private directory
1597 :param keytab_name: File name of DNS keytab file
1600 setup_file(setup_path("named.txt"), path, {
1601 "DNSDOMAIN": dnsdomain,
1603 "DNS_KEYTAB": keytab_name,
1604 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1605 "PRIVATE_DIR": private_dir
1608 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1609 """Write out a file containing zone statements suitable for inclusion in a
1610 named.conf file (including GSS-TSIG configuration).
1612 :param path: Path of the new named.conf file.
1613 :param setup_path: Setup path function.
1614 :param dnsdomain: DNS Domain name
1615 :param hostname: Local hostname
1616 :param realm: Realm name
1619 setup_file(setup_path("krb5.conf"), path, {
1620 "DNSDOMAIN": dnsdomain,
1621 "HOSTNAME": hostname,