2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
43 from auth import system_session, admin_session
44 from samba import version, Ldb, substitute_var, valid_netbios_name, setup_file
45 from samba import check_all_substituted, read_and_sub_file
46 from samba import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008
47 from samba.samdb import SamDB
48 from samba.idmap import IDmapDB
49 from samba.dcerpc import security
50 from samba.ndr import ndr_pack
52 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
53 from ms_display_specifiers import read_ms_ldif
54 from schema import Schema
55 from provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
56 from signal import SIGTERM
57 from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
59 __docformat__ = "restructuredText"
62 """Find the setup directory used by provision."""
63 dirname = os.path.dirname(__file__)
64 if "/site-packages/" in dirname:
65 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
66 for suffix in ["share/setup", "share/samba/setup", "setup"]:
67 ret = os.path.join(prefix, suffix)
68 if os.path.isdir(ret):
71 ret = os.path.join(dirname, "../../../setup")
72 if os.path.isdir(ret):
74 raise Exception("Unable to find setup directory.")
76 # descriptors of the naming contexts
77 # hard coded at this point, but will probably be changed when
78 # we enable different fsmo roles
80 def get_config_descriptor(domain_sid):
81 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
82 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
83 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
84 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
85 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
86 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
87 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
88 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
89 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
90 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
91 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
92 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
93 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
94 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
95 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
96 sec = security.descriptor.from_sddl(sddl, domain_sid)
97 return b64encode(ndr_pack(sec))
99 def get_domain_descriptor(domain_sid):
100 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
101 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
102 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
103 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
104 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
105 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
106 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
107 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
108 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
109 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
110 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-832762594-175224951-1765713900-498)" \
111 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
112 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
113 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
114 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
115 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
116 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
119 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
120 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
121 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)" \
122 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
123 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
124 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
126 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
127 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
128 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
129 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
130 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
131 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
133 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
134 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
135 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
136 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
137 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
138 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
141 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
143 "(A;;RPLCLORC;;;ED)" \
144 "(A;;RPLCLORC;;;AU)" \
145 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
146 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
147 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
148 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
149 sec = security.descriptor.from_sddl(sddl, domain_sid)
150 return b64encode(ndr_pack(sec))
152 DEFAULTSITE = "Default-First-Site-Name"
156 class ProvisioningError(Exception):
157 """A generic provision error."""
159 class InvalidNetbiosName(Exception):
160 """A specified name was not a valid NetBIOS name."""
161 def __init__(self, name):
162 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
165 class ProvisionPaths(object):
167 self.shareconf = None
178 self.dns_keytab = None
181 self.private_dir = None
183 self.slapdconf = None
184 self.modulesconf = None
185 self.memberofconf = None
187 self.olmmrserveridsconf = None
188 self.olmmrsyncreplconf = None
191 self.olcseedldif = None
194 class ProvisionNames(object):
200 self.ldapmanagerdn = None
201 self.dnsdomain = None
203 self.netbiosname = None
210 class ProvisionResult(object):
217 def check_install(lp, session_info, credentials):
218 """Check whether the current install seems ok.
220 :param lp: Loadparm context
221 :param session_info: Session information
222 :param credentials: Credentials
224 if lp.get("realm") == "":
225 raise Exception("Realm empty")
226 ldb = Ldb(lp.get("sam database"), session_info=session_info,
227 credentials=credentials, lp=lp)
228 if len(ldb.search("(cn=Administrator)")) != 1:
229 raise ProvisioningError("No administrator account found")
232 def findnss(nssfn, names):
233 """Find a user or group from a list of possibilities.
235 :param nssfn: NSS Function to try (should raise KeyError if not found)
236 :param names: Names to check.
237 :return: Value return by first names list.
244 raise KeyError("Unable to find user/group %r" % names)
247 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
248 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
251 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
252 """Setup a ldb in the private dir.
254 :param ldb: LDB file to import data into
255 :param ldif_path: Path of the LDIF file to load
256 :param subst_vars: Optional variables to subsitute in LDIF.
257 :param nocontrols: Optional list of controls, can be None for no controls
259 assert isinstance(ldif_path, str)
260 data = read_and_sub_file(ldif_path, subst_vars)
261 ldb.add_ldif(data,controls)
264 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
265 """Modify a ldb in the private dir.
267 :param ldb: LDB object.
268 :param ldif_path: LDIF file path.
269 :param subst_vars: Optional dictionary with substitution variables.
271 data = read_and_sub_file(ldif_path, subst_vars)
273 ldb.modify_ldif(data)
276 def setup_ldb(ldb, ldif_path, subst_vars):
277 """Import a LDIF a file into a LDB handle, optionally substituting variables.
279 :note: Either all LDIF data will be added or none (using transactions).
281 :param ldb: LDB file to import into.
282 :param ldif_path: Path to the LDIF file.
283 :param subst_vars: Dictionary with substitution variables.
285 assert ldb is not None
286 ldb.transaction_start()
288 setup_add_ldif(ldb, ldif_path, subst_vars)
290 ldb.transaction_cancel()
292 ldb.transaction_commit()
295 def provision_paths_from_lp(lp, dnsdomain):
296 """Set the default paths for provisioning.
298 :param lp: Loadparm context.
299 :param dnsdomain: DNS Domain name
301 paths = ProvisionPaths()
302 paths.private_dir = lp.get("private dir")
303 paths.dns_keytab = "dns.keytab"
305 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
306 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
307 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
308 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
309 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
310 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
311 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
312 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
313 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
314 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
315 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
316 paths.phpldapadminconfig = os.path.join(paths.private_dir,
317 "phpldapadmin-config.php")
318 paths.ldapdir = os.path.join(paths.private_dir,
320 paths.slapdconf = os.path.join(paths.ldapdir,
322 paths.slapdpid = os.path.join(paths.ldapdir,
324 paths.modulesconf = os.path.join(paths.ldapdir,
326 paths.memberofconf = os.path.join(paths.ldapdir,
328 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
329 "mmr_serverids.conf")
330 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
332 paths.olcdir = os.path.join(paths.ldapdir,
334 paths.olcseedldif = os.path.join(paths.ldapdir,
336 paths.hklm = "hklm.ldb"
337 paths.hkcr = "hkcr.ldb"
338 paths.hkcu = "hkcu.ldb"
339 paths.hku = "hku.ldb"
340 paths.hkpd = "hkpd.ldb"
341 paths.hkpt = "hkpt.ldb"
343 paths.sysvol = lp.get("path", "sysvol")
345 paths.netlogon = lp.get("path", "netlogon")
347 paths.smbconf = lp.configfile
352 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
353 serverrole=None, rootdn=None, domaindn=None, configdn=None,
354 schemadn=None, serverdn=None, sitename=None):
355 """Guess configuration settings to use."""
358 hostname = socket.gethostname().split(".")[0]
360 netbiosname = lp.get("netbios name")
361 if netbiosname is None:
362 netbiosname = hostname
363 assert netbiosname is not None
364 netbiosname = netbiosname.upper()
365 if not valid_netbios_name(netbiosname):
366 raise InvalidNetbiosName(netbiosname)
368 if dnsdomain is None:
369 dnsdomain = lp.get("realm")
370 assert dnsdomain is not None
371 dnsdomain = dnsdomain.lower()
373 if serverrole is None:
374 serverrole = lp.get("server role")
375 assert serverrole is not None
376 serverrole = serverrole.lower()
378 realm = dnsdomain.upper()
380 if lp.get("realm").upper() != realm:
381 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
383 if serverrole == "domain controller":
385 domain = lp.get("workgroup")
386 assert domain is not None
387 domain = domain.upper()
389 if lp.get("workgroup").upper() != domain:
390 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
393 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
397 domaindn = "DC=" + netbiosname
399 if not valid_netbios_name(domain):
400 raise InvalidNetbiosName(domain)
402 if hostname.upper() == realm:
403 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
404 if netbiosname == realm:
405 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
407 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
413 configdn = "CN=Configuration," + rootdn
415 schemadn = "CN=Schema," + configdn
420 names = ProvisionNames()
421 names.rootdn = rootdn
422 names.domaindn = domaindn
423 names.configdn = configdn
424 names.schemadn = schemadn
425 names.ldapmanagerdn = "CN=Manager," + rootdn
426 names.dnsdomain = dnsdomain
427 names.domain = domain
429 names.netbiosname = netbiosname
430 names.hostname = hostname
431 names.sitename = sitename
432 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
437 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
438 targetdir, sid_generator):
439 """Create a new smb.conf file based on a couple of basic settings.
441 assert smbconf is not None
443 hostname = socket.gethostname().split(".")[0]
444 netbiosname = hostname.upper()
446 if serverrole is None:
447 serverrole = "standalone"
449 assert serverrole in ("domain controller", "member server", "standalone")
450 if serverrole == "domain controller":
452 elif serverrole == "member server":
453 smbconfsuffix = "member"
454 elif serverrole == "standalone":
455 smbconfsuffix = "standalone"
457 if sid_generator is None:
458 sid_generator = "internal"
460 assert domain is not None
461 domain = domain.upper()
463 assert realm is not None
464 realm = realm.upper()
466 default_lp = param.LoadParm()
467 #Load non-existant file
468 if os.path.exists(smbconf):
469 default_lp.load(smbconf)
471 if targetdir is not None:
472 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
473 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
475 default_lp.set("lock dir", os.path.abspath(targetdir))
480 if sid_generator == "internal":
481 sid_generator_line = ""
483 sid_generator_line = "sid generator = " + sid_generator
485 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
486 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
488 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
490 "NETBIOS_NAME": netbiosname,
493 "SERVERROLE": serverrole,
494 "NETLOGONPATH": netlogon,
495 "SYSVOLPATH": sysvol,
496 "SIDGENERATOR_LINE": sid_generator_line,
497 "PRIVATEDIR_LINE": privatedir_line,
498 "LOCKDIR_LINE": lockdir_line
502 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
503 users_gid, wheel_gid):
504 """setup reasonable name mappings for sam names to unix names.
506 :param samdb: SamDB object.
507 :param idmap: IDmap db object.
508 :param sid: The domain sid.
509 :param domaindn: The domain DN.
510 :param root_uid: uid of the UNIX root user.
511 :param nobody_uid: uid of the UNIX nobody user.
512 :param users_gid: gid of the UNIX users group.
513 :param wheel_gid: gid of the UNIX wheel group."""
515 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
516 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
518 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
519 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
521 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
522 provision_backend, names, schema,
525 """Setup the partitions for the SAM database.
527 Alternatively, provision() may call this, and then populate the database.
529 :note: This will wipe the Sam Database!
531 :note: This function always removes the local SAM LDB file. The erase
532 parameter controls whether to erase the existing data, which
533 may not be stored locally but in LDAP.
536 assert session_info is not None
538 old_partitions = None
539 new_partitions = None
541 # We use options=["modules:"] to stop the modules loading - we
542 # just want to wipe and re-initialise the database, not start it up
545 os.unlink(samdb_path)
549 samdb = Ldb(url=samdb_path, session_info=session_info,
550 lp=lp, options=["modules:"])
552 ldap_backend_line = "# No LDAP backend"
553 if provision_backend.type is not "ldb":
554 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
556 samdb.transaction_start()
558 message("Setting up sam.ldb partitions and settings")
559 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
560 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
561 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
562 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
563 "LDAP_BACKEND_LINE": ldap_backend_line,
567 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
568 "BACKEND_TYPE": provision_backend.type,
569 "SERVER_ROLE": serverrole
572 message("Setting up sam.ldb rootDSE")
573 setup_samdb_rootdse(samdb, setup_path, names)
576 samdb.transaction_cancel()
579 samdb.transaction_commit()
582 def secretsdb_self_join(secretsdb, domain,
583 netbiosname, domainsid, machinepass,
584 realm=None, dnsdomain=None,
586 key_version_number=1,
587 secure_channel_type=SEC_CHAN_WKSTA):
588 """Add domain join-specific bits to a secrets database.
590 :param secretsdb: Ldb Handle to the secrets database
591 :param machinepass: Machine password
593 attrs=["whenChanged",
601 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
602 msg["secureChannelType"] = str(secure_channel_type)
603 msg["flatname"] = [domain]
604 msg["objectClass"] = ["top", "primaryDomain"]
605 if realm is not None:
606 if dnsdomain is None:
607 dnsdomain = realm.lower()
608 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
610 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
611 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
612 msg["privateKeytab"] = ["secrets.keytab"];
615 msg["secret"] = [machinepass]
616 msg["samAccountName"] = ["%s$" % netbiosname]
617 msg["secureChannelType"] = [str(secure_channel_type)]
618 msg["objectSid"] = [ndr_pack(domainsid)]
620 res = secretsdb.search(base="cn=Primary Domains",
622 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
623 scope=SCOPE_ONELEVEL)
626 if del_msg.dn is not msg.dn:
627 secretsdb.delete(del_msg.dn)
629 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
632 msg["priorSecret"] = res[0]["secret"]
633 msg["priorWhenChanged"] = res[0]["whenChanged"]
635 if res["privateKeytab"] is not None:
636 msg["privateKeytab"] = res[0]["privateKeytab"]
638 if res["krb5Keytab"] is not None:
639 msg["krb5Keytab"] = res[0]["krb5Keytab"]
642 el.set_flags(ldb.FLAG_MOD_REPLACE)
643 secretsdb.modify(msg)
648 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
649 dns_keytab_path, dnspass):
650 """Add DNS specific bits to a secrets database.
652 :param secretsdb: Ldb Handle to the secrets database
653 :param setup_path: Setup path function
654 :param machinepass: Machine password
656 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
658 "DNSDOMAIN": dnsdomain,
659 "DNS_KEYTAB": dns_keytab_path,
660 "DNSPASS_B64": b64encode(dnspass),
664 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
665 """Setup the secrets database.
667 :param path: Path to the secrets database.
668 :param setup_path: Get the path to a setup file.
669 :param session_info: Session info.
670 :param credentials: Credentials
671 :param lp: Loadparm context
672 :return: LDB handle for the created secrets database
674 if os.path.exists(path):
676 secrets_ldb = Ldb(path, session_info=session_info,
679 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
680 secrets_ldb = Ldb(path, session_info=session_info,
682 secrets_ldb.transaction_start()
683 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
685 if backend_credentials is not None and backend_credentials.authentication_requested():
686 if backend_credentials.get_bind_dn() is not None:
687 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
688 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
689 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
692 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
693 "LDAPADMINUSER": backend_credentials.get_username(),
694 "LDAPADMINREALM": backend_credentials.get_realm(),
695 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
700 def setup_privileges(path, setup_path, session_info, lp):
701 """Setup the privileges database.
703 :param path: Path to the privileges database.
704 :param setup_path: Get the path to a setup file.
705 :param session_info: Session info.
706 :param credentials: Credentials
707 :param lp: Loadparm context
708 :return: LDB handle for the created secrets database
710 if os.path.exists(path):
712 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
713 privilege_ldb.erase()
714 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
717 def setup_registry(path, setup_path, session_info, lp):
718 """Setup the registry.
720 :param path: Path to the registry database
721 :param setup_path: Function that returns the path to a setup.
722 :param session_info: Session information
723 :param credentials: Credentials
724 :param lp: Loadparm context
726 reg = registry.Registry()
727 hive = registry.open_ldb(path, session_info=session_info,
729 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
730 provision_reg = setup_path("provision.reg")
731 assert os.path.exists(provision_reg)
732 reg.diff_apply(provision_reg)
735 def setup_idmapdb(path, setup_path, session_info, lp):
736 """Setup the idmap database.
738 :param path: path to the idmap database
739 :param setup_path: Function that returns a path to a setup file
740 :param session_info: Session information
741 :param credentials: Credentials
742 :param lp: Loadparm context
744 if os.path.exists(path):
747 idmap_ldb = IDmapDB(path, session_info=session_info,
751 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
755 def setup_samdb_rootdse(samdb, setup_path, names):
756 """Setup the SamDB rootdse.
758 :param samdb: Sam Database handle
759 :param setup_path: Obtain setup path
761 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
762 "SCHEMADN": names.schemadn,
763 "NETBIOSNAME": names.netbiosname,
764 "DNSDOMAIN": names.dnsdomain,
765 "REALM": names.realm,
766 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
767 "DOMAINDN": names.domaindn,
768 "ROOTDN": names.rootdn,
769 "CONFIGDN": names.configdn,
770 "SERVERDN": names.serverdn,
774 def setup_self_join(samdb, names,
775 machinepass, dnspass,
776 domainsid, invocationid, setup_path,
777 policyguid, policyguid_dc, domainControllerFunctionality,
779 """Join a host to its own domain."""
780 assert isinstance(invocationid, str)
781 if ntdsguid is not None:
782 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
785 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
786 "CONFIGDN": names.configdn,
787 "SCHEMADN": names.schemadn,
788 "DOMAINDN": names.domaindn,
789 "SERVERDN": names.serverdn,
790 "INVOCATIONID": invocationid,
791 "NETBIOSNAME": names.netbiosname,
792 "DEFAULTSITE": names.sitename,
793 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
794 "MACHINEPASS_B64": b64encode(machinepass),
795 "DNSPASS_B64": b64encode(dnspass),
796 "REALM": names.realm,
797 "DOMAIN": names.domain,
798 "DNSDOMAIN": names.dnsdomain,
799 "SAMBA_VERSION_STRING": version,
800 "NTDSGUID": ntdsguid_line,
801 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
803 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
804 "POLICYGUID": policyguid,
805 "POLICYGUID_DC": policyguid_dc,
806 "DNSDOMAIN": names.dnsdomain,
807 "DOMAINSID": str(domainsid),
808 "DOMAINDN": names.domaindn})
810 # add the NTDSGUID based SPNs
811 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
812 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
813 expression="", scope=SCOPE_BASE)
814 assert isinstance(names.ntdsguid, str)
816 # Setup fSMORoleOwner entries to point at the newly created DC entry
817 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
818 "DOMAIN": names.domain,
819 "DNSDOMAIN": names.dnsdomain,
820 "DOMAINDN": names.domaindn,
821 "CONFIGDN": names.configdn,
822 "SCHEMADN": names.schemadn,
823 "DEFAULTSITE": names.sitename,
824 "SERVERDN": names.serverdn,
825 "NETBIOSNAME": names.netbiosname,
826 "NTDSGUID": names.ntdsguid
830 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
832 domainsid, domainguid, policyguid, policyguid_dc,
833 fill, adminpass, krbtgtpass,
834 machinepass, invocationid, dnspass, ntdsguid,
835 serverrole, dom_for_fun_level=None,
837 """Setup a complete SAM Database.
839 :note: This will wipe the main SAM database file!
842 # ATTENTION: Do NOT change these default values without discussion with the
843 # team and/or release manager. They have a big impact on the whole program!
844 domainControllerFunctionality = DS_DC_FUNCTION_2008
846 if dom_for_fun_level is None:
847 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
848 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
849 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
851 if dom_for_fun_level > domainControllerFunctionality:
852 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!")
854 domainFunctionality = dom_for_fun_level
855 forestFunctionality = dom_for_fun_level
857 # Also wipes the database
858 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
859 provision_backend=provision_backend, session_info=session_info,
861 serverrole=serverrole, schema=schema)
864 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
866 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
867 samdb = Ldb(session_info=session_info,
868 credentials=provision_backend.credentials, lp=lp)
870 message("Pre-loading the Samba 4 and AD schema")
872 # Load the schema from the one we computed earlier
873 samdb.set_schema_from_ldb(schema.ldb)
875 # And now we can connect to the DB - the schema won't be loaded from the DB
881 samdb.transaction_start()
883 # Set the domain functionality levels onto the database.
884 # Various module (the password_hash module in particular) need
885 # to know what level of AD we are emulating.
887 # These will be fixed into the database via the database
888 # modifictions below, but we need them set from the start.
889 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
890 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
891 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
893 samdb.set_domain_sid(str(domainsid))
894 if serverrole == "domain controller":
895 samdb.set_invocation_id(invocationid)
897 message("Adding DomainDN: %s" % names.domaindn)
899 #impersonate domain admin
900 admin_session_info = admin_session(lp, str(domainsid))
901 samdb.set_session_info(admin_session_info)
902 if domainguid is not None:
903 domainguid_line = "objectGUID: %s\n-" % domainguid
907 descr = get_domain_descriptor(domainsid)
908 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
909 "DOMAINDN": names.domaindn,
910 "DOMAINGUID": domainguid_line,
915 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
916 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
917 "DOMAINSID": str(domainsid),
918 "SCHEMADN": names.schemadn,
919 "NETBIOSNAME": names.netbiosname,
920 "DEFAULTSITE": names.sitename,
921 "CONFIGDN": names.configdn,
922 "SERVERDN": names.serverdn,
923 "POLICYGUID": policyguid,
924 "DOMAINDN": names.domaindn,
925 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
926 "SAMBA_VERSION_STRING": version
929 message("Adding configuration container")
930 descr = get_config_descriptor(domainsid);
931 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
932 "CONFIGDN": names.configdn,
936 # The LDIF here was created when the Schema object was constructed
937 message("Setting up sam.ldb schema")
938 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
939 samdb.modify_ldif(schema.schema_dn_modify)
940 samdb.write_prefixes_from_schema()
941 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
942 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
943 {"SCHEMADN": names.schemadn})
945 message("Setting up sam.ldb configuration data")
946 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
947 "CONFIGDN": names.configdn,
948 "NETBIOSNAME": names.netbiosname,
949 "DEFAULTSITE": names.sitename,
950 "DNSDOMAIN": names.dnsdomain,
951 "DOMAIN": names.domain,
952 "SCHEMADN": names.schemadn,
953 "DOMAINDN": names.domaindn,
954 "SERVERDN": names.serverdn,
955 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
958 message("Setting up display specifiers")
959 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
960 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
961 check_all_substituted(display_specifiers_ldif)
962 samdb.add_ldif(display_specifiers_ldif)
964 message("Adding users container")
965 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
966 "DOMAINDN": names.domaindn})
967 message("Modifying users container")
968 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
969 "DOMAINDN": names.domaindn})
970 message("Adding computers container")
971 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
972 "DOMAINDN": names.domaindn})
973 message("Modifying computers container")
974 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
975 "DOMAINDN": names.domaindn})
976 message("Setting up sam.ldb data")
977 setup_add_ldif(samdb, setup_path("provision.ldif"), {
978 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
979 "DOMAINDN": names.domaindn,
980 "NETBIOSNAME": names.netbiosname,
981 "DEFAULTSITE": names.sitename,
982 "CONFIGDN": names.configdn,
983 "SERVERDN": names.serverdn,
984 "POLICYGUID_DC": policyguid_dc
987 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
988 "DOMAINDN": names.domaindn})
990 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
991 "CONFIGDN": names.configdn,
992 "SCHEMADN": names.schemadn})
993 if fill == FILL_FULL:
994 message("Setting up sam.ldb users and groups")
995 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
996 "DOMAINDN": names.domaindn,
997 "DOMAINSID": str(domainsid),
998 "CONFIGDN": names.configdn,
999 "ADMINPASS_B64": b64encode(adminpass),
1000 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1003 if serverrole == "domain controller":
1004 message("Setting up self join")
1005 setup_self_join(samdb, names=names, invocationid=invocationid,
1007 machinepass=machinepass,
1008 domainsid=domainsid, policyguid=policyguid,
1009 policyguid_dc=policyguid_dc,
1010 setup_path=setup_path,
1011 domainControllerFunctionality=domainControllerFunctionality,
1014 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1015 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1016 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1017 assert isinstance(names.ntdsguid, str)
1020 samdb.transaction_cancel()
1023 samdb.transaction_commit()
1028 FILL_NT4SYNC = "NT4SYNC"
1032 def provision(setup_dir, message, session_info,
1033 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1035 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1037 domain=None, hostname=None, hostip=None, hostip6=None,
1038 domainsid=None, adminpass=None, ldapadminpass=None,
1039 krbtgtpass=None, domainguid=None,
1040 policyguid=None, policyguid_dc=None, invocationid=None,
1041 machinepass=None, ntdsguid=None,
1042 dnspass=None, root=None, nobody=None, users=None,
1043 wheel=None, backup=None, aci=None, serverrole=None,
1044 dom_for_fun_level=None,
1045 ldap_backend_extra_port=None, backend_type=None,
1047 ol_mmr_urls=None, ol_olc=None,
1048 setup_ds_path=None, slapd_path=None, nosync=False,
1049 ldap_dryrun_mode=False):
1052 :note: caution, this wipes all existing data!
1055 def setup_path(file):
1056 return os.path.join(setup_dir, file)
1058 if domainsid is None:
1059 domainsid = security.random_sid()
1061 domainsid = security.dom_sid(domainsid)
1063 # create/adapt the group policy GUIDs
1064 if policyguid is None:
1065 policyguid = str(uuid.uuid4())
1066 policyguid = policyguid.upper()
1067 if policyguid_dc is None:
1068 policyguid_dc = str(uuid.uuid4())
1069 policyguid_dc = policyguid_dc.upper()
1071 if adminpass is None:
1072 adminpass = glue.generate_random_str(12)
1073 if krbtgtpass is None:
1074 krbtgtpass = glue.generate_random_str(12)
1075 if machinepass is None:
1076 machinepass = glue.generate_random_str(12)
1078 dnspass = glue.generate_random_str(12)
1079 if ldapadminpass is None:
1080 #Make a new, random password between Samba and it's LDAP server
1081 ldapadminpass=glue.generate_random_str(12)
1083 if backend_type is None:
1084 backend_type = "ldb"
1086 sid_generator = "internal"
1087 if backend_type == "fedora-ds":
1088 sid_generator = "backend"
1090 root_uid = findnss_uid([root or "root"])
1091 nobody_uid = findnss_uid([nobody or "nobody"])
1092 users_gid = findnss_gid([users or "users"])
1094 wheel_gid = findnss_gid(["wheel", "adm"])
1096 wheel_gid = findnss_gid([wheel])
1098 if targetdir is not None:
1099 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1100 os.makedirs(os.path.join(targetdir, "etc"))
1101 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1102 elif smbconf is None:
1103 smbconf = param.default_path()
1105 # only install a new smb.conf if there isn't one there already
1106 if not os.path.exists(smbconf):
1107 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1108 targetdir, sid_generator)
1110 lp = param.LoadParm()
1113 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1114 dnsdomain=realm, serverrole=serverrole,
1115 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1116 serverdn=serverdn, sitename=sitename)
1118 paths = provision_paths_from_lp(lp, names.dnsdomain)
1122 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1123 except socket.gaierror, (socket.EAI_NODATA, msg):
1128 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1129 except socket.gaierror, (socket.EAI_NODATA, msg):
1132 if serverrole is None:
1133 serverrole = lp.get("server role")
1135 assert serverrole in ("domain controller", "member server", "standalone")
1136 if invocationid is None and serverrole == "domain controller":
1137 invocationid = str(uuid.uuid4())
1139 if not os.path.exists(paths.private_dir):
1140 os.mkdir(paths.private_dir)
1142 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1144 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1146 if backend_type == "ldb":
1147 provision_backend = LDBBackend(backend_type,
1148 paths=paths, setup_path=setup_path,
1149 lp=lp, credentials=credentials,
1152 elif backend_type == "existing":
1153 provision_backend = ExistingBackend(backend_type,
1154 paths=paths, setup_path=setup_path,
1155 lp=lp, credentials=credentials,
1158 elif backend_type == "fedora-ds":
1159 provision_backend = FDSBackend(backend_type,
1160 paths=paths, setup_path=setup_path,
1161 lp=lp, credentials=credentials,
1164 domainsid=domainsid,
1167 ldapadminpass=ldapadminpass,
1168 slapd_path=slapd_path,
1169 ldap_backend_extra_port=ldap_backend_extra_port,
1170 ldap_dryrun_mode=ldap_dryrun_mode,
1172 setup_ds_path=setup_ds_path)
1173 elif backend_type == "openldap":
1174 provision_backend = OpenLDAPBackend(backend_type,
1175 paths=paths, setup_path=setup_path,
1176 lp=lp, credentials=credentials,
1179 domainsid=domainsid,
1182 ldapadminpass=ldapadminpass,
1183 slapd_path=slapd_path,
1184 ldap_backend_extra_port=ldap_backend_extra_port,
1185 ldap_dryrun_mode=ldap_dryrun_mode,
1186 ol_mmr_urls=ol_mmr_urls,
1189 raise ProvisioningError("Unknown LDAP backend type selected")
1191 provision_backend.init()
1192 provision_backend.start()
1194 # only install a new shares config db if there is none
1195 if not os.path.exists(paths.shareconf):
1196 message("Setting up share.ldb")
1197 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1199 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1202 message("Setting up secrets.ldb")
1203 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1204 session_info=session_info,
1205 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1207 message("Setting up the registry")
1208 setup_registry(paths.hklm, setup_path, session_info,
1211 message("Setting up the privileges database")
1212 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1214 message("Setting up idmap db")
1215 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1218 message("Setting up SAM db")
1219 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1220 provision_backend, lp, names,
1222 domainsid=domainsid,
1223 schema=schema, domainguid=domainguid,
1224 policyguid=policyguid, policyguid_dc=policyguid_dc,
1226 adminpass=adminpass, krbtgtpass=krbtgtpass,
1227 invocationid=invocationid,
1228 machinepass=machinepass, dnspass=dnspass,
1229 ntdsguid=ntdsguid, serverrole=serverrole,
1230 dom_for_fun_level=dom_for_fun_level)
1232 if serverrole == "domain controller":
1233 if paths.netlogon is None:
1234 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1235 message("Please either remove %s or see the template at %s" %
1236 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1237 assert(paths.netlogon is not None)
1239 if paths.sysvol is None:
1240 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1241 message("Please either remove %s or see the template at %s" %
1242 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1243 assert(paths.sysvol is not None)
1245 # Set up group policies (domain policy and domain controller policy)
1247 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1248 "{" + policyguid + "}")
1249 os.makedirs(policy_path, 0755)
1250 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1251 "[General]\r\nVersion=65543")
1252 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1253 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1255 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1256 "{" + policyguid_dc + "}")
1257 os.makedirs(policy_path_dc, 0755)
1258 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1259 "[General]\r\nVersion=2")
1260 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1261 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1263 if not os.path.isdir(paths.netlogon):
1264 os.makedirs(paths.netlogon, 0755)
1266 if samdb_fill == FILL_FULL:
1267 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1268 root_uid=root_uid, nobody_uid=nobody_uid,
1269 users_gid=users_gid, wheel_gid=wheel_gid)
1271 message("Setting up sam.ldb rootDSE marking as synchronized")
1272 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1274 # Only make a zone file on the first DC, it should be replicated with DNS replication
1275 if serverrole == "domain controller":
1276 secretsdb_self_join(secrets_ldb, domain=domain,
1278 dnsdomain=names.dnsdomain,
1279 netbiosname=names.netbiosname,
1280 domainsid=domainsid,
1281 machinepass=machinepass,
1282 secure_channel_type=SEC_CHAN_BDC)
1284 secretsdb_setup_dns(secrets_ldb, setup_path,
1285 realm=names.realm, dnsdomain=names.dnsdomain,
1286 dns_keytab_path=paths.dns_keytab,
1289 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1290 assert isinstance(domainguid, str)
1292 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1294 hostip6=hostip6, hostname=names.hostname,
1296 domainguid=domainguid, ntdsguid=names.ntdsguid)
1298 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1299 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1301 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1302 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1303 keytab_name=paths.dns_keytab)
1304 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1305 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1307 create_krb5_conf(paths.krb5conf, setup_path,
1308 dnsdomain=names.dnsdomain, hostname=names.hostname,
1310 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1312 provision_backend.post_setup()
1313 provision_backend.shutdown()
1315 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1318 #Now commit the secrets.ldb to disk
1319 secrets_ldb.transaction_commit()
1321 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1323 message("Once the above files are installed, your Samba4 server will be ready to use")
1324 message("Server Role: %s" % serverrole)
1325 message("Hostname: %s" % names.hostname)
1326 message("NetBIOS Domain: %s" % names.domain)
1327 message("DNS Domain: %s" % names.dnsdomain)
1328 message("DOMAIN SID: %s" % str(domainsid))
1329 if samdb_fill == FILL_FULL:
1330 message("Admin password: %s" % adminpass)
1331 if provision_backend.type is not "ldb":
1332 if provision_backend.credentials.get_bind_dn() is not None:
1333 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1335 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1337 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1339 if provision_backend.slapd_command_escaped is not None:
1340 # now display slapd_command_file.txt to show how slapd must be started next time
1341 message("Use later the following commandline to start slapd, then Samba:")
1342 message(provision_backend.slapd_command_escaped)
1343 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1346 result = ProvisionResult()
1347 result.domaindn = domaindn
1348 result.paths = paths
1350 result.samdb = samdb
1355 def provision_become_dc(setup_dir=None,
1356 smbconf=None, targetdir=None, realm=None,
1357 rootdn=None, domaindn=None, schemadn=None,
1358 configdn=None, serverdn=None,
1359 domain=None, hostname=None, domainsid=None,
1360 adminpass=None, krbtgtpass=None, domainguid=None,
1361 policyguid=None, policyguid_dc=None, invocationid=None,
1363 dnspass=None, root=None, nobody=None, users=None,
1364 wheel=None, backup=None, serverrole=None,
1365 ldap_backend=None, ldap_backend_type=None,
1366 sitename=None, debuglevel=1):
1369 """print a message if quiet is not set."""
1372 glue.set_debug_level(debuglevel)
1374 return provision(setup_dir, message, system_session(), None,
1375 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1376 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1377 configdn=configdn, serverdn=serverdn, domain=domain,
1378 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1379 machinepass=machinepass, serverrole="domain controller",
1383 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1384 """Create a PHP LDAP admin configuration file.
1386 :param path: Path to write the configuration to.
1387 :param setup_path: Function to generate setup paths.
1389 setup_file(setup_path("phpldapadmin-config.php"), path,
1390 {"S4_LDAPI_URI": ldapi_uri})
1393 def create_zone_file(path, setup_path, dnsdomain,
1394 hostip, hostip6, hostname, realm, domainguid,
1396 """Write out a DNS zone file, from the info in the current database.
1398 :param path: Path of the new zone file.
1399 :param setup_path: Setup path function.
1400 :param dnsdomain: DNS Domain name
1401 :param domaindn: DN of the Domain
1402 :param hostip: Local IPv4 IP
1403 :param hostip6: Local IPv6 IP
1404 :param hostname: Local hostname
1405 :param realm: Realm name
1406 :param domainguid: GUID of the domain.
1407 :param ntdsguid: GUID of the hosts nTDSDSA record.
1409 assert isinstance(domainguid, str)
1411 if hostip6 is not None:
1412 hostip6_base_line = " IN AAAA " + hostip6
1413 hostip6_host_line = hostname + " IN AAAA " + hostip6
1415 hostip6_base_line = ""
1416 hostip6_host_line = ""
1418 if hostip is not None:
1419 hostip_base_line = " IN A " + hostip
1420 hostip_host_line = hostname + " IN A " + hostip
1422 hostip_base_line = ""
1423 hostip_host_line = ""
1425 setup_file(setup_path("provision.zone"), path, {
1426 "HOSTNAME": hostname,
1427 "DNSDOMAIN": dnsdomain,
1429 "HOSTIP_BASE_LINE": hostip_base_line,
1430 "HOSTIP_HOST_LINE": hostip_host_line,
1431 "DOMAINGUID": domainguid,
1432 "DATESTRING": time.strftime("%Y%m%d%H"),
1433 "DEFAULTSITE": DEFAULTSITE,
1434 "NTDSGUID": ntdsguid,
1435 "HOSTIP6_BASE_LINE": hostip6_base_line,
1436 "HOSTIP6_HOST_LINE": hostip6_host_line,
1440 def create_named_conf(path, setup_path, realm, dnsdomain,
1442 """Write out a file containing zone statements suitable for inclusion in a
1443 named.conf file (including GSS-TSIG configuration).
1445 :param path: Path of the new named.conf file.
1446 :param setup_path: Setup path function.
1447 :param realm: Realm name
1448 :param dnsdomain: DNS Domain name
1449 :param private_dir: Path to private directory
1450 :param keytab_name: File name of DNS keytab file
1453 setup_file(setup_path("named.conf"), path, {
1454 "DNSDOMAIN": dnsdomain,
1456 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1457 "PRIVATE_DIR": private_dir
1460 def create_named_txt(path, setup_path, realm, dnsdomain,
1461 private_dir, keytab_name):
1462 """Write out a file containing zone statements suitable for inclusion in a
1463 named.conf file (including GSS-TSIG configuration).
1465 :param path: Path of the new named.conf file.
1466 :param setup_path: Setup path function.
1467 :param realm: Realm name
1468 :param dnsdomain: DNS Domain name
1469 :param private_dir: Path to private directory
1470 :param keytab_name: File name of DNS keytab file
1473 setup_file(setup_path("named.txt"), path, {
1474 "DNSDOMAIN": dnsdomain,
1476 "DNS_KEYTAB": keytab_name,
1477 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1478 "PRIVATE_DIR": private_dir
1481 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1482 """Write out a file containing zone statements suitable for inclusion in a
1483 named.conf file (including GSS-TSIG configuration).
1485 :param path: Path of the new named.conf file.
1486 :param setup_path: Setup path function.
1487 :param dnsdomain: DNS Domain name
1488 :param hostname: Local hostname
1489 :param realm: Realm name
1492 setup_file(setup_path("krb5.conf"), path, {
1493 "DNSDOMAIN": dnsdomain,
1494 "HOSTNAME": hostname,