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 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 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1052 "DNSDOMAIN": names.dnsdomain,
1053 "DOMAINDN": names.domaindn,
1054 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1055 "HOSTNAME" : names.hostname,
1056 "DNSNAME" : '%s.%s' % (
1057 names.netbiosname.lower(), names.dnsdomain.lower())
1061 def getpolicypath(sysvolpath, dnsdomain, guid):
1062 """Return the physical path of policy given its guid.
1064 :param sysvolpath: Path to the sysvol folder
1065 :param dnsdomain: DNS name of the AD domain
1066 :param guid: The GUID of the policy
1067 :return: A string with the complete path to the policy folder
1070 guid = "{%s}" % guid
1071 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1075 def create_gpo_struct(policy_path):
1076 if not os.path.exists(policy_path):
1077 os.makedirs(policy_path, 0775)
1078 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1080 f.write("[General]\r\nVersion=0")
1083 p = os.path.join(policy_path, "MACHINE")
1084 if not os.path.exists(p):
1085 os.makedirs(p, 0775)
1086 p = os.path.join(policy_path, "USER")
1087 if not os.path.exists(p):
1088 os.makedirs(p, 0775)
1091 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1092 """Create the default GPO for a domain
1094 :param sysvolpath: Physical path for the sysvol folder
1095 :param dnsdomain: DNS domain name of the AD domain
1096 :param policyguid: GUID of the default domain policy
1097 :param policyguid_dc: GUID of the default domain controler policy
1099 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1100 create_gpo_struct(policy_path)
1102 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1103 create_gpo_struct(policy_path)
1106 def setup_samdb(path, session_info, provision_backend, lp, names,
1107 logger, fill, serverrole, schema, am_rodc=False):
1108 """Setup a complete SAM Database.
1110 :note: This will wipe the main SAM database file!
1113 # Also wipes the database
1114 setup_samdb_partitions(path, logger=logger, lp=lp,
1115 provision_backend=provision_backend, session_info=session_info,
1116 names=names, serverrole=serverrole, schema=schema)
1118 # Load the database, but don's load the global schema and don't connect
1120 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1121 credentials=provision_backend.credentials, lp=lp,
1122 global_schema=False, am_rodc=am_rodc)
1124 logger.info("Pre-loading the Samba 4 and AD schema")
1126 # Load the schema from the one we computed earlier
1127 samdb.set_schema(schema, write_indices_and_attributes=False)
1129 # Set the NTDS settings DN manually - in order to have it already around
1130 # before the provisioned tree exists and we connect
1131 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1133 # And now we can connect to the DB - the schema won't be loaded from the
1137 # But we have to give it one more kick to have it use the schema
1138 # during provision - it needs, now that it is connected, to write
1139 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1140 samdb.set_schema(schema, write_indices_and_attributes=True)
1145 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1146 policyguid_dc, fill, adminpass, krbtgtpass, machinepass,
1147 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1148 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1150 if next_rid is None:
1153 # Provision does not make much sense values larger than 1000000000
1154 # as the upper range of the rIDAvailablePool is 1073741823 and
1155 # we don't want to create a domain that cannot allocate rids.
1156 if next_rid < 1000 or next_rid > 1000000000:
1157 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1158 error += "the valid range is %u-%u. The default is %u." % (
1159 1000, 1000000000, 1000)
1160 raise ProvisioningError(error)
1162 # ATTENTION: Do NOT change these default values without discussion with the
1163 # team and/or release manager. They have a big impact on the whole program!
1164 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1166 if dom_for_fun_level is None:
1167 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1169 if dom_for_fun_level > domainControllerFunctionality:
1170 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!")
1172 domainFunctionality = dom_for_fun_level
1173 forestFunctionality = dom_for_fun_level
1175 # Set the NTDS settings DN manually - in order to have it already around
1176 # before the provisioned tree exists and we connect
1177 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1179 samdb.transaction_start()
1181 # Set the domain functionality levels onto the database.
1182 # Various module (the password_hash module in particular) need
1183 # to know what level of AD we are emulating.
1185 # These will be fixed into the database via the database
1186 # modifictions below, but we need them set from the start.
1187 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1188 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1189 samdb.set_opaque_integer("domainControllerFunctionality",
1190 domainControllerFunctionality)
1192 samdb.set_domain_sid(str(domainsid))
1193 samdb.set_invocation_id(invocationid)
1195 logger.info("Adding DomainDN: %s" % names.domaindn)
1197 # impersonate domain admin
1198 admin_session_info = admin_session(lp, str(domainsid))
1199 samdb.set_session_info(admin_session_info)
1200 if domainguid is not None:
1201 domainguid_line = "objectGUID: %s\n-" % domainguid
1203 domainguid_line = ""
1205 descr = b64encode(get_domain_descriptor(domainsid))
1206 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1207 "DOMAINDN": names.domaindn,
1208 "DOMAINSID": str(domainsid),
1209 "DESCRIPTOR": descr,
1210 "DOMAINGUID": domainguid_line
1213 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1214 "DOMAINDN": names.domaindn,
1215 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1216 "NEXTRID": str(next_rid),
1217 "DEFAULTSITE": names.sitename,
1218 "CONFIGDN": names.configdn,
1219 "POLICYGUID": policyguid,
1220 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1221 "SAMBA_VERSION_STRING": version
1224 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1225 if fill == FILL_FULL:
1226 logger.info("Adding configuration container")
1227 descr = b64encode(get_config_descriptor(domainsid))
1228 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1229 "CONFIGDN": names.configdn,
1230 "DESCRIPTOR": descr,
1233 # The LDIF here was created when the Schema object was constructed
1234 logger.info("Setting up sam.ldb schema")
1235 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1236 samdb.modify_ldif(schema.schema_dn_modify)
1237 samdb.write_prefixes_from_schema()
1238 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1239 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1240 {"SCHEMADN": names.schemadn})
1242 # Now register this container in the root of the forest
1243 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1244 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1248 samdb.transaction_cancel()
1251 samdb.transaction_commit()
1253 samdb.transaction_start()
1255 samdb.invocation_id = invocationid
1257 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1258 if fill == FILL_FULL:
1259 logger.info("Setting up sam.ldb configuration data")
1260 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1261 "CONFIGDN": names.configdn,
1262 "NETBIOSNAME": names.netbiosname,
1263 "DEFAULTSITE": names.sitename,
1264 "DNSDOMAIN": names.dnsdomain,
1265 "DOMAIN": names.domain,
1266 "SCHEMADN": names.schemadn,
1267 "DOMAINDN": names.domaindn,
1268 "SERVERDN": names.serverdn,
1269 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1270 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1273 logger.info("Setting up display specifiers")
1274 display_specifiers_ldif = read_ms_ldif(
1275 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1276 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1277 {"CONFIGDN": names.configdn})
1278 check_all_substituted(display_specifiers_ldif)
1279 samdb.add_ldif(display_specifiers_ldif)
1281 logger.info("Adding users container")
1282 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1283 "DOMAINDN": names.domaindn})
1284 logger.info("Modifying users container")
1285 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1286 "DOMAINDN": names.domaindn})
1287 logger.info("Adding computers container")
1288 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1289 "DOMAINDN": names.domaindn})
1290 logger.info("Modifying computers container")
1291 setup_modify_ldif(samdb,
1292 setup_path("provision_computers_modify.ldif"), {
1293 "DOMAINDN": names.domaindn})
1294 logger.info("Setting up sam.ldb data")
1295 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1296 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1297 "DOMAINDN": names.domaindn,
1298 "NETBIOSNAME": names.netbiosname,
1299 "DEFAULTSITE": names.sitename,
1300 "CONFIGDN": names.configdn,
1301 "SERVERDN": names.serverdn,
1302 "RIDAVAILABLESTART": str(next_rid + 600),
1303 "POLICYGUID_DC": policyguid_dc
1306 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1307 if fill == FILL_FULL:
1308 setup_modify_ldif(samdb,
1309 setup_path("provision_configuration_references.ldif"), {
1310 "CONFIGDN": names.configdn,
1311 "SCHEMADN": names.schemadn})
1313 logger.info("Setting up well known security principals")
1314 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1315 "CONFIGDN": names.configdn,
1318 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1319 setup_modify_ldif(samdb,
1320 setup_path("provision_basedn_references.ldif"),
1321 {"DOMAINDN": names.domaindn})
1323 logger.info("Setting up sam.ldb users and groups")
1324 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1325 "DOMAINDN": names.domaindn,
1326 "DOMAINSID": str(domainsid),
1327 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1328 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1331 logger.info("Setting up self join")
1332 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1333 invocationid=invocationid,
1335 machinepass=machinepass,
1336 domainsid=domainsid,
1339 policyguid=policyguid,
1340 policyguid_dc=policyguid_dc,
1341 domainControllerFunctionality=domainControllerFunctionality,
1344 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1345 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1346 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1347 assert isinstance(names.ntdsguid, str)
1349 samdb.transaction_cancel()
1352 samdb.transaction_commit()
1357 FILL_SUBDOMAIN = "SUBDOMAIN"
1358 FILL_NT4SYNC = "NT4SYNC"
1360 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1361 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)"
1364 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb):
1365 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1366 for root, dirs, files in os.walk(path, topdown=False):
1368 setntacl(lp, os.path.join(root, name), acl, domsid,
1369 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1371 setntacl(lp, os.path.join(root, name), acl, domsid,
1372 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1375 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1376 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1379 :param sysvol: Physical path for the sysvol folder
1380 :param dnsdomain: The DNS name of the domain
1381 :param domainsid: The SID of the domain
1382 :param domaindn: The DN of the domain (ie. DC=...)
1383 :param samdb: An LDB object on the SAM db
1384 :param lp: an LP object
1387 # Set ACL for GPO root folder
1388 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1389 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1390 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1392 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1393 attrs=["cn", "nTSecurityDescriptor"],
1394 expression="", scope=ldb.SCOPE_ONELEVEL)
1397 acl = ndr_unpack(security.descriptor,
1398 str(policy["nTSecurityDescriptor"])).as_sddl()
1399 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1400 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1401 str(domainsid), use_ntvfs,
1405 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1406 domaindn, lp, use_ntvfs):
1407 """Set the ACL for the sysvol share and the subfolders
1409 :param samdb: An LDB object on the SAM db
1410 :param netlogon: Physical path for the netlogon folder
1411 :param sysvol: Physical path for the sysvol folder
1412 :param uid: The UID of the "Administrator" user
1413 :param gid: The GID of the "Domain adminstrators" group
1414 :param domainsid: The SID of the domain
1415 :param dnsdomain: The DNS name of the domain
1416 :param domaindn: The DN of the domain (ie. DC=...)
1421 # This will ensure that the smbd code we are running when setting ACLs
1422 # is initialised with the smb.conf
1423 s3conf = s3param.get_context()
1424 s3conf.load(lp.configfile)
1425 # ensure we are using the right samba_dsdb passdb backend, no matter what
1426 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1427 passdb.reload_static_pdb()
1429 # ensure that we init the samba_dsdb backend, so the domain sid is
1430 # marked in secrets.tdb
1431 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1433 # now ensure everything matches correctly, to avoid wierd issues
1434 if passdb.get_global_sam_sid() != domainsid:
1435 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))
1437 domain_info = s4_passdb.domain_info()
1438 if domain_info["dom_sid"] != domainsid:
1439 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))
1441 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1442 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()))
1447 os.chown(sysvol, -1, gid)
1453 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1454 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1455 for root, dirs, files in os.walk(sysvol, topdown=False):
1457 if use_ntvfs and canchown:
1458 os.chown(os.path.join(root, name), -1, gid)
1459 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1461 if use_ntvfs and canchown:
1462 os.chown(os.path.join(root, name), -1, gid)
1463 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1465 # Set acls on Policy folder and policies folders
1466 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1468 def acl_type(direct_db_access):
1469 if direct_db_access:
1474 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1475 fsacl = getntacl(lp, path, direct_db_access=direct_db_access)
1476 fsacl_sddl = fsacl.as_sddl(domainsid)
1477 if fsacl_sddl != acl:
1478 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))
1480 for root, dirs, files in os.walk(path, topdown=False):
1482 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1484 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1485 fsacl_sddl = fsacl.as_sddl(domainsid)
1486 if fsacl_sddl != acl:
1487 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))
1490 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1492 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1493 fsacl_sddl = fsacl.as_sddl(domainsid)
1494 if fsacl_sddl != acl:
1495 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))
1498 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1500 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1503 :param sysvol: Physical path for the sysvol folder
1504 :param dnsdomain: The DNS name of the domain
1505 :param domainsid: The SID of the domain
1506 :param domaindn: The DN of the domain (ie. DC=...)
1507 :param samdb: An LDB object on the SAM db
1508 :param lp: an LP object
1511 # Set ACL for GPO root folder
1512 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1513 fsacl = getntacl(lp, root_policy_path, direct_db_access=direct_db_access)
1515 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1516 fsacl_sddl = fsacl.as_sddl(domainsid)
1517 if fsacl_sddl != POLICIES_ACL:
1518 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))
1519 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1520 attrs=["cn", "nTSecurityDescriptor"],
1521 expression="", scope=ldb.SCOPE_ONELEVEL)
1524 acl = ndr_unpack(security.descriptor,
1525 str(policy["nTSecurityDescriptor"])).as_sddl()
1526 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1527 check_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1528 domainsid, direct_db_access)
1531 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1533 """Set the ACL for the sysvol share and the subfolders
1535 :param samdb: An LDB object on the SAM db
1536 :param netlogon: Physical path for the netlogon folder
1537 :param sysvol: Physical path for the sysvol folder
1538 :param uid: The UID of the "Administrator" user
1539 :param gid: The GID of the "Domain adminstrators" group
1540 :param domainsid: The SID of the domain
1541 :param dnsdomain: The DNS name of the domain
1542 :param domaindn: The DN of the domain (ie. DC=...)
1545 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1546 s3conf = s3param.get_context()
1547 s3conf.load(lp.configfile)
1548 # ensure we are using the right samba_dsdb passdb backend, no matter what
1549 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1550 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1551 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1553 # now ensure everything matches correctly, to avoid wierd issues
1554 if passdb.get_global_sam_sid() != domainsid:
1555 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))
1557 domain_info = s4_passdb.domain_info()
1558 if domain_info["dom_sid"] != domainsid:
1559 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))
1561 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1562 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()))
1564 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1565 for direct_db_access in [True, False]:
1566 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1567 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access)
1569 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1570 fsacl_sddl = fsacl.as_sddl(domainsid)
1571 if fsacl_sddl != SYSVOL_ACL:
1572 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))
1574 # Check acls on Policy folder and policies folders
1575 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1579 def interface_ips_v4(lp):
1580 """return only IPv4 IPs"""
1581 ips = samba.interface_ips(lp, False)
1584 if i.find(':') == -1:
1589 def interface_ips_v6(lp, linklocal=False):
1590 """return only IPv6 IPs"""
1591 ips = samba.interface_ips(lp, False)
1594 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1599 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1600 domainsid, schema=None,
1601 targetdir=None, samdb_fill=FILL_FULL,
1602 hostip=None, hostip6=None,
1603 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1604 domainguid=None, policyguid=None, policyguid_dc=None,
1605 invocationid=None, machinepass=None, ntdsguid=None,
1607 dnspass=None, serverrole=None, dom_for_fun_level=None,
1608 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1609 # create/adapt the group policy GUIDs
1610 # Default GUID for default policy are described at
1611 # "How Core Group Policy Works"
1612 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1613 if policyguid is None:
1614 policyguid = DEFAULT_POLICY_GUID
1615 policyguid = policyguid.upper()
1616 if policyguid_dc is None:
1617 policyguid_dc = DEFAULT_DC_POLICY_GUID
1618 policyguid_dc = policyguid_dc.upper()
1620 if invocationid is None:
1621 invocationid = str(uuid.uuid4())
1623 if krbtgtpass is None:
1624 krbtgtpass = samba.generate_random_password(128, 255)
1625 if machinepass is None:
1626 machinepass = samba.generate_random_password(128, 255)
1628 dnspass = samba.generate_random_password(128, 255)
1630 samdb = fill_samdb(samdb, lp, names, logger=logger,
1631 domainsid=domainsid, schema=schema, domainguid=domainguid,
1632 policyguid=policyguid, policyguid_dc=policyguid_dc,
1633 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1634 invocationid=invocationid, machinepass=machinepass,
1635 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1636 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1637 next_rid=next_rid, dc_rid=dc_rid)
1639 if serverrole == "active directory domain controller":
1641 # Set up group policies (domain policy and domain controller
1643 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1645 if not skip_sysvolacl:
1646 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1647 paths.wheel_gid, domainsid, names.dnsdomain,
1648 names.domaindn, lp, use_ntvfs)
1650 logger.info("Setting acl on sysvol skipped")
1652 secretsdb_self_join(secrets_ldb, domain=names.domain,
1653 realm=names.realm, dnsdomain=names.dnsdomain,
1654 netbiosname=names.netbiosname, domainsid=domainsid,
1655 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1657 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1658 # In future, this might be determined from some configuration
1659 kerberos_enctypes = str(ENC_ALL_TYPES)
1662 msg = ldb.Message(ldb.Dn(samdb,
1663 samdb.searchone("distinguishedName",
1664 expression="samAccountName=%s$" % names.netbiosname,
1665 scope=ldb.SCOPE_SUBTREE)))
1666 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1667 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1668 name="msDS-SupportedEncryptionTypes")
1670 except ldb.LdbError, (enum, estr):
1671 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1672 # It might be that this attribute does not exist in this schema
1675 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1676 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1677 dnspass=dnspass, os_level=dom_for_fun_level,
1678 targetdir=targetdir, site=DEFAULTSITE)
1680 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1681 attribute="objectGUID")
1682 assert isinstance(domainguid, str)
1684 lastProvisionUSNs = get_last_provision_usn(samdb)
1685 maxUSN = get_max_usn(samdb, str(names.rootdn))
1686 if lastProvisionUSNs is not None:
1687 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1689 set_provision_usn(samdb, 0, maxUSN, invocationid)
1691 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1692 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1693 { 'NTDSGUID' : names.ntdsguid })
1695 # fix any dangling GUIDs from the provision
1696 logger.info("Fixing provision GUIDs")
1697 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1699 samdb.transaction_start()
1701 # a small number of GUIDs are missing because of ordering issues in the
1703 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1704 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1705 scope=ldb.SCOPE_BASE,
1706 attrs=['defaultObjectCategory'])
1707 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1708 scope=ldb.SCOPE_ONELEVEL,
1709 attrs=['ipsecOwnersReference',
1710 'ipsecFilterReference',
1711 'ipsecISAKMPReference',
1712 'ipsecNegotiationPolicyReference',
1713 'ipsecNFAReference'])
1715 samdb.transaction_cancel()
1718 samdb.transaction_commit()
1722 "ROLE_STANDALONE": "standalone server",
1723 "ROLE_DOMAIN_MEMBER": "member server",
1724 "ROLE_DOMAIN_BDC": "active directory domain controller",
1725 "ROLE_DOMAIN_PDC": "active directory domain controller",
1726 "dc": "active directory domain controller",
1727 "member": "member server",
1728 "domain controller": "active directory domain controller",
1729 "active directory domain controller": "active directory domain controller",
1730 "member server": "member server",
1731 "standalone": "standalone server",
1732 "standalone server": "standalone server",
1736 def sanitize_server_role(role):
1737 """Sanitize a server role name.
1739 :param role: Server role
1740 :raise ValueError: If the role can not be interpreted
1741 :return: Sanitized server role (one of "member server",
1742 "active directory domain controller", "standalone server")
1745 return _ROLES_MAP[role]
1747 raise ValueError(role)
1750 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1752 """Create AD entries for the fake ypserver.
1754 This is needed for being able to manipulate posix attrs via ADUC.
1756 samdb.transaction_start()
1758 logger.info("Setting up fake yp server settings")
1759 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1760 "DOMAINDN": domaindn,
1761 "NETBIOSNAME": netbiosname,
1762 "NISDOMAIN": nisdomain,
1765 samdb.transaction_cancel()
1768 samdb.transaction_commit()
1771 def provision(logger, session_info, credentials, smbconf=None,
1772 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1773 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1774 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1775 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1776 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1777 dns_backend=None, dns_forwarder=None, dnspass=None,
1778 invocationid=None, machinepass=None, ntdsguid=None,
1779 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1780 serverrole=None, dom_for_fun_level=None, backend_type=None,
1781 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1782 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1783 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1786 :note: caution, this wipes all existing data!
1790 serverrole = sanitize_server_role(serverrole)
1792 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1794 if ldapadminpass is None:
1795 # Make a new, random password between Samba and it's LDAP server
1796 ldapadminpass = samba.generate_random_password(128, 255)
1798 if backend_type is None:
1799 backend_type = "ldb"
1801 if domainsid is None:
1802 domainsid = security.random_sid()
1804 domainsid = security.dom_sid(domainsid)
1806 root_uid = findnss_uid([root or "root"])
1807 nobody_uid = findnss_uid([nobody or "nobody"])
1808 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1810 wheel_gid = findnss_gid(["wheel", "adm"])
1812 wheel_gid = findnss_gid([wheel])
1814 bind_gid = findnss_gid(["bind", "named"])
1818 if targetdir is not None:
1819 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1820 elif smbconf is None:
1821 smbconf = samba.param.default_path()
1822 if not os.path.exists(os.path.dirname(smbconf)):
1823 os.makedirs(os.path.dirname(smbconf))
1825 server_services = []
1828 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1830 if dns_backend != "SAMBA_INTERNAL":
1831 server_services.append("-dns")
1833 if dns_forwarder is not None:
1834 global_param["dns forwarder"] = [dns_forwarder]
1837 server_services.append("+smb")
1838 server_services.append("-s3fs")
1839 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1841 if len(server_services) > 0:
1842 global_param["server services"] = server_services
1844 # only install a new smb.conf if there isn't one there already
1845 if os.path.exists(smbconf):
1846 # if Samba Team members can't figure out the weird errors
1847 # loading an empty smb.conf gives, then we need to be smarter.
1848 # Pretend it just didn't exist --abartlet
1849 f = open(smbconf, 'r')
1851 data = f.read().lstrip()
1854 if data is None or data == "":
1855 make_smbconf(smbconf, hostname, domain, realm,
1856 targetdir, serverrole=serverrole,
1857 eadb=useeadb, use_ntvfs=use_ntvfs,
1858 lp=lp, global_param=global_param)
1860 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1861 serverrole=serverrole,
1862 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1865 lp = samba.param.LoadParm()
1867 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1868 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1869 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1870 sitename=sitename, rootdn=rootdn)
1871 paths = provision_paths_from_lp(lp, names.dnsdomain)
1873 paths.bind_gid = bind_gid
1874 paths.root_uid = root_uid;
1875 paths.wheel_gid = wheel_gid
1878 logger.info("Looking up IPv4 addresses")
1879 hostips = interface_ips_v4(lp)
1880 if len(hostips) > 0:
1882 if len(hostips) > 1:
1883 logger.warning("More than one IPv4 address found. Using %s",
1885 if hostip == "127.0.0.1":
1888 logger.warning("No IPv4 address will be assigned")
1891 logger.info("Looking up IPv6 addresses")
1892 hostips = interface_ips_v6(lp, linklocal=False)
1894 hostip6 = hostips[0]
1895 if len(hostips) > 1:
1896 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1898 logger.warning("No IPv6 address will be assigned")
1900 names.hostip = hostip
1901 names.hostip6 = hostip6
1903 if serverrole is None:
1904 serverrole = lp.get("server role")
1906 if not os.path.exists(paths.private_dir):
1907 os.mkdir(paths.private_dir)
1908 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1909 os.mkdir(os.path.join(paths.private_dir, "tls"))
1910 if not os.path.exists(paths.state_dir):
1911 os.mkdir(paths.state_dir)
1913 if paths.sysvol and not os.path.exists(paths.sysvol):
1914 os.makedirs(paths.sysvol, 0775)
1916 if not use_ntvfs and serverrole == "active directory domain controller":
1917 s3conf = s3param.get_context()
1918 s3conf.load(lp.configfile)
1920 if paths.sysvol is None:
1921 raise MissingShareError("sysvol", paths.smbconf)
1923 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1926 smbd.set_simple_acl(file.name, 0755, wheel_gid)
1928 if not smbd.have_posix_acls():
1929 # This clue is only strictly correct for RPM and
1930 # Debian-like Linux systems, but hopefully other users
1931 # will get enough clue from it.
1932 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.")
1934 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1936 smbd.chown(file.name, root_uid, wheel_gid)
1938 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1942 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1944 schema = Schema(domainsid, invocationid=invocationid,
1945 schemadn=names.schemadn)
1947 if backend_type == "ldb":
1948 provision_backend = LDBBackend(backend_type, paths=paths,
1949 lp=lp, credentials=credentials,
1950 names=names, logger=logger)
1951 elif backend_type == "existing":
1952 # If support for this is ever added back, then the URI will need to be
1954 provision_backend = ExistingBackend(backend_type, paths=paths,
1955 lp=lp, credentials=credentials,
1956 names=names, logger=logger,
1957 ldap_backend_forced_uri=None)
1958 elif backend_type == "fedora-ds":
1959 provision_backend = FDSBackend(backend_type, paths=paths,
1960 lp=lp, credentials=credentials,
1961 names=names, logger=logger, domainsid=domainsid,
1962 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1963 slapd_path=slapd_path,
1965 elif backend_type == "openldap":
1966 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1967 lp=lp, credentials=credentials,
1968 names=names, logger=logger, domainsid=domainsid,
1969 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1970 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1972 raise ValueError("Unknown LDAP backend type selected")
1974 provision_backend.init()
1975 provision_backend.start()
1977 # only install a new shares config db if there is none
1978 if not os.path.exists(paths.shareconf):
1979 logger.info("Setting up share.ldb")
1980 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1981 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1983 logger.info("Setting up secrets.ldb")
1984 secrets_ldb = setup_secretsdb(paths,
1985 session_info=session_info,
1986 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1989 logger.info("Setting up the registry")
1990 setup_registry(paths.hklm, session_info, lp=lp)
1992 logger.info("Setting up the privileges database")
1993 setup_privileges(paths.privilege, session_info, lp=lp)
1995 logger.info("Setting up idmap db")
1996 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
1998 setup_name_mappings(idmap, sid=str(domainsid),
1999 root_uid=root_uid, nobody_uid=nobody_uid,
2000 users_gid=users_gid, wheel_gid=wheel_gid)
2002 logger.info("Setting up SAM db")
2003 samdb = setup_samdb(paths.samdb, session_info,
2004 provision_backend, lp, names, logger=logger,
2005 serverrole=serverrole,
2006 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2008 if serverrole == "active directory domain controller":
2009 if paths.netlogon is None:
2010 raise MissingShareError("netlogon", paths.smbconf)
2012 if paths.sysvol is None:
2013 raise MissingShareError("sysvol", paths.smbconf)
2015 if not os.path.isdir(paths.netlogon):
2016 os.makedirs(paths.netlogon, 0755)
2018 if adminpass is None:
2019 adminpass = samba.generate_random_password(12, 32)
2020 adminpass_generated = True
2022 adminpass_generated = False
2024 if samdb_fill == FILL_FULL:
2025 provision_fill(samdb, secrets_ldb, logger, names, paths,
2026 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2027 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2028 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2029 krbtgtpass=krbtgtpass, domainguid=domainguid,
2030 policyguid=policyguid, policyguid_dc=policyguid_dc,
2031 invocationid=invocationid, machinepass=machinepass,
2032 ntdsguid=ntdsguid, dns_backend=dns_backend,
2033 dnspass=dnspass, serverrole=serverrole,
2034 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2035 lp=lp, use_ntvfs=use_ntvfs,
2036 skip_sysvolacl=skip_sysvolacl)
2038 create_krb5_conf(paths.krb5conf,
2039 dnsdomain=names.dnsdomain, hostname=names.hostname,
2041 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2042 "generated at %s", paths.krb5conf)
2044 if serverrole == "active directory domain controller":
2045 create_dns_update_list(lp, logger, paths)
2047 backend_result = provision_backend.post_setup()
2048 provision_backend.shutdown()
2050 create_phpldapadmin_config(paths.phpldapadminconfig,
2053 secrets_ldb.transaction_cancel()
2056 # Now commit the secrets.ldb to disk
2057 secrets_ldb.transaction_commit()
2059 # the commit creates the dns.keytab, now chown it
2060 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2061 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2063 os.chmod(dns_keytab_path, 0640)
2064 os.chown(dns_keytab_path, -1, paths.bind_gid)
2066 if not os.environ.has_key('SAMBA_SELFTEST'):
2067 logger.info("Failed to chown %s to bind gid %u",
2068 dns_keytab_path, paths.bind_gid)
2070 result = ProvisionResult()
2071 result.server_role = serverrole
2072 result.domaindn = domaindn
2073 result.paths = paths
2074 result.names = names
2076 result.samdb = samdb
2077 result.idmap = idmap
2078 result.domainsid = str(domainsid)
2080 if samdb_fill == FILL_FULL:
2081 result.adminpass_generated = adminpass_generated
2082 result.adminpass = adminpass
2084 result.adminpass_generated = False
2085 result.adminpass = None
2087 result.backend_result = backend_result
2090 provision_fake_ypserver(logger=logger, samdb=samdb,
2091 domaindn=names.domaindn, netbiosname=names.netbiosname,
2092 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2097 def provision_become_dc(smbconf=None, targetdir=None,
2098 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2099 serverdn=None, domain=None, hostname=None, domainsid=None,
2100 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2101 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2102 dns_backend=None, root=None, nobody=None, users=None, wheel=None,
2103 backup=None, serverrole=None, ldap_backend=None,
2104 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2106 logger = logging.getLogger("provision")
2107 samba.set_debug_level(debuglevel)
2109 res = provision(logger, system_session(), None,
2110 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2111 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2112 configdn=configdn, serverdn=serverdn, domain=domain,
2113 hostname=hostname, hostip=None, domainsid=domainsid,
2114 machinepass=machinepass,
2115 serverrole="active directory domain controller",
2116 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2117 use_ntvfs=use_ntvfs)
2118 res.lp.set("debuglevel", str(debuglevel))
2122 def create_phpldapadmin_config(path, ldapi_uri):
2123 """Create a PHP LDAP admin configuration file.
2125 :param path: Path to write the configuration to.
2127 setup_file(setup_path("phpldapadmin-config.php"), path,
2128 {"S4_LDAPI_URI": ldapi_uri})
2131 def create_krb5_conf(path, dnsdomain, hostname, realm):
2132 """Write out a file containing zone statements suitable for inclusion in a
2133 named.conf file (including GSS-TSIG configuration).
2135 :param path: Path of the new named.conf file.
2136 :param dnsdomain: DNS Domain name
2137 :param hostname: Local hostname
2138 :param realm: Realm name
2140 setup_file(setup_path("krb5.conf"), path, {
2141 "DNSDOMAIN": dnsdomain,
2142 "HOSTNAME": hostname,
2147 class ProvisioningError(Exception):
2148 """A generic provision error."""
2150 def __init__(self, value):
2154 return "ProvisioningError: " + self.value
2157 class InvalidNetbiosName(Exception):
2158 """A specified name was not a valid NetBIOS name."""
2160 def __init__(self, name):
2161 super(InvalidNetbiosName, self).__init__(
2162 "The name '%r' is not a valid NetBIOS name" % name)
2165 class MissingShareError(ProvisioningError):
2167 def __init__(self, name, smbconf):
2168 super(MissingShareError, self).__init__(
2169 "Existing smb.conf does not have a [%s] share, but you are "
2170 "configuring a DC. Please remove %s or add the share manually." %