1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba4 server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__ = "restructuredText"
29 from base64 import b64encode
44 from samba.auth import system_session, admin_session
46 from samba.samba3 import smbd, passdb
47 from samba.samba3 import param as s3param
48 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
52 check_all_substituted,
53 is_valid_netbios_char,
59 from samba.dcerpc import security, misc
60 from samba.dcerpc.misc import (
64 from samba.dsdb import (
65 DS_DOMAIN_FUNCTION_2003,
66 DS_DOMAIN_FUNCTION_2008_R2,
69 from samba.idmap import IDmapDB
70 from samba.ms_display_specifiers import read_ms_ldif
71 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
72 from samba.ndr import ndr_pack, ndr_unpack
73 from samba.provision.backend import (
79 from samba.provision.descriptor import (
80 get_config_descriptor,
83 from samba.provision.common import (
88 from samba.provision.sambadns import (
90 create_dns_update_list
95 from samba.schema import Schema
96 from samba.samdb import SamDB
97 from samba.dbchecker import dbcheck
100 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
101 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
102 DEFAULTSITE = "Default-First-Site-Name"
103 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
106 class ProvisionPaths(object):
109 self.shareconf = None
120 self.dns_keytab = None
123 self.private_dir = None
124 self.state_dir = None
125 self.phpldapadminconfig = None
128 class ProvisionNames(object):
135 self.ldapmanagerdn = None
136 self.dnsdomain = None
138 self.netbiosname = None
145 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
147 """Get key provision parameters (realm, domain, ...) from a given provision
149 :param samdb: An LDB object connected to the sam.ldb file
150 :param secretsdb: An LDB object connected to the secrets.ldb file
151 :param idmapdb: An LDB object connected to the idmap.ldb file
152 :param paths: A list of path to provision object
153 :param smbconf: Path to the smb.conf file
154 :param lp: A LoadParm object
155 :return: A list of key provision parameters
157 names = ProvisionNames()
158 names.adminpass = None
160 # NT domain, kerberos realm, root dn, domain dn, domain dns name
161 names.domain = string.upper(lp.get("workgroup"))
162 names.realm = lp.get("realm")
163 names.dnsdomain = names.realm.lower()
164 basedn = samba.dn_from_dns_name(names.dnsdomain)
165 names.realm = string.upper(names.realm)
167 # Get the netbiosname first (could be obtained from smb.conf in theory)
168 res = secretsdb.search(expression="(flatname=%s)" %
169 names.domain,base="CN=Primary Domains",
170 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
171 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
173 names.smbconf = smbconf
175 # That's a bit simplistic but it's ok as long as we have only 3
177 current = samdb.search(expression="(objectClass=*)",
178 base="", scope=ldb.SCOPE_BASE,
179 attrs=["defaultNamingContext", "schemaNamingContext",
180 "configurationNamingContext","rootDomainNamingContext"])
182 names.configdn = current[0]["configurationNamingContext"]
183 configdn = str(names.configdn)
184 names.schemadn = current[0]["schemaNamingContext"]
185 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
186 current[0]["defaultNamingContext"][0]))):
187 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
188 "is not the same ..." % (paths.samdb,
189 str(current[0]["defaultNamingContext"][0]),
190 paths.smbconf, basedn)))
192 names.domaindn=current[0]["defaultNamingContext"]
193 names.rootdn=current[0]["rootDomainNamingContext"]
195 res3 = samdb.search(expression="(objectClass=site)",
196 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
197 names.sitename = str(res3[0]["cn"])
199 # dns hostname and server dn
200 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
201 base="OU=Domain Controllers,%s" % basedn,
202 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
203 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
205 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
206 attrs=[], base=configdn)
207 names.serverdn = server_res[0].dn
209 # invocation id/objectguid
210 res5 = samdb.search(expression="(objectClass=*)",
211 base="CN=NTDS Settings,%s" % str(names.serverdn),
212 scope=ldb.SCOPE_BASE,
213 attrs=["invocationID", "objectGUID"])
214 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
215 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
218 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
219 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
220 "objectSid","msDS-Behavior-Version" ])
221 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
222 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
223 if res6[0].get("msDS-Behavior-Version") is None or \
224 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
225 names.domainlevel = DS_DOMAIN_FUNCTION_2000
227 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
230 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
231 base="CN=Policies,CN=System," + basedn,
232 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
233 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
235 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
237 base="CN=Policies,CN=System," + basedn,
238 scope=ldb.SCOPE_ONELEVEL,
239 attrs=["cn","displayName"])
241 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
243 names.policyid_dc = None
244 res9 = idmapdb.search(expression="(cn=%s)" %
245 (security.SID_BUILTIN_ADMINISTRATORS),
248 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
249 names.wheel_gid = res9[0]["xidNumber"]
253 def update_provision_usn(samdb, low, high, id, replace=False):
254 """Update the field provisionUSN in sam.ldb
256 This field is used to track range of USN modified by provision and
258 This value is used afterward by next provision to figure out if
259 the field have been modified since last provision.
261 :param samdb: An LDB object connect to sam.ldb
262 :param low: The lowest USN modified by this upgrade
263 :param high: The highest USN modified by this upgrade
264 :param id: The invocation id of the samba's dc
265 :param replace: A boolean indicating if the range should replace any
266 existing one or appended (default)
271 entry = samdb.search(base="@PROVISION",
272 scope=ldb.SCOPE_BASE,
273 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
274 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
275 if not re.search(';', e):
276 e = "%s;%s" % (e, id)
279 tab.append("%s-%s;%s" % (low, high, id))
280 delta = ldb.Message()
281 delta.dn = ldb.Dn(samdb, "@PROVISION")
282 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
283 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
284 entry = samdb.search(expression='provisionnerID=*',
285 base="@PROVISION", scope=ldb.SCOPE_BASE,
286 attrs=["provisionnerID"])
287 if len(entry) == 0 or len(entry[0]) == 0:
288 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
292 def set_provision_usn(samdb, low, high, id):
293 """Set the field provisionUSN in sam.ldb
294 This field is used to track range of USN modified by provision and
296 This value is used afterward by next provision to figure out if
297 the field have been modified since last provision.
299 :param samdb: An LDB object connect to sam.ldb
300 :param low: The lowest USN modified by this upgrade
301 :param high: The highest USN modified by this upgrade
302 :param id: The invocationId of the provision"""
305 tab.append("%s-%s;%s" % (low, high, id))
307 delta = ldb.Message()
308 delta.dn = ldb.Dn(samdb, "@PROVISION")
309 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
310 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
314 def get_max_usn(samdb,basedn):
315 """ This function return the biggest USN present in the provision
317 :param samdb: A LDB object pointing to the sam.ldb
318 :param basedn: A string containing the base DN of the provision
320 :return: The biggest USN in the provision"""
322 res = samdb.search(expression="objectClass=*",base=basedn,
323 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
324 controls=["search_options:1:2",
325 "server_sort:1:1:uSNChanged",
326 "paged_results:1:1"])
327 return res[0]["uSNChanged"]
330 def get_last_provision_usn(sam):
331 """Get USNs ranges modified by a provision or an upgradeprovision
333 :param sam: An LDB object pointing to the sam.ldb
334 :return: a dictionary which keys are invocation id and values are an array
335 of integer representing the different ranges
338 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
339 base="@PROVISION", scope=ldb.SCOPE_BASE,
340 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
341 except ldb.LdbError, (ecode, emsg):
342 if ecode == ldb.ERR_NO_SUCH_OBJECT:
349 if entry[0].get("provisionnerID"):
350 for e in entry[0]["provisionnerID"]:
352 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
353 tab1 = str(r).split(';')
358 if (len(myids) > 0 and id not in myids):
360 tab2 = p.split(tab1[0])
361 if range.get(id) is None:
363 range[id].append(tab2[0])
364 range[id].append(tab2[1])
370 class ProvisionResult(object):
371 """Result of a provision.
373 :ivar server_role: The server role
374 :ivar paths: ProvisionPaths instance
375 :ivar domaindn: The domain dn, as string
379 self.server_role = None
386 self.domainsid = None
387 self.adminpass_generated = None
388 self.adminpass = None
389 self.backend_result = None
391 def report_logger(self, logger):
392 """Report this provision result to a logger."""
394 "Once the above files are installed, your Samba4 server will "
396 if self.adminpass_generated:
397 logger.info("Admin password: %s", self.adminpass)
398 logger.info("Server Role: %s", self.server_role)
399 logger.info("Hostname: %s", self.names.hostname)
400 logger.info("NetBIOS Domain: %s", self.names.domain)
401 logger.info("DNS Domain: %s", self.names.dnsdomain)
402 logger.info("DOMAIN SID: %s", self.domainsid)
404 if self.paths.phpldapadminconfig is not None:
406 "A phpLDAPadmin configuration file suitable for administering "
407 "the Samba 4 LDAP server has been created in %s.",
408 self.paths.phpldapadminconfig)
410 if self.backend_result:
411 self.backend_result.report_logger(logger)
414 def check_install(lp, session_info, credentials):
415 """Check whether the current install seems ok.
417 :param lp: Loadparm context
418 :param session_info: Session information
419 :param credentials: Credentials
421 if lp.get("realm") == "":
422 raise Exception("Realm empty")
423 samdb = Ldb(lp.samdb_url(), session_info=session_info,
424 credentials=credentials, lp=lp)
425 if len(samdb.search("(cn=Administrator)")) != 1:
426 raise ProvisioningError("No administrator account found")
429 def findnss(nssfn, names):
430 """Find a user or group from a list of possibilities.
432 :param nssfn: NSS Function to try (should raise KeyError if not found)
433 :param names: Names to check.
434 :return: Value return by first names list.
441 raise KeyError("Unable to find user/group in %r" % names)
444 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
445 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
448 def provision_paths_from_lp(lp, dnsdomain):
449 """Set the default paths for provisioning.
451 :param lp: Loadparm context.
452 :param dnsdomain: DNS Domain name
454 paths = ProvisionPaths()
455 paths.private_dir = lp.get("private dir")
456 paths.state_dir = lp.get("state directory")
458 # This is stored without path prefix for the "privateKeytab" attribute in
459 # "secrets_dns.ldif".
460 paths.dns_keytab = "dns.keytab"
461 paths.keytab = "secrets.keytab"
463 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
464 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
465 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
466 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
467 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
468 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
469 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
470 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
471 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
472 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
473 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
474 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
475 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
476 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
477 paths.phpldapadminconfig = os.path.join(paths.private_dir,
478 "phpldapadmin-config.php")
479 paths.hklm = "hklm.ldb"
480 paths.hkcr = "hkcr.ldb"
481 paths.hkcu = "hkcu.ldb"
482 paths.hku = "hku.ldb"
483 paths.hkpd = "hkpd.ldb"
484 paths.hkpt = "hkpt.ldb"
485 paths.sysvol = lp.get("path", "sysvol")
486 paths.netlogon = lp.get("path", "netlogon")
487 paths.smbconf = lp.configfile
491 def determine_netbios_name(hostname):
492 """Determine a netbios name from a hostname."""
493 # remove forbidden chars and force the length to be <16
494 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
495 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
498 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
499 serverrole=None, rootdn=None, domaindn=None, configdn=None,
500 schemadn=None, serverdn=None, sitename=None):
501 """Guess configuration settings to use."""
504 hostname = socket.gethostname().split(".")[0]
506 netbiosname = lp.get("netbios name")
507 if netbiosname is None:
508 netbiosname = determine_netbios_name(hostname)
509 netbiosname = netbiosname.upper()
510 if not valid_netbios_name(netbiosname):
511 raise InvalidNetbiosName(netbiosname)
513 if dnsdomain is None:
514 dnsdomain = lp.get("realm")
515 if dnsdomain is None or dnsdomain == "":
516 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
518 dnsdomain = dnsdomain.lower()
520 if serverrole is None:
521 serverrole = lp.get("server role")
522 if serverrole is None:
523 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
525 serverrole = serverrole.lower()
527 realm = dnsdomain.upper()
529 if lp.get("realm") == "":
530 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
532 if lp.get("realm").upper() != realm:
533 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))
535 if lp.get("server role").lower() != serverrole:
536 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"), lp.configfile, serverrole))
538 if serverrole == "active directory domain controller":
540 # This will, for better or worse, default to 'WORKGROUP'
541 domain = lp.get("workgroup")
542 domain = domain.upper()
544 if lp.get("workgroup").upper() != domain:
545 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
548 domaindn = samba.dn_from_dns_name(dnsdomain)
550 if domain == netbiosname:
551 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
555 domaindn = "DC=" + netbiosname
557 if not valid_netbios_name(domain):
558 raise InvalidNetbiosName(domain)
560 if hostname.upper() == realm:
561 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
562 if netbiosname.upper() == realm:
563 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
565 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
571 configdn = "CN=Configuration," + rootdn
573 schemadn = "CN=Schema," + configdn
576 sitename = DEFAULTSITE
578 names = ProvisionNames()
579 names.rootdn = rootdn
580 names.domaindn = domaindn
581 names.configdn = configdn
582 names.schemadn = schemadn
583 names.ldapmanagerdn = "CN=Manager," + rootdn
584 names.dnsdomain = dnsdomain
585 names.domain = domain
587 names.netbiosname = netbiosname
588 names.hostname = hostname
589 names.sitename = sitename
590 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
591 netbiosname, sitename, configdn)
596 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
597 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
599 """Create a new smb.conf file based on a couple of basic settings.
601 assert smbconf is not None
604 hostname = socket.gethostname().split(".")[0]
606 netbiosname = determine_netbios_name(hostname)
608 if serverrole is None:
609 serverrole = "standalone server"
611 assert domain is not None
612 domain = domain.upper()
614 assert realm is not None
615 realm = realm.upper()
618 "netbios name": netbiosname,
621 "server role": serverrole,
625 lp = samba.param.LoadParm()
626 #Load non-existent file
627 if os.path.exists(smbconf):
630 if global_param is not None:
631 for ent in global_param:
632 if global_param[ent] is not None:
633 global_settings[ent] = " ".join(global_param[ent])
635 if targetdir is not None:
636 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
637 global_settings["lock dir"] = os.path.abspath(targetdir)
638 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
639 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
641 lp.set("lock dir", os.path.abspath(targetdir))
642 lp.set("state directory", global_settings["state directory"])
643 lp.set("cache directory", global_settings["cache directory"])
646 if use_ntvfs and not lp.get("posix:eadb"):
647 if targetdir is not None:
648 privdir = os.path.join(targetdir, "private")
650 privdir = lp.get("private dir")
651 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
652 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
653 if targetdir is not None:
654 statedir = os.path.join(targetdir, "state")
656 statedir = lp.get("state directory")
657 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
660 if serverrole == "active directory domain controller":
661 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
662 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
665 global_settings["passdb backend"] = "samba_dsdb"
667 f = open(smbconf, 'w')
669 f.write("[globals]\n")
670 for key, val in global_settings.iteritems():
671 f.write("\t%s = %s\n" % (key, val))
674 for name, path in shares.iteritems():
675 f.write("[%s]\n" % name)
676 f.write("\tpath = %s\n" % path)
677 f.write("\tread only = no\n")
681 # reload the smb.conf
684 # and dump it without any values that are the default
685 # this ensures that any smb.conf parameters that were set
686 # on the provision/join command line are set in the resulting smb.conf
687 f = open(smbconf, mode='w')
694 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
695 users_gid, wheel_gid):
696 """setup reasonable name mappings for sam names to unix names.
698 :param samdb: SamDB object.
699 :param idmap: IDmap db object.
700 :param sid: The domain sid.
701 :param domaindn: The domain DN.
702 :param root_uid: uid of the UNIX root user.
703 :param nobody_uid: uid of the UNIX nobody user.
704 :param users_gid: gid of the UNIX users group.
705 :param wheel_gid: gid of the UNIX wheel group.
707 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
708 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
710 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
711 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
714 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
715 provision_backend, names, schema, serverrole,
717 """Setup the partitions for the SAM database.
719 Alternatively, provision() may call this, and then populate the database.
721 :note: This will wipe the Sam Database!
723 :note: This function always removes the local SAM LDB file. The erase
724 parameter controls whether to erase the existing data, which
725 may not be stored locally but in LDAP.
728 assert session_info is not None
730 # We use options=["modules:"] to stop the modules loading - we
731 # just want to wipe and re-initialise the database, not start it up
734 os.unlink(samdb_path)
738 samdb = Ldb(url=samdb_path, session_info=session_info,
739 lp=lp, options=["modules:"])
741 ldap_backend_line = "# No LDAP backend"
742 if provision_backend.type != "ldb":
743 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
745 samdb.transaction_start()
747 logger.info("Setting up sam.ldb partitions and settings")
748 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
749 "LDAP_BACKEND_LINE": ldap_backend_line
753 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
754 "BACKEND_TYPE": provision_backend.type,
755 "SERVER_ROLE": serverrole
758 logger.info("Setting up sam.ldb rootDSE")
759 setup_samdb_rootdse(samdb, names)
761 samdb.transaction_cancel()
764 samdb.transaction_commit()
767 def secretsdb_self_join(secretsdb, domain,
768 netbiosname, machinepass, domainsid=None,
769 realm=None, dnsdomain=None,
771 key_version_number=1,
772 secure_channel_type=SEC_CHAN_WKSTA):
773 """Add domain join-specific bits to a secrets database.
775 :param secretsdb: Ldb Handle to the secrets database
776 :param machinepass: Machine password
778 attrs = ["whenChanged",
785 if realm is not None:
786 if dnsdomain is None:
787 dnsdomain = realm.lower()
788 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
791 shortname = netbiosname.lower()
793 # We don't need to set msg["flatname"] here, because rdn_name will handle
794 # it, and it causes problems for modifies anyway
795 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
796 msg["secureChannelType"] = [str(secure_channel_type)]
797 msg["objectClass"] = ["top", "primaryDomain"]
798 if dnsname is not None:
799 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
800 msg["realm"] = [realm]
801 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
802 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
803 msg["privateKeytab"] = ["secrets.keytab"]
805 msg["secret"] = [machinepass]
806 msg["samAccountName"] = ["%s$" % netbiosname]
807 msg["secureChannelType"] = [str(secure_channel_type)]
808 if domainsid is not None:
809 msg["objectSid"] = [ndr_pack(domainsid)]
811 # This complex expression tries to ensure that we don't have more
812 # than one record for this SID, realm or netbios domain at a time,
813 # but we don't delete the old record that we are about to modify,
814 # because that would delete the keytab and previous password.
815 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
816 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
817 scope=ldb.SCOPE_ONELEVEL)
820 secretsdb.delete(del_msg.dn)
822 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
825 msg["priorSecret"] = [res[0]["secret"][0]]
826 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
829 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
834 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
840 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
841 secretsdb.modify(msg)
842 secretsdb.rename(res[0].dn, msg.dn)
844 spn = [ 'HOST/%s' % shortname ]
845 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
846 # we are a domain controller then we add servicePrincipalName
847 # entries for the keytab code to update.
848 spn.extend([ 'HOST/%s' % dnsname ])
849 msg["servicePrincipalName"] = spn
854 def setup_secretsdb(paths, session_info, backend_credentials, lp):
855 """Setup the secrets database.
857 :note: This function does not handle exceptions and transaction on purpose,
858 it's up to the caller to do this job.
860 :param path: Path to the secrets database.
861 :param session_info: Session info.
862 :param credentials: Credentials
863 :param lp: Loadparm context
864 :return: LDB handle for the created secrets database
866 if os.path.exists(paths.secrets):
867 os.unlink(paths.secrets)
869 keytab_path = os.path.join(paths.private_dir, paths.keytab)
870 if os.path.exists(keytab_path):
871 os.unlink(keytab_path)
873 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
874 if os.path.exists(dns_keytab_path):
875 os.unlink(dns_keytab_path)
879 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
881 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
882 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
883 secrets_ldb.transaction_start()
885 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
887 if (backend_credentials is not None and
888 backend_credentials.authentication_requested()):
889 if backend_credentials.get_bind_dn() is not None:
890 setup_add_ldif(secrets_ldb,
891 setup_path("secrets_simple_ldap.ldif"), {
892 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
893 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
896 setup_add_ldif(secrets_ldb,
897 setup_path("secrets_sasl_ldap.ldif"), {
898 "LDAPADMINUSER": backend_credentials.get_username(),
899 "LDAPADMINREALM": backend_credentials.get_realm(),
900 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
903 secrets_ldb.transaction_cancel()
908 def setup_privileges(path, session_info, lp):
909 """Setup the privileges database.
911 :param path: Path to the privileges database.
912 :param session_info: Session info.
913 :param credentials: Credentials
914 :param lp: Loadparm context
915 :return: LDB handle for the created secrets database
917 if os.path.exists(path):
919 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
920 privilege_ldb.erase()
921 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
924 def setup_registry(path, session_info, lp):
925 """Setup the registry.
927 :param path: Path to the registry database
928 :param session_info: Session information
929 :param credentials: Credentials
930 :param lp: Loadparm context
932 reg = samba.registry.Registry()
933 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
934 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
935 provision_reg = setup_path("provision.reg")
936 assert os.path.exists(provision_reg)
937 reg.diff_apply(provision_reg)
940 def setup_idmapdb(path, session_info, lp):
941 """Setup the idmap database.
943 :param path: path to the idmap database
944 :param session_info: Session information
945 :param credentials: Credentials
946 :param lp: Loadparm context
948 if os.path.exists(path):
951 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
953 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
957 def setup_samdb_rootdse(samdb, names):
958 """Setup the SamDB rootdse.
960 :param samdb: Sam Database handle
962 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
963 "SCHEMADN": names.schemadn,
964 "DOMAINDN": names.domaindn,
965 "ROOTDN" : names.rootdn,
966 "CONFIGDN": names.configdn,
967 "SERVERDN": names.serverdn,
971 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
972 dns_backend, dnspass, domainsid, next_rid, invocationid,
973 policyguid, policyguid_dc,
974 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
975 """Join a host to its own domain."""
976 assert isinstance(invocationid, str)
977 if ntdsguid is not None:
978 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
985 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
986 "CONFIGDN": names.configdn,
987 "SCHEMADN": names.schemadn,
988 "DOMAINDN": names.domaindn,
989 "SERVERDN": names.serverdn,
990 "INVOCATIONID": invocationid,
991 "NETBIOSNAME": names.netbiosname,
992 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
993 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
994 "DOMAINSID": str(domainsid),
995 "DCRID": str(dc_rid),
996 "SAMBA_VERSION_STRING": version,
997 "NTDSGUID": ntdsguid_line,
998 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
999 domainControllerFunctionality),
1000 "RIDALLOCATIONSTART": str(next_rid + 100),
1001 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1003 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1004 "POLICYGUID": policyguid,
1005 "POLICYGUID_DC": policyguid_dc,
1006 "DNSDOMAIN": names.dnsdomain,
1007 "DOMAINDN": names.domaindn})
1009 # If we are setting up a subdomain, then this has been replicated in, so we
1010 # don't need to add it
1011 if fill == FILL_FULL:
1012 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1013 "CONFIGDN": names.configdn,
1014 "SCHEMADN": names.schemadn,
1015 "DOMAINDN": names.domaindn,
1016 "SERVERDN": names.serverdn,
1017 "INVOCATIONID": invocationid,
1018 "NETBIOSNAME": names.netbiosname,
1019 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1020 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1021 "DOMAINSID": str(domainsid),
1022 "DCRID": str(dc_rid),
1023 "SAMBA_VERSION_STRING": version,
1024 "NTDSGUID": ntdsguid_line,
1025 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1026 domainControllerFunctionality)})
1028 # Setup fSMORoleOwner entries to point at the newly created DC entry
1029 setup_modify_ldif(samdb,
1030 setup_path("provision_self_join_modify_config.ldif"), {
1031 "CONFIGDN": names.configdn,
1032 "SCHEMADN": names.schemadn,
1033 "DEFAULTSITE": names.sitename,
1034 "NETBIOSNAME": names.netbiosname,
1035 "SERVERDN": names.serverdn,
1038 system_session_info = system_session()
1039 samdb.set_session_info(system_session_info)
1040 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1041 # modify a serverReference under cn=config when we are a subdomain, we must
1042 # be system due to ACLs
1043 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1044 "DOMAINDN": names.domaindn,
1045 "SERVERDN": names.serverdn,
1046 "NETBIOSNAME": names.netbiosname,
1049 samdb.set_session_info(admin_session_info)
1051 if dns_backend != "SAMBA_INTERNAL":
1052 # This is Samba4 specific and should be replaced by the correct
1053 # DNS AD-style setup
1054 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1055 "DNSDOMAIN": names.dnsdomain,
1056 "DOMAINDN": names.domaindn,
1057 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1058 "HOSTNAME" : names.hostname,
1059 "DNSNAME" : '%s.%s' % (
1060 names.netbiosname.lower(), names.dnsdomain.lower())
1064 def getpolicypath(sysvolpath, dnsdomain, guid):
1065 """Return the physical path of policy given its guid.
1067 :param sysvolpath: Path to the sysvol folder
1068 :param dnsdomain: DNS name of the AD domain
1069 :param guid: The GUID of the policy
1070 :return: A string with the complete path to the policy folder
1073 guid = "{%s}" % guid
1074 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1078 def create_gpo_struct(policy_path):
1079 if not os.path.exists(policy_path):
1080 os.makedirs(policy_path, 0775)
1081 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1083 f.write("[General]\r\nVersion=0")
1086 p = os.path.join(policy_path, "MACHINE")
1087 if not os.path.exists(p):
1088 os.makedirs(p, 0775)
1089 p = os.path.join(policy_path, "USER")
1090 if not os.path.exists(p):
1091 os.makedirs(p, 0775)
1094 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1095 """Create the default GPO for a domain
1097 :param sysvolpath: Physical path for the sysvol folder
1098 :param dnsdomain: DNS domain name of the AD domain
1099 :param policyguid: GUID of the default domain policy
1100 :param policyguid_dc: GUID of the default domain controler policy
1102 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1103 create_gpo_struct(policy_path)
1105 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1106 create_gpo_struct(policy_path)
1109 def setup_samdb(path, session_info, provision_backend, lp, names,
1110 logger, fill, serverrole, schema, am_rodc=False):
1111 """Setup a complete SAM Database.
1113 :note: This will wipe the main SAM database file!
1116 # Also wipes the database
1117 setup_samdb_partitions(path, logger=logger, lp=lp,
1118 provision_backend=provision_backend, session_info=session_info,
1119 names=names, serverrole=serverrole, schema=schema)
1121 # Load the database, but don's load the global schema and don't connect
1123 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1124 credentials=provision_backend.credentials, lp=lp,
1125 global_schema=False, am_rodc=am_rodc)
1127 logger.info("Pre-loading the Samba 4 and AD schema")
1129 # Load the schema from the one we computed earlier
1130 samdb.set_schema(schema, write_indices_and_attributes=False)
1132 # Set the NTDS settings DN manually - in order to have it already around
1133 # before the provisioned tree exists and we connect
1134 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1136 # And now we can connect to the DB - the schema won't be loaded from the
1140 # But we have to give it one more kick to have it use the schema
1141 # during provision - it needs, now that it is connected, to write
1142 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1143 samdb.set_schema(schema, write_indices_and_attributes=True)
1148 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1149 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1150 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1151 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1153 if next_rid is None:
1156 # Provision does not make much sense values larger than 1000000000
1157 # as the upper range of the rIDAvailablePool is 1073741823 and
1158 # we don't want to create a domain that cannot allocate rids.
1159 if next_rid < 1000 or next_rid > 1000000000:
1160 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1161 error += "the valid range is %u-%u. The default is %u." % (
1162 1000, 1000000000, 1000)
1163 raise ProvisioningError(error)
1165 # ATTENTION: Do NOT change these default values without discussion with the
1166 # team and/or release manager. They have a big impact on the whole program!
1167 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1169 if dom_for_fun_level is None:
1170 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1172 if dom_for_fun_level > domainControllerFunctionality:
1173 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_R2). This won't work!")
1175 domainFunctionality = dom_for_fun_level
1176 forestFunctionality = dom_for_fun_level
1178 # Set the NTDS settings DN manually - in order to have it already around
1179 # before the provisioned tree exists and we connect
1180 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1182 samdb.transaction_start()
1184 # Set the domain functionality levels onto the database.
1185 # Various module (the password_hash module in particular) need
1186 # to know what level of AD we are emulating.
1188 # These will be fixed into the database via the database
1189 # modifictions below, but we need them set from the start.
1190 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1191 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1192 samdb.set_opaque_integer("domainControllerFunctionality",
1193 domainControllerFunctionality)
1195 samdb.set_domain_sid(str(domainsid))
1196 samdb.set_invocation_id(invocationid)
1198 logger.info("Adding DomainDN: %s" % names.domaindn)
1200 # impersonate domain admin
1201 admin_session_info = admin_session(lp, str(domainsid))
1202 samdb.set_session_info(admin_session_info)
1203 if domainguid is not None:
1204 domainguid_line = "objectGUID: %s\n-" % domainguid
1206 domainguid_line = ""
1208 descr = b64encode(get_domain_descriptor(domainsid))
1209 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1210 "DOMAINDN": names.domaindn,
1211 "DOMAINSID": str(domainsid),
1212 "DESCRIPTOR": descr,
1213 "DOMAINGUID": domainguid_line
1216 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1217 "DOMAINDN": names.domaindn,
1218 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1219 "NEXTRID": str(next_rid),
1220 "DEFAULTSITE": names.sitename,
1221 "CONFIGDN": names.configdn,
1222 "POLICYGUID": policyguid,
1223 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1224 "SAMBA_VERSION_STRING": version
1227 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1228 if fill == FILL_FULL:
1229 logger.info("Adding configuration container")
1230 descr = b64encode(get_config_descriptor(domainsid))
1231 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1232 "CONFIGDN": names.configdn,
1233 "DESCRIPTOR": descr,
1236 # The LDIF here was created when the Schema object was constructed
1237 logger.info("Setting up sam.ldb schema")
1238 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1239 samdb.modify_ldif(schema.schema_dn_modify)
1240 samdb.write_prefixes_from_schema()
1241 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1242 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1243 {"SCHEMADN": names.schemadn})
1245 # Now register this container in the root of the forest
1246 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1247 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1251 samdb.transaction_cancel()
1254 samdb.transaction_commit()
1256 samdb.transaction_start()
1258 samdb.invocation_id = invocationid
1260 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1261 if fill == FILL_FULL:
1262 logger.info("Setting up sam.ldb configuration data")
1263 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1264 "CONFIGDN": names.configdn,
1265 "NETBIOSNAME": names.netbiosname,
1266 "DEFAULTSITE": names.sitename,
1267 "DNSDOMAIN": names.dnsdomain,
1268 "DOMAIN": names.domain,
1269 "SCHEMADN": names.schemadn,
1270 "DOMAINDN": names.domaindn,
1271 "SERVERDN": names.serverdn,
1272 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1273 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1276 logger.info("Setting up display specifiers")
1277 display_specifiers_ldif = read_ms_ldif(
1278 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1279 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1280 {"CONFIGDN": names.configdn})
1281 check_all_substituted(display_specifiers_ldif)
1282 samdb.add_ldif(display_specifiers_ldif)
1284 logger.info("Adding users container")
1285 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1286 "DOMAINDN": names.domaindn})
1287 logger.info("Modifying users container")
1288 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1289 "DOMAINDN": names.domaindn})
1290 logger.info("Adding computers container")
1291 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1292 "DOMAINDN": names.domaindn})
1293 logger.info("Modifying computers container")
1294 setup_modify_ldif(samdb,
1295 setup_path("provision_computers_modify.ldif"), {
1296 "DOMAINDN": names.domaindn})
1297 logger.info("Setting up sam.ldb data")
1298 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1299 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1300 "DOMAINDN": names.domaindn,
1301 "NETBIOSNAME": names.netbiosname,
1302 "DEFAULTSITE": names.sitename,
1303 "CONFIGDN": names.configdn,
1304 "SERVERDN": names.serverdn,
1305 "RIDAVAILABLESTART": str(next_rid + 600),
1306 "POLICYGUID_DC": policyguid_dc
1309 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1310 if fill == FILL_FULL:
1311 setup_modify_ldif(samdb,
1312 setup_path("provision_configuration_references.ldif"), {
1313 "CONFIGDN": names.configdn,
1314 "SCHEMADN": names.schemadn})
1316 logger.info("Setting up well known security principals")
1317 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1318 "CONFIGDN": names.configdn,
1321 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1322 setup_modify_ldif(samdb,
1323 setup_path("provision_basedn_references.ldif"),
1324 {"DOMAINDN": names.domaindn})
1326 logger.info("Setting up sam.ldb users and groups")
1327 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1328 "DOMAINDN": names.domaindn,
1329 "DOMAINSID": str(domainsid),
1330 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1331 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1334 logger.info("Setting up self join")
1335 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1336 invocationid=invocationid,
1337 dns_backend=dns_backend,
1339 machinepass=machinepass,
1340 domainsid=domainsid,
1343 policyguid=policyguid,
1344 policyguid_dc=policyguid_dc,
1345 domainControllerFunctionality=domainControllerFunctionality,
1348 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1349 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1350 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1351 assert isinstance(names.ntdsguid, str)
1353 samdb.transaction_cancel()
1356 samdb.transaction_commit()
1361 FILL_SUBDOMAIN = "SUBDOMAIN"
1362 FILL_NT4SYNC = "NT4SYNC"
1364 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1365 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)"
1368 def set_dir_acl(path, acl, lp, domsid, use_ntvfs):
1369 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs)
1370 for root, dirs, files in os.walk(path, topdown=False):
1372 setntacl(lp, os.path.join(root, name), acl, domsid,
1373 use_ntvfs=use_ntvfs)
1375 setntacl(lp, os.path.join(root, name), acl, domsid,
1376 use_ntvfs=use_ntvfs)
1379 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs):
1380 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1383 :param sysvol: Physical path for the sysvol folder
1384 :param dnsdomain: The DNS name of the domain
1385 :param domainsid: The SID of the domain
1386 :param domaindn: The DN of the domain (ie. DC=...)
1387 :param samdb: An LDB object on the SAM db
1388 :param lp: an LP object
1391 # Set ACL for GPO root folder
1392 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1393 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1394 use_ntvfs=use_ntvfs)
1396 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1397 attrs=["cn", "nTSecurityDescriptor"],
1398 expression="", scope=ldb.SCOPE_ONELEVEL)
1401 acl = ndr_unpack(security.descriptor,
1402 str(policy["nTSecurityDescriptor"])).as_sddl()
1403 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1404 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1405 str(domainsid), use_ntvfs)
1408 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1409 domaindn, lp, use_ntvfs):
1410 """Set the ACL for the sysvol share and the subfolders
1412 :param samdb: An LDB object on the SAM db
1413 :param netlogon: Physical path for the netlogon folder
1414 :param sysvol: Physical path for the sysvol folder
1415 :param uid: The UID of the "Administrator" user
1416 :param gid: The GID of the "Domain adminstrators" group
1417 :param domainsid: The SID of the domain
1418 :param dnsdomain: The DNS name of the domain
1419 :param domaindn: The DN of the domain (ie. DC=...)
1423 # This will ensure that the smbd code we are running when setting ACLs
1424 # is initialised with the smb.conf
1425 s3conf = s3param.get_context()
1426 s3conf.load(lp.configfile)
1427 # ensure we are using the right samba_dsdb passdb backend, no matter what
1428 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1429 passdb.reload_static_pdb()
1431 # ensure that we init the samba_dsdb backend, so the domain sid is
1432 # marked in secrets.tdb
1433 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1435 # now ensure everything matches correctly, to avoid wierd issues
1436 if passdb.get_global_sam_sid() != domainsid:
1437 raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid))
1439 domain_info = s4_passdb.domain_info()
1440 if domain_info["dom_sid"] != domainsid:
1441 raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
1443 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1444 raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
1449 os.chown(sysvol, -1, gid)
1455 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1456 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs)
1457 for root, dirs, files in os.walk(sysvol, topdown=False):
1459 if use_ntvfs and canchown:
1460 os.chown(os.path.join(root, name), -1, gid)
1461 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs)
1463 if use_ntvfs and canchown:
1464 os.chown(os.path.join(root, name), -1, gid)
1465 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs)
1467 # Set acls on Policy folder and policies folders
1468 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs)
1470 def acl_type(direct_db_access):
1471 if direct_db_access:
1476 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1477 fsacl = getntacl(lp, path, direct_db_access=direct_db_access)
1478 fsacl_sddl = fsacl.as_sddl(domainsid)
1479 if fsacl_sddl != acl:
1480 raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), path, fsacl_sddl, acl))
1482 for root, dirs, files in os.walk(path, topdown=False):
1484 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1486 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1487 fsacl_sddl = fsacl.as_sddl(domainsid)
1488 if fsacl_sddl != acl:
1489 raise ProvisioningError('%s ACL on GPO file %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl))
1492 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1494 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1495 fsacl_sddl = fsacl.as_sddl(domainsid)
1496 if fsacl_sddl != acl:
1497 raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl))
1500 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1502 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1505 :param sysvol: Physical path for the sysvol folder
1506 :param dnsdomain: The DNS name of the domain
1507 :param domainsid: The SID of the domain
1508 :param domaindn: The DN of the domain (ie. DC=...)
1509 :param samdb: An LDB object on the SAM db
1510 :param lp: an LP object
1513 # Set ACL for GPO root folder
1514 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1515 fsacl = getntacl(lp, root_policy_path, direct_db_access=direct_db_access)
1517 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1518 fsacl_sddl = fsacl.as_sddl(domainsid)
1519 if fsacl_sddl != POLICIES_ACL:
1520 raise ProvisioningError('%s ACL on policy root %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), root_policy_path, fsacl_sddl, fsacl))
1521 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1522 attrs=["cn", "nTSecurityDescriptor"],
1523 expression="", scope=ldb.SCOPE_ONELEVEL)
1526 acl = ndr_unpack(security.descriptor,
1527 str(policy["nTSecurityDescriptor"])).as_sddl()
1528 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1529 check_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1530 domainsid, direct_db_access)
1533 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1535 """Set the ACL for the sysvol share and the subfolders
1537 :param samdb: An LDB object on the SAM db
1538 :param netlogon: Physical path for the netlogon folder
1539 :param sysvol: Physical path for the sysvol folder
1540 :param uid: The UID of the "Administrator" user
1541 :param gid: The GID of the "Domain adminstrators" group
1542 :param domainsid: The SID of the domain
1543 :param dnsdomain: The DNS name of the domain
1544 :param domaindn: The DN of the domain (ie. DC=...)
1547 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1548 s3conf = s3param.get_context()
1549 s3conf.load(lp.configfile)
1550 # ensure we are using the right samba_dsdb passdb backend, no matter what
1551 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1552 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1553 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1555 # now ensure everything matches correctly, to avoid wierd issues
1556 if passdb.get_global_sam_sid() != domainsid:
1557 raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid))
1559 domain_info = s4_passdb.domain_info()
1560 if domain_info["dom_sid"] != domainsid:
1561 raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
1563 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1564 raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
1566 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1567 for direct_db_access in [True, False]:
1568 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1569 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access)
1571 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1572 fsacl_sddl = fsacl.as_sddl(domainsid)
1573 if fsacl_sddl != SYSVOL_ACL:
1574 raise ProvisioningError('%s ACL on sysvol directory %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), dir_path, fsacl_sddl, SYSVOL_ACL))
1576 # Check acls on Policy folder and policies folders
1577 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1581 def interface_ips_v4(lp):
1582 """return only IPv4 IPs"""
1583 ips = samba.interface_ips(lp, False)
1586 if i.find(':') == -1:
1591 def interface_ips_v6(lp, linklocal=False):
1592 """return only IPv6 IPs"""
1593 ips = samba.interface_ips(lp, False)
1596 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1601 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1602 domainsid, schema=None,
1603 targetdir=None, samdb_fill=FILL_FULL,
1604 hostip=None, hostip6=None,
1605 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1606 domainguid=None, policyguid=None, policyguid_dc=None,
1607 invocationid=None, machinepass=None, ntdsguid=None,
1608 dns_backend=None, dnspass=None,
1609 serverrole=None, dom_for_fun_level=None,
1610 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=True):
1611 # create/adapt the group policy GUIDs
1612 # Default GUID for default policy are described at
1613 # "How Core Group Policy Works"
1614 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1615 if policyguid is None:
1616 policyguid = DEFAULT_POLICY_GUID
1617 policyguid = policyguid.upper()
1618 if policyguid_dc is None:
1619 policyguid_dc = DEFAULT_DC_POLICY_GUID
1620 policyguid_dc = policyguid_dc.upper()
1622 if invocationid is None:
1623 invocationid = str(uuid.uuid4())
1625 if krbtgtpass is None:
1626 krbtgtpass = samba.generate_random_password(128, 255)
1627 if machinepass is None:
1628 machinepass = samba.generate_random_password(128, 255)
1630 dnspass = samba.generate_random_password(128, 255)
1632 samdb = fill_samdb(samdb, lp, names, logger=logger,
1633 domainsid=domainsid, schema=schema, domainguid=domainguid,
1634 policyguid=policyguid, policyguid_dc=policyguid_dc,
1635 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1636 invocationid=invocationid, machinepass=machinepass,
1637 dns_backend=dns_backend, dnspass=dnspass,
1638 ntdsguid=ntdsguid, serverrole=serverrole,
1639 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1640 next_rid=next_rid, dc_rid=dc_rid)
1642 if serverrole == "active directory domain controller":
1644 # Set up group policies (domain policy and domain controller
1646 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1648 if not skip_sysvolacl:
1649 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1650 paths.wheel_gid, domainsid, names.dnsdomain,
1651 names.domaindn, lp, use_ntvfs)
1653 secretsdb_self_join(secrets_ldb, domain=names.domain,
1654 realm=names.realm, dnsdomain=names.dnsdomain,
1655 netbiosname=names.netbiosname, domainsid=domainsid,
1656 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1658 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1659 # In future, this might be determined from some configuration
1660 kerberos_enctypes = str(ENC_ALL_TYPES)
1663 msg = ldb.Message(ldb.Dn(samdb,
1664 samdb.searchone("distinguishedName",
1665 expression="samAccountName=%s$" % names.netbiosname,
1666 scope=ldb.SCOPE_SUBTREE)))
1667 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1668 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1669 name="msDS-SupportedEncryptionTypes")
1671 except ldb.LdbError, (enum, estr):
1672 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1673 # It might be that this attribute does not exist in this schema
1676 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1677 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1678 dnspass=dnspass, os_level=dom_for_fun_level,
1679 targetdir=targetdir, site=DEFAULTSITE)
1681 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1682 attribute="objectGUID")
1683 assert isinstance(domainguid, str)
1685 lastProvisionUSNs = get_last_provision_usn(samdb)
1686 maxUSN = get_max_usn(samdb, str(names.rootdn))
1687 if lastProvisionUSNs is not None:
1688 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1690 set_provision_usn(samdb, 0, maxUSN, invocationid)
1692 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1693 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1694 { 'NTDSGUID' : names.ntdsguid })
1696 # fix any dangling GUIDs from the provision
1697 logger.info("Fixing provision GUIDs")
1698 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1700 samdb.transaction_start()
1702 # a small number of GUIDs are missing because of ordering issues in the
1704 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1705 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1706 scope=ldb.SCOPE_BASE,
1707 attrs=['defaultObjectCategory'])
1708 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1709 scope=ldb.SCOPE_ONELEVEL,
1710 attrs=['ipsecOwnersReference',
1711 'ipsecFilterReference',
1712 'ipsecISAKMPReference',
1713 'ipsecNegotiationPolicyReference',
1714 'ipsecNFAReference'])
1716 samdb.transaction_cancel()
1719 samdb.transaction_commit()
1723 "ROLE_STANDALONE": "standalone server",
1724 "ROLE_DOMAIN_MEMBER": "member server",
1725 "ROLE_DOMAIN_BDC": "active directory domain controller",
1726 "ROLE_DOMAIN_PDC": "active directory domain controller",
1727 "dc": "active directory domain controller",
1728 "member": "member server",
1729 "domain controller": "active directory domain controller",
1730 "active directory domain controller": "active directory domain controller",
1731 "member server": "member server",
1732 "standalone": "standalone server",
1733 "standalone server": "standalone server",
1737 def sanitize_server_role(role):
1738 """Sanitize a server role name.
1740 :param role: Server role
1741 :raise ValueError: If the role can not be interpreted
1742 :return: Sanitized server role (one of "member server",
1743 "active directory domain controller", "standalone server")
1746 return _ROLES_MAP[role]
1748 raise ValueError(role)
1751 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1753 """Create AD entries for the fake ypserver.
1755 This is needed for being able to manipulate posix attrs via ADUC.
1757 samdb.transaction_start()
1759 logger.info("Setting up fake yp server settings")
1760 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1761 "DOMAINDN": domaindn,
1762 "NETBIOSNAME": netbiosname,
1763 "NISDOMAIN": nisdomain,
1766 samdb.transaction_cancel()
1769 samdb.transaction_commit()
1772 def provision(logger, session_info, credentials, smbconf=None,
1773 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1774 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1775 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1776 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1777 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1778 dns_backend=None, dns_forwarder=None, dnspass=None,
1779 invocationid=None, machinepass=None, ntdsguid=None,
1780 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1781 serverrole=None, dom_for_fun_level=None, backend_type=None,
1782 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1783 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1784 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1787 :note: caution, this wipes all existing data!
1791 serverrole = sanitize_server_role(serverrole)
1793 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1795 if ldapadminpass is None:
1796 # Make a new, random password between Samba and it's LDAP server
1797 ldapadminpass = samba.generate_random_password(128, 255)
1799 if backend_type is None:
1800 backend_type = "ldb"
1802 if domainsid is None:
1803 domainsid = security.random_sid()
1805 domainsid = security.dom_sid(domainsid)
1807 root_uid = findnss_uid([root or "root"])
1808 nobody_uid = findnss_uid([nobody or "nobody"])
1809 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1811 wheel_gid = findnss_gid(["wheel", "adm"])
1813 wheel_gid = findnss_gid([wheel])
1815 bind_gid = findnss_gid(["bind", "named"])
1819 if targetdir is not None:
1820 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1821 elif smbconf is None:
1822 smbconf = samba.param.default_path()
1823 if not os.path.exists(os.path.dirname(smbconf)):
1824 os.makedirs(os.path.dirname(smbconf))
1826 server_services = []
1829 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1831 if dns_backend != "SAMBA_INTERNAL":
1832 server_services.append("-dns")
1834 if dns_forwarder is not None:
1835 global_param["dns forwarder"] = [dns_forwarder]
1838 server_services.append("+smb")
1839 server_services.append("-s3fs")
1840 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1842 if len(server_services) > 0:
1843 global_param["server services"] = server_services
1845 # only install a new smb.conf if there isn't one there already
1846 if os.path.exists(smbconf):
1847 # if Samba Team members can't figure out the weird errors
1848 # loading an empty smb.conf gives, then we need to be smarter.
1849 # Pretend it just didn't exist --abartlet
1850 f = open(smbconf, 'r')
1852 data = f.read().lstrip()
1855 if data is None or data == "":
1856 make_smbconf(smbconf, hostname, domain, realm,
1857 targetdir, serverrole=serverrole,
1858 eadb=useeadb, use_ntvfs=use_ntvfs,
1859 lp=lp, global_param=global_param)
1861 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1862 serverrole=serverrole,
1863 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1866 lp = samba.param.LoadParm()
1868 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1869 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1870 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1871 sitename=sitename, rootdn=rootdn)
1872 paths = provision_paths_from_lp(lp, names.dnsdomain)
1874 paths.bind_gid = bind_gid
1875 paths.root_uid = root_uid;
1876 paths.wheel_gid = wheel_gid
1879 logger.info("Looking up IPv4 addresses")
1880 hostips = interface_ips_v4(lp)
1881 if len(hostips) > 0:
1883 if len(hostips) > 1:
1884 logger.warning("More than one IPv4 address found. Using %s",
1886 if hostip == "127.0.0.1":
1889 logger.warning("No IPv4 address will be assigned")
1892 logger.info("Looking up IPv6 addresses")
1893 hostips = interface_ips_v6(lp, linklocal=False)
1895 hostip6 = hostips[0]
1896 if len(hostips) > 1:
1897 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1899 logger.warning("No IPv6 address will be assigned")
1901 names.hostip = hostip
1902 names.hostip6 = hostip6
1904 if serverrole is None:
1905 serverrole = lp.get("server role")
1907 if not os.path.exists(paths.private_dir):
1908 os.mkdir(paths.private_dir)
1909 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1910 os.mkdir(os.path.join(paths.private_dir, "tls"))
1911 if not os.path.exists(paths.state_dir):
1912 os.mkdir(paths.state_dir)
1914 if paths.sysvol and not os.path.exists(paths.sysvol):
1915 os.makedirs(paths.sysvol, 0775)
1917 if not use_ntvfs and serverrole == "active directory domain controller":
1918 s3conf = s3param.get_context()
1919 s3conf.load(lp.configfile)
1921 if paths.sysvol is None:
1922 raise MissingShareError("sysvol", paths.smbconf)
1924 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1927 smbd.set_simple_acl(file.name, 0755, wheel_gid)
1929 if not smbd.have_posix_acls():
1930 # This clue is only strictly correct for RPM and
1931 # Debian-like Linux systems, but hopefully other users
1932 # will get enough clue from it.
1933 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1935 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1937 smbd.chown(file.name, root_uid, wheel_gid)
1939 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1943 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1945 schema = Schema(domainsid, invocationid=invocationid,
1946 schemadn=names.schemadn)
1948 if backend_type == "ldb":
1949 provision_backend = LDBBackend(backend_type, paths=paths,
1950 lp=lp, credentials=credentials,
1951 names=names, logger=logger)
1952 elif backend_type == "existing":
1953 # If support for this is ever added back, then the URI will need to be
1955 provision_backend = ExistingBackend(backend_type, paths=paths,
1956 lp=lp, credentials=credentials,
1957 names=names, logger=logger,
1958 ldap_backend_forced_uri=None)
1959 elif backend_type == "fedora-ds":
1960 provision_backend = FDSBackend(backend_type, paths=paths,
1961 lp=lp, credentials=credentials,
1962 names=names, logger=logger, domainsid=domainsid,
1963 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1964 slapd_path=slapd_path,
1966 elif backend_type == "openldap":
1967 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1968 lp=lp, credentials=credentials,
1969 names=names, logger=logger, domainsid=domainsid,
1970 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1971 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1973 raise ValueError("Unknown LDAP backend type selected")
1975 provision_backend.init()
1976 provision_backend.start()
1978 # only install a new shares config db if there is none
1979 if not os.path.exists(paths.shareconf):
1980 logger.info("Setting up share.ldb")
1981 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1982 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1984 logger.info("Setting up secrets.ldb")
1985 secrets_ldb = setup_secretsdb(paths,
1986 session_info=session_info,
1987 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1990 logger.info("Setting up the registry")
1991 setup_registry(paths.hklm, session_info, lp=lp)
1993 logger.info("Setting up the privileges database")
1994 setup_privileges(paths.privilege, session_info, lp=lp)
1996 logger.info("Setting up idmap db")
1997 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
1999 setup_name_mappings(idmap, sid=str(domainsid),
2000 root_uid=root_uid, nobody_uid=nobody_uid,
2001 users_gid=users_gid, wheel_gid=wheel_gid)
2003 logger.info("Setting up SAM db")
2004 samdb = setup_samdb(paths.samdb, session_info,
2005 provision_backend, lp, names, logger=logger,
2006 serverrole=serverrole,
2007 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2009 if serverrole == "active directory domain controller":
2010 if paths.netlogon is None:
2011 raise MissingShareError("netlogon", paths.smbconf)
2013 if paths.sysvol is None:
2014 raise MissingShareError("sysvol", paths.smbconf)
2016 if not os.path.isdir(paths.netlogon):
2017 os.makedirs(paths.netlogon, 0755)
2019 if adminpass is None:
2020 adminpass = samba.generate_random_password(12, 32)
2021 adminpass_generated = True
2023 adminpass_generated = False
2025 if samdb_fill == FILL_FULL:
2026 provision_fill(samdb, secrets_ldb, logger, names, paths,
2027 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2028 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2029 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2030 krbtgtpass=krbtgtpass, domainguid=domainguid,
2031 policyguid=policyguid, policyguid_dc=policyguid_dc,
2032 invocationid=invocationid, machinepass=machinepass,
2033 ntdsguid=ntdsguid, dns_backend=dns_backend,
2034 dnspass=dnspass, serverrole=serverrole,
2035 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2036 lp=lp, use_ntvfs=use_ntvfs,
2037 skip_sysvolacl=skip_sysvolacl)
2039 create_krb5_conf(paths.krb5conf,
2040 dnsdomain=names.dnsdomain, hostname=names.hostname,
2042 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2043 "generated at %s", paths.krb5conf)
2045 if serverrole == "active directory domain controller":
2046 create_dns_update_list(lp, logger, paths)
2048 backend_result = provision_backend.post_setup()
2049 provision_backend.shutdown()
2051 create_phpldapadmin_config(paths.phpldapadminconfig,
2054 secrets_ldb.transaction_cancel()
2057 # Now commit the secrets.ldb to disk
2058 secrets_ldb.transaction_commit()
2060 # the commit creates the dns.keytab, now chown it
2061 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2062 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2064 os.chmod(dns_keytab_path, 0640)
2065 os.chown(dns_keytab_path, -1, paths.bind_gid)
2067 if not os.environ.has_key('SAMBA_SELFTEST'):
2068 logger.info("Failed to chown %s to bind gid %u",
2069 dns_keytab_path, paths.bind_gid)
2071 result = ProvisionResult()
2072 result.server_role = serverrole
2073 result.domaindn = domaindn
2074 result.paths = paths
2075 result.names = names
2077 result.samdb = samdb
2078 result.idmap = idmap
2079 result.domainsid = str(domainsid)
2081 if samdb_fill == FILL_FULL:
2082 result.adminpass_generated = adminpass_generated
2083 result.adminpass = adminpass
2085 result.adminpass_generated = False
2086 result.adminpass = None
2088 result.backend_result = backend_result
2091 provision_fake_ypserver(logger=logger, samdb=samdb,
2092 domaindn=names.domaindn, netbiosname=names.netbiosname,
2093 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2098 def provision_become_dc(smbconf=None, targetdir=None,
2099 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2100 serverdn=None, domain=None, hostname=None, domainsid=None,
2101 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2102 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2103 dns_backend=None, root=None, nobody=None, users=None, wheel=None,
2104 backup=None, serverrole=None, ldap_backend=None,
2105 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2107 logger = logging.getLogger("provision")
2108 samba.set_debug_level(debuglevel)
2110 res = provision(logger, system_session(), None,
2111 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2112 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2113 configdn=configdn, serverdn=serverdn, domain=domain,
2114 hostname=hostname, hostip=None, domainsid=domainsid,
2115 machinepass=machinepass,
2116 serverrole="active directory domain controller",
2117 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2118 use_ntvfs=use_ntvfs)
2119 res.lp.set("debuglevel", str(debuglevel))
2123 def create_phpldapadmin_config(path, ldapi_uri):
2124 """Create a PHP LDAP admin configuration file.
2126 :param path: Path to write the configuration to.
2128 setup_file(setup_path("phpldapadmin-config.php"), path,
2129 {"S4_LDAPI_URI": ldapi_uri})
2132 def create_krb5_conf(path, dnsdomain, hostname, realm):
2133 """Write out a file containing zone statements suitable for inclusion in a
2134 named.conf file (including GSS-TSIG configuration).
2136 :param path: Path of the new named.conf file.
2137 :param dnsdomain: DNS Domain name
2138 :param hostname: Local hostname
2139 :param realm: Realm name
2141 setup_file(setup_path("krb5.conf"), path, {
2142 "DNSDOMAIN": dnsdomain,
2143 "HOSTNAME": hostname,
2148 class ProvisioningError(Exception):
2149 """A generic provision error."""
2151 def __init__(self, value):
2155 return "ProvisioningError: " + self.value
2158 class InvalidNetbiosName(Exception):
2159 """A specified name was not a valid NetBIOS name."""
2161 def __init__(self, name):
2162 super(InvalidNetbiosName, self).__init__(
2163 "The name '%r' is not a valid NetBIOS name" % name)
2166 class MissingShareError(ProvisioningError):
2168 def __init__(self, name, smbconf):
2169 super(MissingShareError, self).__init__(
2170 "Existing smb.conf does not have a [%s] share, but you are "
2171 "configuring a DC. Please remove %s or add the share manually." %