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
45 from samba.auth import system_session, admin_session
47 from samba.samba3 import smbd, passdb
48 from samba.samba3 import param as s3param
49 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
53 check_all_substituted,
54 is_valid_netbios_char,
60 from samba.dcerpc import security, misc
61 from samba.dcerpc.misc import (
65 from samba.dsdb import (
66 DS_DOMAIN_FUNCTION_2003,
67 DS_DOMAIN_FUNCTION_2008_R2,
70 from samba.idmap import IDmapDB
71 from samba.ms_display_specifiers import read_ms_ldif
72 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
73 from samba.ndr import ndr_pack, ndr_unpack
74 from samba.provision.backend import (
80 from samba.descriptor import (
82 get_config_descriptor,
83 get_config_partitions_descriptor,
84 get_config_sites_descriptor,
85 get_config_ntds_quotas_descriptor,
86 get_config_delete_protected1_descriptor,
87 get_config_delete_protected1wd_descriptor,
88 get_config_delete_protected2_descriptor,
89 get_domain_descriptor,
90 get_domain_infrastructure_descriptor,
91 get_domain_builtin_descriptor,
92 get_domain_computers_descriptor,
93 get_domain_users_descriptor,
94 get_domain_controllers_descriptor,
95 get_domain_delete_protected1_descriptor,
96 get_domain_delete_protected2_descriptor,
97 get_dns_partition_descriptor,
98 get_dns_forest_microsoft_dns_descriptor,
99 get_dns_domain_microsoft_dns_descriptor,
101 from samba.provision.common import (
110 from samba.provision.sambadns import (
113 create_dns_update_list
117 import samba.registry
118 from samba.schema import Schema
119 from samba.samdb import SamDB
120 from samba.dbchecker import dbcheck
123 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
124 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
125 DEFAULTSITE = "Default-First-Site-Name"
126 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
129 class ProvisionPaths(object):
132 self.shareconf = None
143 self.dns_keytab = None
146 self.private_dir = None
147 self.state_dir = None
150 class ProvisionNames(object):
158 self.dnsforestdn = None
159 self.dnsdomaindn = None
160 self.ldapmanagerdn = None
161 self.dnsdomain = None
163 self.netbiosname = None
168 self.domainsid = None
169 self.forestsid = None
170 self.domainguid = None
174 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
176 """Get key provision parameters (realm, domain, ...) from a given provision
178 :param samdb: An LDB object connected to the sam.ldb file
179 :param secretsdb: An LDB object connected to the secrets.ldb file
180 :param idmapdb: An LDB object connected to the idmap.ldb file
181 :param paths: A list of path to provision object
182 :param smbconf: Path to the smb.conf file
183 :param lp: A LoadParm object
184 :return: A list of key provision parameters
186 names = ProvisionNames()
187 names.adminpass = None
189 # NT domain, kerberos realm, root dn, domain dn, domain dns name
190 names.domain = string.upper(lp.get("workgroup"))
191 names.realm = lp.get("realm")
192 names.dnsdomain = names.realm.lower()
193 basedn = samba.dn_from_dns_name(names.dnsdomain)
194 names.realm = string.upper(names.realm)
196 # Get the netbiosname first (could be obtained from smb.conf in theory)
197 res = secretsdb.search(expression="(flatname=%s)" %
198 names.domain,base="CN=Primary Domains",
199 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
200 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
202 names.smbconf = smbconf
204 # That's a bit simplistic but it's ok as long as we have only 3
206 current = samdb.search(expression="(objectClass=*)",
207 base="", scope=ldb.SCOPE_BASE,
208 attrs=["defaultNamingContext", "schemaNamingContext",
209 "configurationNamingContext","rootDomainNamingContext",
212 names.configdn = current[0]["configurationNamingContext"][0]
213 names.schemadn = current[0]["schemaNamingContext"][0]
214 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
215 current[0]["defaultNamingContext"][0]))):
216 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
217 "is not the same ..." % (paths.samdb,
218 str(current[0]["defaultNamingContext"][0]),
219 paths.smbconf, basedn)))
221 names.domaindn=current[0]["defaultNamingContext"][0]
222 names.rootdn=current[0]["rootDomainNamingContext"][0]
223 names.ncs=current[0]["namingContexts"]
224 names.dnsforestdn = None
225 names.dnsdomaindn = None
227 for i in range(0, len(names.ncs)):
230 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
231 if nc == dnsforestdn:
232 names.dnsforestdn = dnsforestdn
235 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
236 if nc == dnsdomaindn:
237 names.dnsdomaindn = dnsdomaindn
241 res3 = samdb.search(expression="(objectClass=site)",
242 base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
243 names.sitename = str(res3[0]["cn"])
245 # dns hostname and server dn
246 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
247 base="OU=Domain Controllers,%s" % basedn,
248 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
250 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
252 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
254 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
255 attrs=[], base=names.configdn)
256 names.serverdn = str(server_res[0].dn)
258 # invocation id/objectguid
259 res5 = samdb.search(expression="(objectClass=*)",
260 base="CN=NTDS Settings,%s" % str(names.serverdn),
261 scope=ldb.SCOPE_BASE,
262 attrs=["invocationID", "objectGUID"])
263 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
264 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
267 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
268 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
269 "objectSid","msDS-Behavior-Version" ])
270 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
271 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
272 names.forestsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
273 if res6[0].get("msDS-Behavior-Version") is None or \
274 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
275 names.domainlevel = DS_DOMAIN_FUNCTION_2000
277 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
280 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
281 base="CN=Policies,CN=System," + basedn,
282 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
283 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
285 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
286 base="CN=Policies,CN=System," + basedn,
287 scope=ldb.SCOPE_ONELEVEL,
288 attrs=["cn","displayName"])
290 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
292 names.policyid_dc = None
294 res9 = idmapdb.search(expression="(cn=%s-%s)" %
295 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
296 attrs=["xidNumber", "type"])
298 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
299 if res9[0]["type"][0] == "ID_TYPE_BOTH":
300 names.root_gid = res9[0]["xidNumber"][0]
302 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
304 res10 = samdb.search(expression="(samaccountname=dns)",
305 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
306 controls=["search_options:1:2"])
308 has_legacy_dns_account = True
310 has_legacy_dns_account = False
312 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
313 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
314 controls=["search_options:1:2"])
316 has_dns_account = True
318 has_dns_account = False
320 if names.dnsdomaindn is not None:
322 names.dns_backend = 'BIND9_DLZ'
324 names.dns_backend = 'SAMBA_INTERNAL'
325 elif has_dns_account or has_legacy_dns_account:
326 names.dns_backend = 'BIND9_FLATFILE'
328 names.dns_backend = 'NONE'
330 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
331 names.name_map['DnsAdmins'] = str(dns_admins_sid)
336 def update_provision_usn(samdb, low, high, id, replace=False):
337 """Update the field provisionUSN in sam.ldb
339 This field is used to track range of USN modified by provision and
341 This value is used afterward by next provision to figure out if
342 the field have been modified since last provision.
344 :param samdb: An LDB object connect to sam.ldb
345 :param low: The lowest USN modified by this upgrade
346 :param high: The highest USN modified by this upgrade
347 :param id: The invocation id of the samba's dc
348 :param replace: A boolean indicating if the range should replace any
349 existing one or appended (default)
354 entry = samdb.search(base="@PROVISION",
355 scope=ldb.SCOPE_BASE,
356 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
357 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
358 if not re.search(';', e):
359 e = "%s;%s" % (e, id)
362 tab.append("%s-%s;%s" % (low, high, id))
363 delta = ldb.Message()
364 delta.dn = ldb.Dn(samdb, "@PROVISION")
365 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
366 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
367 entry = samdb.search(expression='provisionnerID=*',
368 base="@PROVISION", scope=ldb.SCOPE_BASE,
369 attrs=["provisionnerID"])
370 if len(entry) == 0 or len(entry[0]) == 0:
371 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
375 def set_provision_usn(samdb, low, high, id):
376 """Set the field provisionUSN in sam.ldb
377 This field is used to track range of USN modified by provision and
379 This value is used afterward by next provision to figure out if
380 the field have been modified since last provision.
382 :param samdb: An LDB object connect to sam.ldb
383 :param low: The lowest USN modified by this upgrade
384 :param high: The highest USN modified by this upgrade
385 :param id: The invocationId of the provision"""
388 tab.append("%s-%s;%s" % (low, high, id))
390 delta = ldb.Message()
391 delta.dn = ldb.Dn(samdb, "@PROVISION")
392 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
393 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
397 def get_max_usn(samdb,basedn):
398 """ This function return the biggest USN present in the provision
400 :param samdb: A LDB object pointing to the sam.ldb
401 :param basedn: A string containing the base DN of the provision
403 :return: The biggest USN in the provision"""
405 res = samdb.search(expression="objectClass=*",base=basedn,
406 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
407 controls=["search_options:1:2",
408 "server_sort:1:1:uSNChanged",
409 "paged_results:1:1"])
410 return res[0]["uSNChanged"]
413 def get_last_provision_usn(sam):
414 """Get USNs ranges modified by a provision or an upgradeprovision
416 :param sam: An LDB object pointing to the sam.ldb
417 :return: a dictionary which keys are invocation id and values are an array
418 of integer representing the different ranges
421 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
422 base="@PROVISION", scope=ldb.SCOPE_BASE,
423 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
424 except ldb.LdbError, (ecode, emsg):
425 if ecode == ldb.ERR_NO_SUCH_OBJECT:
432 if entry[0].get("provisionnerID"):
433 for e in entry[0]["provisionnerID"]:
435 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
436 tab1 = str(r).split(';')
441 if (len(myids) > 0 and id not in myids):
443 tab2 = p.split(tab1[0])
444 if range.get(id) is None:
446 range[id].append(tab2[0])
447 range[id].append(tab2[1])
453 class ProvisionResult(object):
454 """Result of a provision.
456 :ivar server_role: The server role
457 :ivar paths: ProvisionPaths instance
458 :ivar domaindn: The domain dn, as string
462 self.server_role = None
469 self.domainsid = None
470 self.adminpass_generated = None
471 self.adminpass = None
472 self.backend_result = None
474 def report_logger(self, logger):
475 """Report this provision result to a logger."""
477 "Once the above files are installed, your Samba4 server will "
479 if self.adminpass_generated:
480 logger.info("Admin password: %s", self.adminpass)
481 logger.info("Server Role: %s", self.server_role)
482 logger.info("Hostname: %s", self.names.hostname)
483 logger.info("NetBIOS Domain: %s", self.names.domain)
484 logger.info("DNS Domain: %s", self.names.dnsdomain)
485 logger.info("DOMAIN SID: %s", self.domainsid)
487 if self.backend_result:
488 self.backend_result.report_logger(logger)
491 def check_install(lp, session_info, credentials):
492 """Check whether the current install seems ok.
494 :param lp: Loadparm context
495 :param session_info: Session information
496 :param credentials: Credentials
498 if lp.get("realm") == "":
499 raise Exception("Realm empty")
500 samdb = Ldb(lp.samdb_url(), session_info=session_info,
501 credentials=credentials, lp=lp)
502 if len(samdb.search("(cn=Administrator)")) != 1:
503 raise ProvisioningError("No administrator account found")
506 def findnss(nssfn, names):
507 """Find a user or group from a list of possibilities.
509 :param nssfn: NSS Function to try (should raise KeyError if not found)
510 :param names: Names to check.
511 :return: Value return by first names list.
518 raise KeyError("Unable to find user/group in %r" % names)
521 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
522 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
525 def provision_paths_from_lp(lp, dnsdomain):
526 """Set the default paths for provisioning.
528 :param lp: Loadparm context.
529 :param dnsdomain: DNS Domain name
531 paths = ProvisionPaths()
532 paths.private_dir = lp.get("private dir")
533 paths.state_dir = lp.get("state directory")
535 # This is stored without path prefix for the "privateKeytab" attribute in
536 # "secrets_dns.ldif".
537 paths.dns_keytab = "dns.keytab"
538 paths.keytab = "secrets.keytab"
540 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
541 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
542 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
543 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
544 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
545 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
546 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
547 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
548 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
549 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
550 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
551 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
552 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
553 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
554 paths.hklm = "hklm.ldb"
555 paths.hkcr = "hkcr.ldb"
556 paths.hkcu = "hkcu.ldb"
557 paths.hku = "hku.ldb"
558 paths.hkpd = "hkpd.ldb"
559 paths.hkpt = "hkpt.ldb"
560 paths.sysvol = lp.get("path", "sysvol")
561 paths.netlogon = lp.get("path", "netlogon")
562 paths.smbconf = lp.configfile
566 def determine_netbios_name(hostname):
567 """Determine a netbios name from a hostname."""
568 # remove forbidden chars and force the length to be <16
569 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
570 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
573 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
574 serverrole=None, rootdn=None, domaindn=None, configdn=None,
575 schemadn=None, serverdn=None, sitename=None,
576 domain_names_forced=False):
577 """Guess configuration settings to use."""
580 hostname = socket.gethostname().split(".")[0]
582 netbiosname = lp.get("netbios name")
583 if netbiosname is None:
584 netbiosname = determine_netbios_name(hostname)
585 netbiosname = netbiosname.upper()
586 if not valid_netbios_name(netbiosname):
587 raise InvalidNetbiosName(netbiosname)
589 if dnsdomain is None:
590 dnsdomain = lp.get("realm")
591 if dnsdomain is None or dnsdomain == "":
592 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
594 dnsdomain = dnsdomain.lower()
596 if serverrole is None:
597 serverrole = lp.get("server role")
598 if serverrole is None:
599 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
601 serverrole = serverrole.lower()
603 realm = dnsdomain.upper()
605 if lp.get("realm") == "":
606 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
608 if lp.get("realm").upper() != realm:
609 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(), lp.configfile, realm))
611 if lp.get("server role").lower() != serverrole:
612 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))
614 if serverrole == "active directory domain controller":
616 # This will, for better or worse, default to 'WORKGROUP'
617 domain = lp.get("workgroup")
618 domain = domain.upper()
620 if lp.get("workgroup").upper() != domain:
621 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))
624 domaindn = samba.dn_from_dns_name(dnsdomain)
626 if domain == netbiosname:
627 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
631 domaindn = "DC=" + netbiosname
633 if not valid_netbios_name(domain):
634 raise InvalidNetbiosName(domain)
636 if hostname.upper() == realm:
637 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
638 if netbiosname.upper() == realm:
639 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
640 if domain == realm and not domain_names_forced:
641 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
647 configdn = "CN=Configuration," + rootdn
649 schemadn = "CN=Schema," + configdn
652 sitename = DEFAULTSITE
654 names = ProvisionNames()
655 names.rootdn = rootdn
656 names.domaindn = domaindn
657 names.configdn = configdn
658 names.schemadn = schemadn
659 names.ldapmanagerdn = "CN=Manager," + rootdn
660 names.dnsdomain = dnsdomain
661 names.domain = domain
663 names.netbiosname = netbiosname
664 names.hostname = hostname
665 names.sitename = sitename
666 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
667 netbiosname, sitename, configdn)
672 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
673 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
675 """Create a new smb.conf file based on a couple of basic settings.
677 assert smbconf is not None
680 hostname = socket.gethostname().split(".")[0]
682 netbiosname = determine_netbios_name(hostname)
684 if serverrole is None:
685 serverrole = "standalone server"
687 assert domain is not None
688 domain = domain.upper()
690 assert realm is not None
691 realm = realm.upper()
694 "netbios name": netbiosname,
697 "server role": serverrole,
701 lp = samba.param.LoadParm()
702 #Load non-existent file
703 if os.path.exists(smbconf):
706 if global_param is not None:
707 for ent in global_param:
708 if global_param[ent] is not None:
709 global_settings[ent] = " ".join(global_param[ent])
711 if targetdir is not None:
712 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
713 global_settings["lock dir"] = os.path.abspath(targetdir)
714 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
715 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
717 lp.set("lock dir", os.path.abspath(targetdir))
718 lp.set("state directory", global_settings["state directory"])
719 lp.set("cache directory", global_settings["cache directory"])
722 if use_ntvfs and not lp.get("posix:eadb"):
723 if targetdir is not None:
724 privdir = os.path.join(targetdir, "private")
726 privdir = lp.get("private dir")
727 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
728 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
729 if targetdir is not None:
730 statedir = os.path.join(targetdir, "state")
732 statedir = lp.get("state directory")
733 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
736 if serverrole == "active directory domain controller":
737 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
738 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
741 global_settings["passdb backend"] = "samba_dsdb"
743 f = open(smbconf, 'w')
745 f.write("[globals]\n")
746 for key, val in global_settings.iteritems():
747 f.write("\t%s = %s\n" % (key, val))
750 for name, path in shares.iteritems():
751 f.write("[%s]\n" % name)
752 f.write("\tpath = %s\n" % path)
753 f.write("\tread only = no\n")
757 # reload the smb.conf
760 # and dump it without any values that are the default
761 # this ensures that any smb.conf parameters that were set
762 # on the provision/join command line are set in the resulting smb.conf
763 f = open(smbconf, mode='w')
770 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
771 users_gid, root_gid):
772 """setup reasonable name mappings for sam names to unix names.
774 :param samdb: SamDB object.
775 :param idmap: IDmap db object.
776 :param sid: The domain sid.
777 :param domaindn: The domain DN.
778 :param root_uid: uid of the UNIX root user.
779 :param nobody_uid: uid of the UNIX nobody user.
780 :param users_gid: gid of the UNIX users group.
781 :param root_gid: gid of the UNIX root group.
783 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
785 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
786 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
789 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
790 provision_backend, names, serverrole,
792 """Setup the partitions for the SAM database.
794 Alternatively, provision() may call this, and then populate the database.
796 :note: This will wipe the Sam Database!
798 :note: This function always removes the local SAM LDB file. The erase
799 parameter controls whether to erase the existing data, which
800 may not be stored locally but in LDAP.
803 assert session_info is not None
805 # We use options=["modules:"] to stop the modules loading - we
806 # just want to wipe and re-initialise the database, not start it up
809 os.unlink(samdb_path)
813 samdb = Ldb(url=samdb_path, session_info=session_info,
814 lp=lp, options=["modules:"])
816 ldap_backend_line = "# No LDAP backend"
817 if provision_backend.type != "ldb":
818 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
820 samdb.transaction_start()
822 logger.info("Setting up sam.ldb partitions and settings")
823 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
824 "LDAP_BACKEND_LINE": ldap_backend_line
828 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
829 "BACKEND_TYPE": provision_backend.type,
830 "SERVER_ROLE": serverrole
833 logger.info("Setting up sam.ldb rootDSE")
834 setup_samdb_rootdse(samdb, names)
836 samdb.transaction_cancel()
839 samdb.transaction_commit()
842 def secretsdb_self_join(secretsdb, domain,
843 netbiosname, machinepass, domainsid=None,
844 realm=None, dnsdomain=None,
846 key_version_number=1,
847 secure_channel_type=SEC_CHAN_WKSTA):
848 """Add domain join-specific bits to a secrets database.
850 :param secretsdb: Ldb Handle to the secrets database
851 :param machinepass: Machine password
853 attrs = ["whenChanged",
860 if realm is not None:
861 if dnsdomain is None:
862 dnsdomain = realm.lower()
863 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
866 shortname = netbiosname.lower()
868 # We don't need to set msg["flatname"] here, because rdn_name will handle
869 # it, and it causes problems for modifies anyway
870 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
871 msg["secureChannelType"] = [str(secure_channel_type)]
872 msg["objectClass"] = ["top", "primaryDomain"]
873 if dnsname is not None:
874 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
875 msg["realm"] = [realm]
876 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
877 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
878 msg["privateKeytab"] = ["secrets.keytab"]
880 msg["secret"] = [machinepass.encode('utf-8')]
881 msg["samAccountName"] = ["%s$" % netbiosname]
882 msg["secureChannelType"] = [str(secure_channel_type)]
883 if domainsid is not None:
884 msg["objectSid"] = [ndr_pack(domainsid)]
886 # This complex expression tries to ensure that we don't have more
887 # than one record for this SID, realm or netbios domain at a time,
888 # but we don't delete the old record that we are about to modify,
889 # because that would delete the keytab and previous password.
890 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
891 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
892 scope=ldb.SCOPE_ONELEVEL)
895 secretsdb.delete(del_msg.dn)
897 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
900 msg["priorSecret"] = [res[0]["secret"][0]]
901 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
904 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
909 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
915 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
916 secretsdb.modify(msg)
917 secretsdb.rename(res[0].dn, msg.dn)
919 spn = [ 'HOST/%s' % shortname ]
920 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
921 # we are a domain controller then we add servicePrincipalName
922 # entries for the keytab code to update.
923 spn.extend([ 'HOST/%s' % dnsname ])
924 msg["servicePrincipalName"] = spn
929 def setup_secretsdb(paths, session_info, backend_credentials, lp):
930 """Setup the secrets database.
932 :note: This function does not handle exceptions and transaction on purpose,
933 it's up to the caller to do this job.
935 :param path: Path to the secrets database.
936 :param session_info: Session info.
937 :param credentials: Credentials
938 :param lp: Loadparm context
939 :return: LDB handle for the created secrets database
941 if os.path.exists(paths.secrets):
942 os.unlink(paths.secrets)
944 keytab_path = os.path.join(paths.private_dir, paths.keytab)
945 if os.path.exists(keytab_path):
946 os.unlink(keytab_path)
948 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
949 if os.path.exists(dns_keytab_path):
950 os.unlink(dns_keytab_path)
954 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
956 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
957 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
958 secrets_ldb.transaction_start()
960 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
962 if (backend_credentials is not None and
963 backend_credentials.authentication_requested()):
964 if backend_credentials.get_bind_dn() is not None:
965 setup_add_ldif(secrets_ldb,
966 setup_path("secrets_simple_ldap.ldif"), {
967 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
968 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
971 setup_add_ldif(secrets_ldb,
972 setup_path("secrets_sasl_ldap.ldif"), {
973 "LDAPADMINUSER": backend_credentials.get_username(),
974 "LDAPADMINREALM": backend_credentials.get_realm(),
975 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
978 secrets_ldb.transaction_cancel()
983 def setup_privileges(path, session_info, lp):
984 """Setup the privileges database.
986 :param path: Path to the privileges database.
987 :param session_info: Session info.
988 :param credentials: Credentials
989 :param lp: Loadparm context
990 :return: LDB handle for the created secrets database
992 if os.path.exists(path):
994 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
995 privilege_ldb.erase()
996 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
999 def setup_registry(path, session_info, lp):
1000 """Setup the registry.
1002 :param path: Path to the registry database
1003 :param session_info: Session information
1004 :param credentials: Credentials
1005 :param lp: Loadparm context
1007 reg = samba.registry.Registry()
1008 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1009 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1010 provision_reg = setup_path("provision.reg")
1011 assert os.path.exists(provision_reg)
1012 reg.diff_apply(provision_reg)
1015 def setup_idmapdb(path, session_info, lp):
1016 """Setup the idmap database.
1018 :param path: path to the idmap database
1019 :param session_info: Session information
1020 :param credentials: Credentials
1021 :param lp: Loadparm context
1023 if os.path.exists(path):
1026 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1028 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1032 def setup_samdb_rootdse(samdb, names):
1033 """Setup the SamDB rootdse.
1035 :param samdb: Sam Database handle
1037 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1038 "SCHEMADN": names.schemadn,
1039 "DOMAINDN": names.domaindn,
1040 "ROOTDN" : names.rootdn,
1041 "CONFIGDN": names.configdn,
1042 "SERVERDN": names.serverdn,
1046 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1047 dns_backend, dnspass, domainsid, next_rid, invocationid,
1048 policyguid, policyguid_dc,
1049 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1050 """Join a host to its own domain."""
1051 assert isinstance(invocationid, str)
1052 if ntdsguid is not None:
1053 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1060 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1061 "CONFIGDN": names.configdn,
1062 "SCHEMADN": names.schemadn,
1063 "DOMAINDN": names.domaindn,
1064 "SERVERDN": names.serverdn,
1065 "INVOCATIONID": invocationid,
1066 "NETBIOSNAME": names.netbiosname,
1067 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1068 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1069 "DOMAINSID": str(domainsid),
1070 "DCRID": str(dc_rid),
1071 "SAMBA_VERSION_STRING": version,
1072 "NTDSGUID": ntdsguid_line,
1073 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1074 domainControllerFunctionality),
1075 "RIDALLOCATIONSTART": str(next_rid + 100),
1076 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1078 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1079 "POLICYGUID": policyguid,
1080 "POLICYGUID_DC": policyguid_dc,
1081 "DNSDOMAIN": names.dnsdomain,
1082 "DOMAINDN": names.domaindn})
1084 # If we are setting up a subdomain, then this has been replicated in, so we
1085 # don't need to add it
1086 if fill == FILL_FULL:
1087 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1088 "CONFIGDN": names.configdn,
1089 "SCHEMADN": names.schemadn,
1090 "DOMAINDN": names.domaindn,
1091 "SERVERDN": names.serverdn,
1092 "INVOCATIONID": invocationid,
1093 "NETBIOSNAME": names.netbiosname,
1094 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1095 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1096 "DOMAINSID": str(domainsid),
1097 "DCRID": str(dc_rid),
1098 "SAMBA_VERSION_STRING": version,
1099 "NTDSGUID": ntdsguid_line,
1100 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1101 domainControllerFunctionality)})
1103 # Setup fSMORoleOwner entries to point at the newly created DC entry
1104 setup_modify_ldif(samdb,
1105 setup_path("provision_self_join_modify_config.ldif"), {
1106 "CONFIGDN": names.configdn,
1107 "SCHEMADN": names.schemadn,
1108 "DEFAULTSITE": names.sitename,
1109 "NETBIOSNAME": names.netbiosname,
1110 "SERVERDN": names.serverdn,
1113 system_session_info = system_session()
1114 samdb.set_session_info(system_session_info)
1115 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1116 # modify a serverReference under cn=config when we are a subdomain, we must
1117 # be system due to ACLs
1118 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1119 "DOMAINDN": names.domaindn,
1120 "SERVERDN": names.serverdn,
1121 "NETBIOSNAME": names.netbiosname,
1124 samdb.set_session_info(admin_session_info)
1126 if dns_backend != "SAMBA_INTERNAL":
1127 # This is Samba4 specific and should be replaced by the correct
1128 # DNS AD-style setup
1129 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1130 "DNSDOMAIN": names.dnsdomain,
1131 "DOMAINDN": names.domaindn,
1132 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1133 "HOSTNAME" : names.hostname,
1134 "DNSNAME" : '%s.%s' % (
1135 names.netbiosname.lower(), names.dnsdomain.lower())
1139 def getpolicypath(sysvolpath, dnsdomain, guid):
1140 """Return the physical path of policy given its guid.
1142 :param sysvolpath: Path to the sysvol folder
1143 :param dnsdomain: DNS name of the AD domain
1144 :param guid: The GUID of the policy
1145 :return: A string with the complete path to the policy folder
1148 guid = "{%s}" % guid
1149 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1153 def create_gpo_struct(policy_path):
1154 if not os.path.exists(policy_path):
1155 os.makedirs(policy_path, 0775)
1156 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1158 f.write("[General]\r\nVersion=0")
1161 p = os.path.join(policy_path, "MACHINE")
1162 if not os.path.exists(p):
1163 os.makedirs(p, 0775)
1164 p = os.path.join(policy_path, "USER")
1165 if not os.path.exists(p):
1166 os.makedirs(p, 0775)
1169 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1170 """Create the default GPO for a domain
1172 :param sysvolpath: Physical path for the sysvol folder
1173 :param dnsdomain: DNS domain name of the AD domain
1174 :param policyguid: GUID of the default domain policy
1175 :param policyguid_dc: GUID of the default domain controler policy
1177 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1178 create_gpo_struct(policy_path)
1180 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1181 create_gpo_struct(policy_path)
1184 def setup_samdb(path, session_info, provision_backend, lp, names,
1185 logger, fill, serverrole, schema, am_rodc=False):
1186 """Setup a complete SAM Database.
1188 :note: This will wipe the main SAM database file!
1191 # Also wipes the database
1192 setup_samdb_partitions(path, logger=logger, lp=lp,
1193 provision_backend=provision_backend, session_info=session_info,
1194 names=names, serverrole=serverrole)
1196 # Load the database, but don's load the global schema and don't connect
1198 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1199 credentials=provision_backend.credentials, lp=lp,
1200 global_schema=False, am_rodc=am_rodc)
1202 logger.info("Pre-loading the Samba 4 and AD schema")
1204 # Load the schema from the one we computed earlier
1205 samdb.set_schema(schema, write_indices_and_attributes=False)
1207 # Set the NTDS settings DN manually - in order to have it already around
1208 # before the provisioned tree exists and we connect
1209 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1211 # And now we can connect to the DB - the schema won't be loaded from the
1215 except ldb.LdbError, (num, string_error):
1216 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1217 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1221 # But we have to give it one more kick to have it use the schema
1222 # during provision - it needs, now that it is connected, to write
1223 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1224 samdb.set_schema(schema, write_indices_and_attributes=True)
1229 def fill_samdb(samdb, lp, names, logger, policyguid,
1230 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1231 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1232 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1234 if next_rid is None:
1237 # Provision does not make much sense values larger than 1000000000
1238 # as the upper range of the rIDAvailablePool is 1073741823 and
1239 # we don't want to create a domain that cannot allocate rids.
1240 if next_rid < 1000 or next_rid > 1000000000:
1241 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1242 error += "the valid range is %u-%u. The default is %u." % (
1243 1000, 1000000000, 1000)
1244 raise ProvisioningError(error)
1246 # ATTENTION: Do NOT change these default values without discussion with the
1247 # team and/or release manager. They have a big impact on the whole program!
1248 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1250 if dom_for_fun_level is None:
1251 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1253 if dom_for_fun_level > domainControllerFunctionality:
1254 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!")
1256 domainFunctionality = dom_for_fun_level
1257 forestFunctionality = dom_for_fun_level
1259 # Set the NTDS settings DN manually - in order to have it already around
1260 # before the provisioned tree exists and we connect
1261 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1263 # Set the domain functionality levels onto the database.
1264 # Various module (the password_hash module in particular) need
1265 # to know what level of AD we are emulating.
1267 # These will be fixed into the database via the database
1268 # modifictions below, but we need them set from the start.
1269 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1270 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1271 samdb.set_opaque_integer("domainControllerFunctionality",
1272 domainControllerFunctionality)
1274 samdb.set_domain_sid(str(names.domainsid))
1275 samdb.set_invocation_id(invocationid)
1277 logger.info("Adding DomainDN: %s" % names.domaindn)
1279 # impersonate domain admin
1280 admin_session_info = admin_session(lp, str(names.domainsid))
1281 samdb.set_session_info(admin_session_info)
1282 if names.domainguid is not None:
1283 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1285 domainguid_line = ""
1287 descr = b64encode(get_domain_descriptor(names.domainsid))
1288 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1289 "DOMAINDN": names.domaindn,
1290 "DOMAINSID": str(names.domainsid),
1291 "DESCRIPTOR": descr,
1292 "DOMAINGUID": domainguid_line
1295 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1296 "DOMAINDN": names.domaindn,
1297 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1298 "NEXTRID": str(next_rid),
1299 "DEFAULTSITE": names.sitename,
1300 "CONFIGDN": names.configdn,
1301 "POLICYGUID": policyguid,
1302 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1303 "SAMBA_VERSION_STRING": version
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 logger.info("Adding configuration container")
1309 descr = b64encode(get_config_descriptor(names.domainsid))
1310 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1311 "CONFIGDN": names.configdn,
1312 "DESCRIPTOR": descr,
1315 # The LDIF here was created when the Schema object was constructed
1316 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1317 logger.info("Setting up sam.ldb schema")
1318 samdb.add_ldif(schema.schema_dn_add,
1319 controls=["relax:0", ignore_checks_oid])
1320 samdb.modify_ldif(schema.schema_dn_modify,
1321 controls=[ignore_checks_oid])
1322 samdb.write_prefixes_from_schema()
1323 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1324 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1325 {"SCHEMADN": names.schemadn},
1326 controls=["relax:0", ignore_checks_oid])
1328 # Now register this container in the root of the forest
1329 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1330 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1333 samdb.invocation_id = invocationid
1335 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1336 if fill == FILL_FULL:
1337 logger.info("Setting up sam.ldb configuration data")
1339 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid))
1340 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid))
1341 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid))
1342 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid))
1343 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1344 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid))
1346 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1347 "CONFIGDN": names.configdn,
1348 "NETBIOSNAME": names.netbiosname,
1349 "DEFAULTSITE": names.sitename,
1350 "DNSDOMAIN": names.dnsdomain,
1351 "DOMAIN": names.domain,
1352 "SCHEMADN": names.schemadn,
1353 "DOMAINDN": names.domaindn,
1354 "SERVERDN": names.serverdn,
1355 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1356 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1357 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1358 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1359 "SERVICES_DESCRIPTOR": protected1_descr,
1360 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1361 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1362 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1363 "PARTITIONS_DESCRIPTOR": partitions_descr,
1364 "SITES_DESCRIPTOR": sites_descr,
1367 logger.info("Setting up display specifiers")
1368 display_specifiers_ldif = read_ms_ldif(
1369 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1370 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1371 {"CONFIGDN": names.configdn})
1372 check_all_substituted(display_specifiers_ldif)
1373 samdb.add_ldif(display_specifiers_ldif)
1375 logger.info("Modifying display specifiers")
1376 setup_modify_ldif(samdb,
1377 setup_path("provision_configuration_modify.ldif"), {
1378 "CONFIGDN": names.configdn,
1379 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1382 logger.info("Adding users container")
1383 users_desc = b64encode(get_domain_users_descriptor(names.domainsid))
1384 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1385 "DOMAINDN": names.domaindn,
1386 "USERS_DESCRIPTOR": users_desc
1388 logger.info("Modifying users container")
1389 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1390 "DOMAINDN": names.domaindn})
1391 logger.info("Adding computers container")
1392 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid))
1393 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1394 "DOMAINDN": names.domaindn,
1395 "COMPUTERS_DESCRIPTOR": computers_desc
1397 logger.info("Modifying computers container")
1398 setup_modify_ldif(samdb,
1399 setup_path("provision_computers_modify.ldif"), {
1400 "DOMAINDN": names.domaindn})
1401 logger.info("Setting up sam.ldb data")
1402 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid))
1403 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid))
1404 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid))
1405 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid))
1406 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid))
1407 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1408 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1409 "DOMAINDN": names.domaindn,
1410 "NETBIOSNAME": names.netbiosname,
1411 "DEFAULTSITE": names.sitename,
1412 "CONFIGDN": names.configdn,
1413 "SERVERDN": names.serverdn,
1414 "RIDAVAILABLESTART": str(next_rid + 600),
1415 "POLICYGUID_DC": policyguid_dc,
1416 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1417 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1418 "SYSTEM_DESCRIPTOR": system_desc,
1419 "BUILTIN_DESCRIPTOR": builtin_desc,
1420 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1423 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1424 if fill == FILL_FULL:
1425 setup_modify_ldif(samdb,
1426 setup_path("provision_configuration_references.ldif"), {
1427 "CONFIGDN": names.configdn,
1428 "SCHEMADN": names.schemadn})
1430 logger.info("Setting up well known security principals")
1431 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1432 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1433 "CONFIGDN": names.configdn,
1434 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1437 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1438 setup_modify_ldif(samdb,
1439 setup_path("provision_basedn_references.ldif"),
1440 {"DOMAINDN": names.domaindn})
1442 logger.info("Setting up sam.ldb users and groups")
1443 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1444 "DOMAINDN": names.domaindn,
1445 "DOMAINSID": str(names.domainsid),
1446 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1447 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1450 logger.info("Setting up self join")
1451 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1452 invocationid=invocationid,
1453 dns_backend=dns_backend,
1455 machinepass=machinepass,
1456 domainsid=names.domainsid,
1459 policyguid=policyguid,
1460 policyguid_dc=policyguid_dc,
1461 domainControllerFunctionality=domainControllerFunctionality,
1464 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1465 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1466 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1467 assert isinstance(names.ntdsguid, str)
1472 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1473 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)"
1474 SYSVOL_SERVICE="sysvol"
1476 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1477 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1478 for root, dirs, files in os.walk(path, topdown=False):
1480 setntacl(lp, os.path.join(root, name), acl, domsid,
1481 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1483 setntacl(lp, os.path.join(root, name), acl, domsid,
1484 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1487 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1488 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1491 :param sysvol: Physical path for the sysvol folder
1492 :param dnsdomain: The DNS name of the domain
1493 :param domainsid: The SID of the domain
1494 :param domaindn: The DN of the domain (ie. DC=...)
1495 :param samdb: An LDB object on the SAM db
1496 :param lp: an LP object
1499 # Set ACL for GPO root folder
1500 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1501 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1502 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1504 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1505 attrs=["cn", "nTSecurityDescriptor"],
1506 expression="", scope=ldb.SCOPE_ONELEVEL)
1509 acl = ndr_unpack(security.descriptor,
1510 str(policy["nTSecurityDescriptor"])).as_sddl()
1511 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1512 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1513 str(domainsid), use_ntvfs,
1517 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1518 domaindn, lp, use_ntvfs):
1519 """Set the ACL for the sysvol share and the subfolders
1521 :param samdb: An LDB object on the SAM db
1522 :param netlogon: Physical path for the netlogon folder
1523 :param sysvol: Physical path for the sysvol folder
1524 :param uid: The UID of the "Administrator" user
1525 :param gid: The GID of the "Domain adminstrators" group
1526 :param domainsid: The SID of the domain
1527 :param dnsdomain: The DNS name of the domain
1528 :param domaindn: The DN of the domain (ie. DC=...)
1533 s3conf = s3param.get_context()
1534 s3conf.load(lp.configfile)
1536 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1539 smbd.set_simple_acl(file.name, 0755, gid)
1541 if not smbd.have_posix_acls():
1542 # This clue is only strictly correct for RPM and
1543 # Debian-like Linux systems, but hopefully other users
1544 # will get enough clue from it.
1545 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1546 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1548 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1549 "Try the mounting the filesystem with the 'acl' option.")
1551 smbd.chown(file.name, uid, gid)
1553 raise ProvisioningError("Unable to chown a file on your filesystem. "
1554 "You may not be running provision as root.")
1558 # This will ensure that the smbd code we are running when setting ACLs
1559 # is initialised with the smb.conf
1560 s3conf = s3param.get_context()
1561 s3conf.load(lp.configfile)
1562 # ensure we are using the right samba_dsdb passdb backend, no matter what
1563 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1564 passdb.reload_static_pdb()
1566 # ensure that we init the samba_dsdb backend, so the domain sid is
1567 # marked in secrets.tdb
1568 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1570 # now ensure everything matches correctly, to avoid wierd issues
1571 if passdb.get_global_sam_sid() != domainsid:
1572 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))
1574 domain_info = s4_passdb.domain_info()
1575 if domain_info["dom_sid"] != domainsid:
1576 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))
1578 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1579 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()))
1584 os.chown(sysvol, -1, gid)
1590 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1591 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1592 skip_invalid_chown=True, passdb=s4_passdb,
1593 service=SYSVOL_SERVICE)
1594 for root, dirs, files in os.walk(sysvol, topdown=False):
1596 if use_ntvfs and canchown:
1597 os.chown(os.path.join(root, name), -1, gid)
1598 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1599 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1600 passdb=s4_passdb, service=SYSVOL_SERVICE)
1602 if use_ntvfs and canchown:
1603 os.chown(os.path.join(root, name), -1, gid)
1604 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1605 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1606 passdb=s4_passdb, service=SYSVOL_SERVICE)
1608 # Set acls on Policy folder and policies folders
1609 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1611 def acl_type(direct_db_access):
1612 if direct_db_access:
1617 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1618 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1619 fsacl_sddl = fsacl.as_sddl(domainsid)
1620 if fsacl_sddl != acl:
1621 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))
1623 for root, dirs, files in os.walk(path, topdown=False):
1625 fsacl = getntacl(lp, os.path.join(root, name),
1626 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1628 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1629 fsacl_sddl = fsacl.as_sddl(domainsid)
1630 if fsacl_sddl != acl:
1631 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))
1634 fsacl = getntacl(lp, os.path.join(root, name),
1635 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1637 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1638 fsacl_sddl = fsacl.as_sddl(domainsid)
1639 if fsacl_sddl != acl:
1640 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))
1643 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1645 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1648 :param sysvol: Physical path for the sysvol folder
1649 :param dnsdomain: The DNS name of the domain
1650 :param domainsid: The SID of the domain
1651 :param domaindn: The DN of the domain (ie. DC=...)
1652 :param samdb: An LDB object on the SAM db
1653 :param lp: an LP object
1656 # Set ACL for GPO root folder
1657 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1658 fsacl = getntacl(lp, root_policy_path,
1659 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1661 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1662 fsacl_sddl = fsacl.as_sddl(domainsid)
1663 if fsacl_sddl != POLICIES_ACL:
1664 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))
1665 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1666 attrs=["cn", "nTSecurityDescriptor"],
1667 expression="", scope=ldb.SCOPE_ONELEVEL)
1670 acl = ndr_unpack(security.descriptor,
1671 str(policy["nTSecurityDescriptor"])).as_sddl()
1672 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1673 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1674 domainsid, direct_db_access)
1677 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1679 """Set the ACL for the sysvol share and the subfolders
1681 :param samdb: An LDB object on the SAM db
1682 :param netlogon: Physical path for the netlogon folder
1683 :param sysvol: Physical path for the sysvol folder
1684 :param uid: The UID of the "Administrator" user
1685 :param gid: The GID of the "Domain adminstrators" group
1686 :param domainsid: The SID of the domain
1687 :param dnsdomain: The DNS name of the domain
1688 :param domaindn: The DN of the domain (ie. DC=...)
1691 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1692 s3conf = s3param.get_context()
1693 s3conf.load(lp.configfile)
1694 # ensure we are using the right samba_dsdb passdb backend, no matter what
1695 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1696 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1697 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1699 # now ensure everything matches correctly, to avoid wierd issues
1700 if passdb.get_global_sam_sid() != domainsid:
1701 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))
1703 domain_info = s4_passdb.domain_info()
1704 if domain_info["dom_sid"] != domainsid:
1705 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))
1707 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1708 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()))
1710 # Ensure we can read this directly, and via the smbd VFS
1711 for direct_db_access in [True, False]:
1712 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1713 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1714 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1716 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1717 fsacl_sddl = fsacl.as_sddl(domainsid)
1718 if fsacl_sddl != SYSVOL_ACL:
1719 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))
1721 # Check acls on Policy folder and policies folders
1722 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1726 def interface_ips_v4(lp):
1727 """return only IPv4 IPs"""
1728 ips = samba.interface_ips(lp, False)
1731 if i.find(':') == -1:
1736 def interface_ips_v6(lp):
1737 """return only IPv6 IPs"""
1738 ips = samba.interface_ips(lp, False)
1741 if i.find(':') != -1:
1746 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1748 targetdir=None, samdb_fill=FILL_FULL,
1749 hostip=None, hostip6=None,
1750 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1751 domainguid=None, policyguid=None, policyguid_dc=None,
1752 invocationid=None, machinepass=None, ntdsguid=None,
1753 dns_backend=None, dnspass=None,
1754 serverrole=None, dom_for_fun_level=None,
1755 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1756 # create/adapt the group policy GUIDs
1757 # Default GUID for default policy are described at
1758 # "How Core Group Policy Works"
1759 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1760 if policyguid is None:
1761 policyguid = DEFAULT_POLICY_GUID
1762 policyguid = policyguid.upper()
1763 if policyguid_dc is None:
1764 policyguid_dc = DEFAULT_DC_POLICY_GUID
1765 policyguid_dc = policyguid_dc.upper()
1767 if invocationid is None:
1768 invocationid = str(uuid.uuid4())
1770 if krbtgtpass is None:
1771 krbtgtpass = samba.generate_random_machine_password(128, 255)
1772 if machinepass is None:
1773 machinepass = samba.generate_random_machine_password(128, 255)
1775 dnspass = samba.generate_random_password(128, 255)
1777 samdb.transaction_start()
1779 samdb = fill_samdb(samdb, lp, names, logger=logger,
1781 policyguid=policyguid, policyguid_dc=policyguid_dc,
1782 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1783 invocationid=invocationid, machinepass=machinepass,
1784 dns_backend=dns_backend, dnspass=dnspass,
1785 ntdsguid=ntdsguid, serverrole=serverrole,
1786 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1787 next_rid=next_rid, dc_rid=dc_rid)
1789 # Set up group policies (domain policy and domain controller
1791 if serverrole == "active directory domain controller":
1792 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1795 samdb.transaction_cancel()
1798 samdb.transaction_commit()
1800 if serverrole == "active directory domain controller":
1801 # Continue setting up sysvol for GPO. This appears to require being
1802 # outside a transaction.
1803 if not skip_sysvolacl:
1804 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1805 paths.root_gid, names.domainsid, names.dnsdomain,
1806 names.domaindn, lp, use_ntvfs)
1808 logger.info("Setting acl on sysvol skipped")
1810 secretsdb_self_join(secrets_ldb, domain=names.domain,
1811 realm=names.realm, dnsdomain=names.dnsdomain,
1812 netbiosname=names.netbiosname, domainsid=names.domainsid,
1813 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1815 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1816 # In future, this might be determined from some configuration
1817 kerberos_enctypes = str(ENC_ALL_TYPES)
1820 msg = ldb.Message(ldb.Dn(samdb,
1821 samdb.searchone("distinguishedName",
1822 expression="samAccountName=%s$" % names.netbiosname,
1823 scope=ldb.SCOPE_SUBTREE)))
1824 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1825 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1826 name="msDS-SupportedEncryptionTypes")
1828 except ldb.LdbError, (enum, estr):
1829 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1830 # It might be that this attribute does not exist in this schema
1833 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1834 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1835 dnspass=dnspass, os_level=dom_for_fun_level,
1836 targetdir=targetdir, fill_level=samdb_fill)
1838 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1839 attribute="objectGUID")
1840 assert isinstance(domainguid, str)
1842 lastProvisionUSNs = get_last_provision_usn(samdb)
1843 maxUSN = get_max_usn(samdb, str(names.rootdn))
1844 if lastProvisionUSNs is not None:
1845 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1847 set_provision_usn(samdb, 0, maxUSN, invocationid)
1849 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1850 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1851 { 'NTDSGUID' : names.ntdsguid })
1853 # fix any dangling GUIDs from the provision
1854 logger.info("Fixing provision GUIDs")
1855 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1857 samdb.transaction_start()
1859 # a small number of GUIDs are missing because of ordering issues in the
1861 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1862 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1863 scope=ldb.SCOPE_BASE,
1864 attrs=['defaultObjectCategory'])
1865 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1866 scope=ldb.SCOPE_ONELEVEL,
1867 attrs=['ipsecOwnersReference',
1868 'ipsecFilterReference',
1869 'ipsecISAKMPReference',
1870 'ipsecNegotiationPolicyReference',
1871 'ipsecNFAReference'])
1872 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
1873 attrs=['attributeId', 'governsId']) != 0:
1874 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
1876 samdb.transaction_cancel()
1879 samdb.transaction_commit()
1883 "ROLE_STANDALONE": "standalone server",
1884 "ROLE_DOMAIN_MEMBER": "member server",
1885 "ROLE_DOMAIN_BDC": "active directory domain controller",
1886 "ROLE_DOMAIN_PDC": "active directory domain controller",
1887 "dc": "active directory domain controller",
1888 "member": "member server",
1889 "domain controller": "active directory domain controller",
1890 "active directory domain controller": "active directory domain controller",
1891 "member server": "member server",
1892 "standalone": "standalone server",
1893 "standalone server": "standalone server",
1897 def sanitize_server_role(role):
1898 """Sanitize a server role name.
1900 :param role: Server role
1901 :raise ValueError: If the role can not be interpreted
1902 :return: Sanitized server role (one of "member server",
1903 "active directory domain controller", "standalone server")
1906 return _ROLES_MAP[role]
1908 raise ValueError(role)
1911 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1913 """Create AD entries for the fake ypserver.
1915 This is needed for being able to manipulate posix attrs via ADUC.
1917 samdb.transaction_start()
1919 logger.info("Setting up fake yp server settings")
1920 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1921 "DOMAINDN": domaindn,
1922 "NETBIOSNAME": netbiosname,
1923 "NISDOMAIN": nisdomain,
1926 samdb.transaction_cancel()
1929 samdb.transaction_commit()
1932 def provision(logger, session_info, smbconf=None,
1933 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1934 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1935 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1936 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1937 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1938 dns_backend=None, dns_forwarder=None, dnspass=None,
1939 invocationid=None, machinepass=None, ntdsguid=None,
1940 root=None, nobody=None, users=None, backup=None, aci=None,
1941 serverrole=None, dom_for_fun_level=None, backend_type=None,
1942 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1943 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1944 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
1945 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, ldap_backend_extra_port=None):
1948 :note: caution, this wipes all existing data!
1952 serverrole = sanitize_server_role(serverrole)
1954 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1956 if ldapadminpass is None:
1957 # Make a new, random password between Samba and it's LDAP server
1958 ldapadminpass = samba.generate_random_password(128, 255)
1960 if backend_type is None:
1961 backend_type = "ldb"
1963 if domainsid is None:
1964 domainsid = security.random_sid()
1966 root_uid = findnss_uid([root or "root"])
1967 nobody_uid = findnss_uid([nobody or "nobody"])
1968 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1969 root_gid = pwd.getpwuid(root_uid).pw_gid
1972 bind_gid = findnss_gid(["bind", "named"])
1976 if targetdir is not None:
1977 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1978 elif smbconf is None:
1979 smbconf = samba.param.default_path()
1980 if not os.path.exists(os.path.dirname(smbconf)):
1981 os.makedirs(os.path.dirname(smbconf))
1983 server_services = []
1986 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1988 if dns_backend != "SAMBA_INTERNAL":
1989 server_services.append("-dns")
1991 if dns_forwarder is not None:
1992 global_param["dns forwarder"] = [dns_forwarder]
1995 server_services.append("+smb")
1996 server_services.append("-s3fs")
1997 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1999 if len(server_services) > 0:
2000 global_param["server services"] = server_services
2002 # only install a new smb.conf if there isn't one there already
2003 if os.path.exists(smbconf):
2004 # if Samba Team members can't figure out the weird errors
2005 # loading an empty smb.conf gives, then we need to be smarter.
2006 # Pretend it just didn't exist --abartlet
2007 f = open(smbconf, 'r')
2009 data = f.read().lstrip()
2012 if data is None or data == "":
2013 make_smbconf(smbconf, hostname, domain, realm,
2014 targetdir, serverrole=serverrole,
2015 eadb=useeadb, use_ntvfs=use_ntvfs,
2016 lp=lp, global_param=global_param)
2018 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2019 serverrole=serverrole,
2020 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2023 lp = samba.param.LoadParm()
2025 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2026 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2027 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2028 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2029 paths = provision_paths_from_lp(lp, names.dnsdomain)
2031 paths.bind_gid = bind_gid
2032 paths.root_uid = root_uid;
2033 paths.root_gid = root_gid
2036 logger.info("Looking up IPv4 addresses")
2037 hostips = interface_ips_v4(lp)
2038 if len(hostips) > 0:
2040 if len(hostips) > 1:
2041 logger.warning("More than one IPv4 address found. Using %s",
2043 if hostip == "127.0.0.1":
2046 logger.warning("No IPv4 address will be assigned")
2049 logger.info("Looking up IPv6 addresses")
2050 hostips = interface_ips_v6(lp)
2052 hostip6 = hostips[0]
2053 if len(hostips) > 1:
2054 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2056 logger.warning("No IPv6 address will be assigned")
2058 names.hostip = hostip
2059 names.hostip6 = hostip6
2060 names.domainguid = domainguid
2061 names.domainsid = domainsid
2062 names.forestsid = domainsid
2064 if serverrole is None:
2065 serverrole = lp.get("server role")
2067 if not os.path.exists(paths.private_dir):
2068 os.mkdir(paths.private_dir)
2069 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
2070 os.makedirs(os.path.join(paths.private_dir, "tls"), 0700)
2071 if not os.path.exists(paths.state_dir):
2072 os.mkdir(paths.state_dir)
2074 if paths.sysvol and not os.path.exists(paths.sysvol):
2075 os.makedirs(paths.sysvol, 0775)
2077 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2079 schema = Schema(domainsid, invocationid=invocationid,
2080 schemadn=names.schemadn)
2082 if backend_type == "ldb":
2083 provision_backend = LDBBackend(backend_type, paths=paths,
2085 names=names, logger=logger)
2086 elif backend_type == "existing":
2087 # If support for this is ever added back, then the URI will need to be
2089 provision_backend = ExistingBackend(backend_type, paths=paths,
2091 names=names, logger=logger,
2092 ldap_backend_forced_uri=ldap_backend_forced_uri)
2093 elif backend_type == "fedora-ds":
2094 provision_backend = FDSBackend(backend_type, paths=paths,
2096 names=names, logger=logger, domainsid=domainsid,
2097 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2098 slapd_path=slapd_path,
2100 elif backend_type == "openldap":
2101 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2103 names=names, logger=logger, domainsid=domainsid,
2104 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2105 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2106 ldap_backend_extra_port=ldap_backend_extra_port,
2107 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2108 ldap_backend_forced_uri=ldap_backend_forced_uri)
2110 raise ValueError("Unknown LDAP backend type selected")
2112 provision_backend.init()
2113 provision_backend.start()
2115 # only install a new shares config db if there is none
2116 if not os.path.exists(paths.shareconf):
2117 logger.info("Setting up share.ldb")
2118 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2119 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2121 logger.info("Setting up secrets.ldb")
2122 secrets_ldb = setup_secretsdb(paths,
2123 session_info=session_info,
2124 backend_credentials=provision_backend.credentials, lp=lp)
2127 logger.info("Setting up the registry")
2128 setup_registry(paths.hklm, session_info, lp=lp)
2130 logger.info("Setting up the privileges database")
2131 setup_privileges(paths.privilege, session_info, lp=lp)
2133 logger.info("Setting up idmap db")
2134 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2136 setup_name_mappings(idmap, sid=str(domainsid),
2137 root_uid=root_uid, nobody_uid=nobody_uid,
2138 users_gid=users_gid, root_gid=root_gid)
2140 logger.info("Setting up SAM db")
2141 samdb = setup_samdb(paths.samdb, session_info,
2142 provision_backend, lp, names, logger=logger,
2143 serverrole=serverrole,
2144 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2146 if serverrole == "active directory domain controller":
2147 if paths.netlogon is None:
2148 raise MissingShareError("netlogon", paths.smbconf)
2150 if paths.sysvol is None:
2151 raise MissingShareError("sysvol", paths.smbconf)
2153 if not os.path.isdir(paths.netlogon):
2154 os.makedirs(paths.netlogon, 0755)
2156 if adminpass is None:
2157 adminpass = samba.generate_random_password(12, 32)
2158 adminpass_generated = True
2160 adminpass = unicode(adminpass, 'utf-8')
2161 adminpass_generated = False
2163 if samdb_fill == FILL_FULL:
2164 provision_fill(samdb, secrets_ldb, logger, names, paths,
2165 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2166 hostip=hostip, hostip6=hostip6,
2167 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2168 krbtgtpass=krbtgtpass,
2169 policyguid=policyguid, policyguid_dc=policyguid_dc,
2170 invocationid=invocationid, machinepass=machinepass,
2171 ntdsguid=ntdsguid, dns_backend=dns_backend,
2172 dnspass=dnspass, serverrole=serverrole,
2173 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2174 lp=lp, use_ntvfs=use_ntvfs,
2175 skip_sysvolacl=skip_sysvolacl)
2177 create_krb5_conf(paths.krb5conf,
2178 dnsdomain=names.dnsdomain, hostname=names.hostname,
2180 logger.info("A Kerberos configuration suitable for Samba AD has been "
2181 "generated at %s", paths.krb5conf)
2183 if serverrole == "active directory domain controller":
2184 create_dns_update_list(lp, logger, paths)
2186 backend_result = provision_backend.post_setup()
2187 provision_backend.shutdown()
2190 secrets_ldb.transaction_cancel()
2193 # Now commit the secrets.ldb to disk
2194 secrets_ldb.transaction_commit()
2196 # the commit creates the dns.keytab, now chown it
2197 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2198 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2200 os.chmod(dns_keytab_path, 0640)
2201 os.chown(dns_keytab_path, -1, paths.bind_gid)
2203 if not os.environ.has_key('SAMBA_SELFTEST'):
2204 logger.info("Failed to chown %s to bind gid %u",
2205 dns_keytab_path, paths.bind_gid)
2207 result = ProvisionResult()
2208 result.server_role = serverrole
2209 result.domaindn = domaindn
2210 result.paths = paths
2211 result.names = names
2213 result.samdb = samdb
2214 result.idmap = idmap
2215 result.domainsid = str(domainsid)
2217 if samdb_fill == FILL_FULL:
2218 result.adminpass_generated = adminpass_generated
2219 result.adminpass = adminpass
2221 result.adminpass_generated = False
2222 result.adminpass = None
2224 result.backend_result = backend_result
2227 provision_fake_ypserver(logger=logger, samdb=samdb,
2228 domaindn=names.domaindn, netbiosname=names.netbiosname,
2229 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2234 def provision_become_dc(smbconf=None, targetdir=None,
2235 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2236 serverdn=None, domain=None, hostname=None, domainsid=None,
2237 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2238 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2239 dns_backend=None, root=None, nobody=None, users=None,
2240 backup=None, serverrole=None, ldap_backend=None,
2241 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2243 logger = logging.getLogger("provision")
2244 samba.set_debug_level(debuglevel)
2246 res = provision(logger, system_session(),
2247 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2248 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2249 configdn=configdn, serverdn=serverdn, domain=domain,
2250 hostname=hostname, hostip=None, domainsid=domainsid,
2251 machinepass=machinepass,
2252 serverrole="active directory domain controller",
2253 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2254 use_ntvfs=use_ntvfs)
2255 res.lp.set("debuglevel", str(debuglevel))
2259 def create_krb5_conf(path, dnsdomain, hostname, realm):
2260 """Write out a file containing a valid krb5.conf file
2262 :param path: Path of the new krb5.conf file.
2263 :param dnsdomain: DNS Domain name
2264 :param hostname: Local hostname
2265 :param realm: Realm name
2267 setup_file(setup_path("krb5.conf"), path, {
2268 "DNSDOMAIN": dnsdomain,
2269 "HOSTNAME": hostname,
2274 class ProvisioningError(Exception):
2275 """A generic provision error."""
2277 def __init__(self, value):
2281 return "ProvisioningError: " + self.value
2284 class InvalidNetbiosName(Exception):
2285 """A specified name was not a valid NetBIOS name."""
2287 def __init__(self, name):
2288 super(InvalidNetbiosName, self).__init__(
2289 "The name '%r' is not a valid NetBIOS name" % name)
2292 class MissingShareError(ProvisioningError):
2294 def __init__(self, name, smbconf):
2295 super(MissingShareError, self).__init__(
2296 "Existing smb.conf does not have a [%s] share, but you are "
2297 "configuring a DC. Please remove %s or add the share manually." %