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 lp.dump(False, smbconf)
766 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
767 users_gid, root_gid):
768 """setup reasonable name mappings for sam names to unix names.
770 :param samdb: SamDB object.
771 :param idmap: IDmap db object.
772 :param sid: The domain sid.
773 :param domaindn: The domain DN.
774 :param root_uid: uid of the UNIX root user.
775 :param nobody_uid: uid of the UNIX nobody user.
776 :param users_gid: gid of the UNIX users group.
777 :param root_gid: gid of the UNIX root group.
779 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
781 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
782 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
785 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
786 provision_backend, names, serverrole,
788 """Setup the partitions for the SAM database.
790 Alternatively, provision() may call this, and then populate the database.
792 :note: This will wipe the Sam Database!
794 :note: This function always removes the local SAM LDB file. The erase
795 parameter controls whether to erase the existing data, which
796 may not be stored locally but in LDAP.
799 assert session_info is not None
801 # We use options=["modules:"] to stop the modules loading - we
802 # just want to wipe and re-initialise the database, not start it up
805 os.unlink(samdb_path)
809 samdb = Ldb(url=samdb_path, session_info=session_info,
810 lp=lp, options=["modules:"])
812 ldap_backend_line = "# No LDAP backend"
813 if provision_backend.type != "ldb":
814 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
816 samdb.transaction_start()
818 logger.info("Setting up sam.ldb partitions and settings")
819 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
820 "LDAP_BACKEND_LINE": ldap_backend_line
824 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
825 "BACKEND_TYPE": provision_backend.type,
826 "SERVER_ROLE": serverrole
829 logger.info("Setting up sam.ldb rootDSE")
830 setup_samdb_rootdse(samdb, names)
832 samdb.transaction_cancel()
835 samdb.transaction_commit()
838 def secretsdb_self_join(secretsdb, domain,
839 netbiosname, machinepass, domainsid=None,
840 realm=None, dnsdomain=None,
842 key_version_number=1,
843 secure_channel_type=SEC_CHAN_WKSTA):
844 """Add domain join-specific bits to a secrets database.
846 :param secretsdb: Ldb Handle to the secrets database
847 :param machinepass: Machine password
849 attrs = ["whenChanged",
856 if realm is not None:
857 if dnsdomain is None:
858 dnsdomain = realm.lower()
859 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
862 shortname = netbiosname.lower()
864 # We don't need to set msg["flatname"] here, because rdn_name will handle
865 # it, and it causes problems for modifies anyway
866 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
867 msg["secureChannelType"] = [str(secure_channel_type)]
868 msg["objectClass"] = ["top", "primaryDomain"]
869 if dnsname is not None:
870 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
871 msg["realm"] = [realm]
872 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
873 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
874 msg["privateKeytab"] = ["secrets.keytab"]
876 msg["secret"] = [machinepass.encode('utf-8')]
877 msg["samAccountName"] = ["%s$" % netbiosname]
878 msg["secureChannelType"] = [str(secure_channel_type)]
879 if domainsid is not None:
880 msg["objectSid"] = [ndr_pack(domainsid)]
882 # This complex expression tries to ensure that we don't have more
883 # than one record for this SID, realm or netbios domain at a time,
884 # but we don't delete the old record that we are about to modify,
885 # because that would delete the keytab and previous password.
886 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
887 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
888 scope=ldb.SCOPE_ONELEVEL)
891 secretsdb.delete(del_msg.dn)
893 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
896 msg["priorSecret"] = [res[0]["secret"][0]]
897 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
900 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
905 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
911 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
912 secretsdb.modify(msg)
913 secretsdb.rename(res[0].dn, msg.dn)
915 spn = [ 'HOST/%s' % shortname ]
916 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
917 # we are a domain controller then we add servicePrincipalName
918 # entries for the keytab code to update.
919 spn.extend([ 'HOST/%s' % dnsname ])
920 msg["servicePrincipalName"] = spn
925 def setup_secretsdb(paths, session_info, backend_credentials, lp):
926 """Setup the secrets database.
928 :note: This function does not handle exceptions and transaction on purpose,
929 it's up to the caller to do this job.
931 :param path: Path to the secrets database.
932 :param session_info: Session info.
933 :param credentials: Credentials
934 :param lp: Loadparm context
935 :return: LDB handle for the created secrets database
937 if os.path.exists(paths.secrets):
938 os.unlink(paths.secrets)
940 keytab_path = os.path.join(paths.private_dir, paths.keytab)
941 if os.path.exists(keytab_path):
942 os.unlink(keytab_path)
944 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
945 if os.path.exists(dns_keytab_path):
946 os.unlink(dns_keytab_path)
950 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
952 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
953 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
954 secrets_ldb.transaction_start()
956 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
958 if (backend_credentials is not None and
959 backend_credentials.authentication_requested()):
960 if backend_credentials.get_bind_dn() is not None:
961 setup_add_ldif(secrets_ldb,
962 setup_path("secrets_simple_ldap.ldif"), {
963 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
964 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
967 setup_add_ldif(secrets_ldb,
968 setup_path("secrets_sasl_ldap.ldif"), {
969 "LDAPADMINUSER": backend_credentials.get_username(),
970 "LDAPADMINREALM": backend_credentials.get_realm(),
971 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
974 secrets_ldb.transaction_cancel()
979 def setup_privileges(path, session_info, lp):
980 """Setup the privileges database.
982 :param path: Path to the privileges database.
983 :param session_info: Session info.
984 :param credentials: Credentials
985 :param lp: Loadparm context
986 :return: LDB handle for the created secrets database
988 if os.path.exists(path):
990 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
991 privilege_ldb.erase()
992 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
995 def setup_registry(path, session_info, lp):
996 """Setup the registry.
998 :param path: Path to the registry database
999 :param session_info: Session information
1000 :param credentials: Credentials
1001 :param lp: Loadparm context
1003 reg = samba.registry.Registry()
1004 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1005 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1006 provision_reg = setup_path("provision.reg")
1007 assert os.path.exists(provision_reg)
1008 reg.diff_apply(provision_reg)
1011 def setup_idmapdb(path, session_info, lp):
1012 """Setup the idmap database.
1014 :param path: path to the idmap database
1015 :param session_info: Session information
1016 :param credentials: Credentials
1017 :param lp: Loadparm context
1019 if os.path.exists(path):
1022 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1024 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1028 def setup_samdb_rootdse(samdb, names):
1029 """Setup the SamDB rootdse.
1031 :param samdb: Sam Database handle
1033 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1034 "SCHEMADN": names.schemadn,
1035 "DOMAINDN": names.domaindn,
1036 "ROOTDN" : names.rootdn,
1037 "CONFIGDN": names.configdn,
1038 "SERVERDN": names.serverdn,
1042 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1043 dns_backend, dnspass, domainsid, next_rid, invocationid,
1044 policyguid, policyguid_dc,
1045 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1046 """Join a host to its own domain."""
1047 assert isinstance(invocationid, str)
1048 if ntdsguid is not None:
1049 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1056 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1057 "CONFIGDN": names.configdn,
1058 "SCHEMADN": names.schemadn,
1059 "DOMAINDN": names.domaindn,
1060 "SERVERDN": names.serverdn,
1061 "INVOCATIONID": invocationid,
1062 "NETBIOSNAME": names.netbiosname,
1063 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1064 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1065 "DOMAINSID": str(domainsid),
1066 "DCRID": str(dc_rid),
1067 "SAMBA_VERSION_STRING": version,
1068 "NTDSGUID": ntdsguid_line,
1069 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1070 domainControllerFunctionality),
1071 "RIDALLOCATIONSTART": str(next_rid + 100),
1072 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1074 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1075 "POLICYGUID": policyguid,
1076 "POLICYGUID_DC": policyguid_dc,
1077 "DNSDOMAIN": names.dnsdomain,
1078 "DOMAINDN": names.domaindn})
1080 # If we are setting up a subdomain, then this has been replicated in, so we
1081 # don't need to add it
1082 if fill == FILL_FULL:
1083 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1084 "CONFIGDN": names.configdn,
1085 "SCHEMADN": names.schemadn,
1086 "DOMAINDN": names.domaindn,
1087 "SERVERDN": names.serverdn,
1088 "INVOCATIONID": invocationid,
1089 "NETBIOSNAME": names.netbiosname,
1090 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1091 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1092 "DOMAINSID": str(domainsid),
1093 "DCRID": str(dc_rid),
1094 "SAMBA_VERSION_STRING": version,
1095 "NTDSGUID": ntdsguid_line,
1096 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1097 domainControllerFunctionality)})
1099 # Setup fSMORoleOwner entries to point at the newly created DC entry
1100 setup_modify_ldif(samdb,
1101 setup_path("provision_self_join_modify_config.ldif"), {
1102 "CONFIGDN": names.configdn,
1103 "SCHEMADN": names.schemadn,
1104 "DEFAULTSITE": names.sitename,
1105 "NETBIOSNAME": names.netbiosname,
1106 "SERVERDN": names.serverdn,
1109 system_session_info = system_session()
1110 samdb.set_session_info(system_session_info)
1111 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1112 # modify a serverReference under cn=config when we are a subdomain, we must
1113 # be system due to ACLs
1114 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1115 "DOMAINDN": names.domaindn,
1116 "SERVERDN": names.serverdn,
1117 "NETBIOSNAME": names.netbiosname,
1120 samdb.set_session_info(admin_session_info)
1122 if dns_backend != "SAMBA_INTERNAL":
1123 # This is Samba4 specific and should be replaced by the correct
1124 # DNS AD-style setup
1125 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1126 "DNSDOMAIN": names.dnsdomain,
1127 "DOMAINDN": names.domaindn,
1128 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1129 "HOSTNAME" : names.hostname,
1130 "DNSNAME" : '%s.%s' % (
1131 names.netbiosname.lower(), names.dnsdomain.lower())
1135 def getpolicypath(sysvolpath, dnsdomain, guid):
1136 """Return the physical path of policy given its guid.
1138 :param sysvolpath: Path to the sysvol folder
1139 :param dnsdomain: DNS name of the AD domain
1140 :param guid: The GUID of the policy
1141 :return: A string with the complete path to the policy folder
1144 guid = "{%s}" % guid
1145 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1149 def create_gpo_struct(policy_path):
1150 if not os.path.exists(policy_path):
1151 os.makedirs(policy_path, 0775)
1152 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1154 f.write("[General]\r\nVersion=0")
1157 p = os.path.join(policy_path, "MACHINE")
1158 if not os.path.exists(p):
1159 os.makedirs(p, 0775)
1160 p = os.path.join(policy_path, "USER")
1161 if not os.path.exists(p):
1162 os.makedirs(p, 0775)
1165 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1166 """Create the default GPO for a domain
1168 :param sysvolpath: Physical path for the sysvol folder
1169 :param dnsdomain: DNS domain name of the AD domain
1170 :param policyguid: GUID of the default domain policy
1171 :param policyguid_dc: GUID of the default domain controler policy
1173 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1174 create_gpo_struct(policy_path)
1176 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1177 create_gpo_struct(policy_path)
1180 def setup_samdb(path, session_info, provision_backend, lp, names,
1181 logger, fill, serverrole, schema, am_rodc=False):
1182 """Setup a complete SAM Database.
1184 :note: This will wipe the main SAM database file!
1187 # Also wipes the database
1188 setup_samdb_partitions(path, logger=logger, lp=lp,
1189 provision_backend=provision_backend, session_info=session_info,
1190 names=names, serverrole=serverrole)
1192 # Load the database, but don's load the global schema and don't connect
1194 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1195 credentials=provision_backend.credentials, lp=lp,
1196 global_schema=False, am_rodc=am_rodc)
1198 logger.info("Pre-loading the Samba 4 and AD schema")
1200 # Load the schema from the one we computed earlier
1201 samdb.set_schema(schema, write_indices_and_attributes=False)
1203 # Set the NTDS settings DN manually - in order to have it already around
1204 # before the provisioned tree exists and we connect
1205 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1207 # And now we can connect to the DB - the schema won't be loaded from the
1211 except ldb.LdbError, (num, string_error):
1212 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1213 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1217 # But we have to give it one more kick to have it use the schema
1218 # during provision - it needs, now that it is connected, to write
1219 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1220 samdb.set_schema(schema, write_indices_and_attributes=True)
1225 def fill_samdb(samdb, lp, names, logger, policyguid,
1226 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1227 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1228 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1230 if next_rid is None:
1233 # Provision does not make much sense values larger than 1000000000
1234 # as the upper range of the rIDAvailablePool is 1073741823 and
1235 # we don't want to create a domain that cannot allocate rids.
1236 if next_rid < 1000 or next_rid > 1000000000:
1237 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1238 error += "the valid range is %u-%u. The default is %u." % (
1239 1000, 1000000000, 1000)
1240 raise ProvisioningError(error)
1242 # ATTENTION: Do NOT change these default values without discussion with the
1243 # team and/or release manager. They have a big impact on the whole program!
1244 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1246 if dom_for_fun_level is None:
1247 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1249 if dom_for_fun_level > domainControllerFunctionality:
1250 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!")
1252 domainFunctionality = dom_for_fun_level
1253 forestFunctionality = dom_for_fun_level
1255 # Set the NTDS settings DN manually - in order to have it already around
1256 # before the provisioned tree exists and we connect
1257 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1259 # Set the domain functionality levels onto the database.
1260 # Various module (the password_hash module in particular) need
1261 # to know what level of AD we are emulating.
1263 # These will be fixed into the database via the database
1264 # modifictions below, but we need them set from the start.
1265 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1266 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1267 samdb.set_opaque_integer("domainControllerFunctionality",
1268 domainControllerFunctionality)
1270 samdb.set_domain_sid(str(names.domainsid))
1271 samdb.set_invocation_id(invocationid)
1273 logger.info("Adding DomainDN: %s" % names.domaindn)
1275 # impersonate domain admin
1276 admin_session_info = admin_session(lp, str(names.domainsid))
1277 samdb.set_session_info(admin_session_info)
1278 if names.domainguid is not None:
1279 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1281 domainguid_line = ""
1283 descr = b64encode(get_domain_descriptor(names.domainsid))
1284 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1285 "DOMAINDN": names.domaindn,
1286 "DOMAINSID": str(names.domainsid),
1287 "DESCRIPTOR": descr,
1288 "DOMAINGUID": domainguid_line
1291 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1292 "DOMAINDN": names.domaindn,
1293 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1294 "NEXTRID": str(next_rid),
1295 "DEFAULTSITE": names.sitename,
1296 "CONFIGDN": names.configdn,
1297 "POLICYGUID": policyguid,
1298 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1299 "SAMBA_VERSION_STRING": version
1302 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1303 if fill == FILL_FULL:
1304 logger.info("Adding configuration container")
1305 descr = b64encode(get_config_descriptor(names.domainsid))
1306 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1307 "CONFIGDN": names.configdn,
1308 "DESCRIPTOR": descr,
1311 # The LDIF here was created when the Schema object was constructed
1312 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1313 logger.info("Setting up sam.ldb schema")
1314 samdb.add_ldif(schema.schema_dn_add,
1315 controls=["relax:0", ignore_checks_oid])
1316 samdb.modify_ldif(schema.schema_dn_modify,
1317 controls=[ignore_checks_oid])
1318 samdb.write_prefixes_from_schema()
1319 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1320 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1321 {"SCHEMADN": names.schemadn},
1322 controls=["relax:0", ignore_checks_oid])
1324 # Now register this container in the root of the forest
1325 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1326 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1329 samdb.invocation_id = invocationid
1331 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1332 if fill == FILL_FULL:
1333 logger.info("Setting up sam.ldb configuration data")
1335 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid))
1336 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid))
1337 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid))
1338 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid))
1339 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1340 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid))
1342 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1343 "CONFIGDN": names.configdn,
1344 "NETBIOSNAME": names.netbiosname,
1345 "DEFAULTSITE": names.sitename,
1346 "DNSDOMAIN": names.dnsdomain,
1347 "DOMAIN": names.domain,
1348 "SCHEMADN": names.schemadn,
1349 "DOMAINDN": names.domaindn,
1350 "SERVERDN": names.serverdn,
1351 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1352 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1353 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1354 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1355 "SERVICES_DESCRIPTOR": protected1_descr,
1356 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1357 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1358 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1359 "PARTITIONS_DESCRIPTOR": partitions_descr,
1360 "SITES_DESCRIPTOR": sites_descr,
1363 logger.info("Setting up display specifiers")
1364 display_specifiers_ldif = read_ms_ldif(
1365 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1366 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1367 {"CONFIGDN": names.configdn})
1368 check_all_substituted(display_specifiers_ldif)
1369 samdb.add_ldif(display_specifiers_ldif)
1371 logger.info("Modifying display specifiers")
1372 setup_modify_ldif(samdb,
1373 setup_path("provision_configuration_modify.ldif"), {
1374 "CONFIGDN": names.configdn,
1375 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1378 logger.info("Adding users container")
1379 users_desc = b64encode(get_domain_users_descriptor(names.domainsid))
1380 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1381 "DOMAINDN": names.domaindn,
1382 "USERS_DESCRIPTOR": users_desc
1384 logger.info("Modifying users container")
1385 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1386 "DOMAINDN": names.domaindn})
1387 logger.info("Adding computers container")
1388 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid))
1389 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1390 "DOMAINDN": names.domaindn,
1391 "COMPUTERS_DESCRIPTOR": computers_desc
1393 logger.info("Modifying computers container")
1394 setup_modify_ldif(samdb,
1395 setup_path("provision_computers_modify.ldif"), {
1396 "DOMAINDN": names.domaindn})
1397 logger.info("Setting up sam.ldb data")
1398 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid))
1399 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid))
1400 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid))
1401 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid))
1402 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid))
1403 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1404 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1405 "DOMAINDN": names.domaindn,
1406 "NETBIOSNAME": names.netbiosname,
1407 "DEFAULTSITE": names.sitename,
1408 "CONFIGDN": names.configdn,
1409 "SERVERDN": names.serverdn,
1410 "RIDAVAILABLESTART": str(next_rid + 600),
1411 "POLICYGUID_DC": policyguid_dc,
1412 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1413 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1414 "SYSTEM_DESCRIPTOR": system_desc,
1415 "BUILTIN_DESCRIPTOR": builtin_desc,
1416 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1419 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1420 if fill == FILL_FULL:
1421 setup_modify_ldif(samdb,
1422 setup_path("provision_configuration_references.ldif"), {
1423 "CONFIGDN": names.configdn,
1424 "SCHEMADN": names.schemadn})
1426 logger.info("Setting up well known security principals")
1427 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1428 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1429 "CONFIGDN": names.configdn,
1430 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1433 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1434 setup_modify_ldif(samdb,
1435 setup_path("provision_basedn_references.ldif"),
1436 {"DOMAINDN": names.domaindn})
1438 logger.info("Setting up sam.ldb users and groups")
1439 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1440 "DOMAINDN": names.domaindn,
1441 "DOMAINSID": str(names.domainsid),
1442 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1443 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1446 logger.info("Setting up self join")
1447 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1448 invocationid=invocationid,
1449 dns_backend=dns_backend,
1451 machinepass=machinepass,
1452 domainsid=names.domainsid,
1455 policyguid=policyguid,
1456 policyguid_dc=policyguid_dc,
1457 domainControllerFunctionality=domainControllerFunctionality,
1460 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1461 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1462 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1463 assert isinstance(names.ntdsguid, str)
1468 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1469 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)"
1470 SYSVOL_SERVICE="sysvol"
1472 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1473 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1474 for root, dirs, files in os.walk(path, topdown=False):
1476 setntacl(lp, os.path.join(root, name), acl, domsid,
1477 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1479 setntacl(lp, os.path.join(root, name), acl, domsid,
1480 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1483 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1484 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1487 :param sysvol: Physical path for the sysvol folder
1488 :param dnsdomain: The DNS name of the domain
1489 :param domainsid: The SID of the domain
1490 :param domaindn: The DN of the domain (ie. DC=...)
1491 :param samdb: An LDB object on the SAM db
1492 :param lp: an LP object
1495 # Set ACL for GPO root folder
1496 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1497 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1498 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1500 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1501 attrs=["cn", "nTSecurityDescriptor"],
1502 expression="", scope=ldb.SCOPE_ONELEVEL)
1505 acl = ndr_unpack(security.descriptor,
1506 str(policy["nTSecurityDescriptor"])).as_sddl()
1507 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1508 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1509 str(domainsid), use_ntvfs,
1513 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1514 domaindn, lp, use_ntvfs):
1515 """Set the ACL for the sysvol share and the subfolders
1517 :param samdb: An LDB object on the SAM db
1518 :param netlogon: Physical path for the netlogon folder
1519 :param sysvol: Physical path for the sysvol folder
1520 :param uid: The UID of the "Administrator" user
1521 :param gid: The GID of the "Domain adminstrators" group
1522 :param domainsid: The SID of the domain
1523 :param dnsdomain: The DNS name of the domain
1524 :param domaindn: The DN of the domain (ie. DC=...)
1529 s3conf = s3param.get_context()
1530 s3conf.load(lp.configfile)
1532 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1535 smbd.set_simple_acl(file.name, 0755, gid)
1537 if not smbd.have_posix_acls():
1538 # This clue is only strictly correct for RPM and
1539 # Debian-like Linux systems, but hopefully other users
1540 # will get enough clue from it.
1541 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1542 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1544 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1545 "Try the mounting the filesystem with the 'acl' option.")
1547 smbd.chown(file.name, uid, gid)
1549 raise ProvisioningError("Unable to chown a file on your filesystem. "
1550 "You may not be running provision as root.")
1554 # This will ensure that the smbd code we are running when setting ACLs
1555 # is initialised with the smb.conf
1556 s3conf = s3param.get_context()
1557 s3conf.load(lp.configfile)
1558 # ensure we are using the right samba_dsdb passdb backend, no matter what
1559 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1560 passdb.reload_static_pdb()
1562 # ensure that we init the samba_dsdb backend, so the domain sid is
1563 # marked in secrets.tdb
1564 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1566 # now ensure everything matches correctly, to avoid wierd issues
1567 if passdb.get_global_sam_sid() != domainsid:
1568 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))
1570 domain_info = s4_passdb.domain_info()
1571 if domain_info["dom_sid"] != domainsid:
1572 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))
1574 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1575 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()))
1580 os.chown(sysvol, -1, gid)
1586 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1587 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1588 skip_invalid_chown=True, passdb=s4_passdb,
1589 service=SYSVOL_SERVICE)
1590 for root, dirs, files in os.walk(sysvol, topdown=False):
1592 if use_ntvfs and canchown:
1593 os.chown(os.path.join(root, name), -1, gid)
1594 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1595 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1596 passdb=s4_passdb, service=SYSVOL_SERVICE)
1598 if use_ntvfs and canchown:
1599 os.chown(os.path.join(root, name), -1, gid)
1600 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1601 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1602 passdb=s4_passdb, service=SYSVOL_SERVICE)
1604 # Set acls on Policy folder and policies folders
1605 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1607 def acl_type(direct_db_access):
1608 if direct_db_access:
1613 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1614 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1615 fsacl_sddl = fsacl.as_sddl(domainsid)
1616 if fsacl_sddl != acl:
1617 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))
1619 for root, dirs, files in os.walk(path, topdown=False):
1621 fsacl = getntacl(lp, os.path.join(root, name),
1622 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1624 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1625 fsacl_sddl = fsacl.as_sddl(domainsid)
1626 if fsacl_sddl != acl:
1627 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))
1630 fsacl = getntacl(lp, os.path.join(root, name),
1631 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1633 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1634 fsacl_sddl = fsacl.as_sddl(domainsid)
1635 if fsacl_sddl != acl:
1636 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))
1639 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1641 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1644 :param sysvol: Physical path for the sysvol folder
1645 :param dnsdomain: The DNS name of the domain
1646 :param domainsid: The SID of the domain
1647 :param domaindn: The DN of the domain (ie. DC=...)
1648 :param samdb: An LDB object on the SAM db
1649 :param lp: an LP object
1652 # Set ACL for GPO root folder
1653 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1654 fsacl = getntacl(lp, root_policy_path,
1655 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1657 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1658 fsacl_sddl = fsacl.as_sddl(domainsid)
1659 if fsacl_sddl != POLICIES_ACL:
1660 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))
1661 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1662 attrs=["cn", "nTSecurityDescriptor"],
1663 expression="", scope=ldb.SCOPE_ONELEVEL)
1666 acl = ndr_unpack(security.descriptor,
1667 str(policy["nTSecurityDescriptor"])).as_sddl()
1668 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1669 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1670 domainsid, direct_db_access)
1673 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1675 """Set the ACL for the sysvol share and the subfolders
1677 :param samdb: An LDB object on the SAM db
1678 :param netlogon: Physical path for the netlogon folder
1679 :param sysvol: Physical path for the sysvol folder
1680 :param uid: The UID of the "Administrator" user
1681 :param gid: The GID of the "Domain adminstrators" group
1682 :param domainsid: The SID of the domain
1683 :param dnsdomain: The DNS name of the domain
1684 :param domaindn: The DN of the domain (ie. DC=...)
1687 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1688 s3conf = s3param.get_context()
1689 s3conf.load(lp.configfile)
1690 # ensure we are using the right samba_dsdb passdb backend, no matter what
1691 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1692 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1693 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1695 # now ensure everything matches correctly, to avoid wierd issues
1696 if passdb.get_global_sam_sid() != domainsid:
1697 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))
1699 domain_info = s4_passdb.domain_info()
1700 if domain_info["dom_sid"] != domainsid:
1701 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))
1703 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1704 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()))
1706 # Ensure we can read this directly, and via the smbd VFS
1707 for direct_db_access in [True, False]:
1708 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1709 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1710 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1712 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1713 fsacl_sddl = fsacl.as_sddl(domainsid)
1714 if fsacl_sddl != SYSVOL_ACL:
1715 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))
1717 # Check acls on Policy folder and policies folders
1718 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1722 def interface_ips_v4(lp):
1723 """return only IPv4 IPs"""
1724 ips = samba.interface_ips(lp, False)
1727 if i.find(':') == -1:
1732 def interface_ips_v6(lp):
1733 """return only IPv6 IPs"""
1734 ips = samba.interface_ips(lp, False)
1737 if i.find(':') != -1:
1742 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1744 targetdir=None, samdb_fill=FILL_FULL,
1745 hostip=None, hostip6=None,
1746 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1747 domainguid=None, policyguid=None, policyguid_dc=None,
1748 invocationid=None, machinepass=None, ntdsguid=None,
1749 dns_backend=None, dnspass=None,
1750 serverrole=None, dom_for_fun_level=None,
1751 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1752 # create/adapt the group policy GUIDs
1753 # Default GUID for default policy are described at
1754 # "How Core Group Policy Works"
1755 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1756 if policyguid is None:
1757 policyguid = DEFAULT_POLICY_GUID
1758 policyguid = policyguid.upper()
1759 if policyguid_dc is None:
1760 policyguid_dc = DEFAULT_DC_POLICY_GUID
1761 policyguid_dc = policyguid_dc.upper()
1763 if invocationid is None:
1764 invocationid = str(uuid.uuid4())
1766 if krbtgtpass is None:
1767 krbtgtpass = samba.generate_random_machine_password(128, 255)
1768 if machinepass is None:
1769 machinepass = samba.generate_random_machine_password(128, 255)
1771 dnspass = samba.generate_random_password(128, 255)
1773 samdb.transaction_start()
1775 samdb = fill_samdb(samdb, lp, names, logger=logger,
1777 policyguid=policyguid, policyguid_dc=policyguid_dc,
1778 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1779 invocationid=invocationid, machinepass=machinepass,
1780 dns_backend=dns_backend, dnspass=dnspass,
1781 ntdsguid=ntdsguid, serverrole=serverrole,
1782 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1783 next_rid=next_rid, dc_rid=dc_rid)
1785 # Set up group policies (domain policy and domain controller
1787 if serverrole == "active directory domain controller":
1788 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1791 samdb.transaction_cancel()
1794 samdb.transaction_commit()
1796 if serverrole == "active directory domain controller":
1797 # Continue setting up sysvol for GPO. This appears to require being
1798 # outside a transaction.
1799 if not skip_sysvolacl:
1800 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1801 paths.root_gid, names.domainsid, names.dnsdomain,
1802 names.domaindn, lp, use_ntvfs)
1804 logger.info("Setting acl on sysvol skipped")
1806 secretsdb_self_join(secrets_ldb, domain=names.domain,
1807 realm=names.realm, dnsdomain=names.dnsdomain,
1808 netbiosname=names.netbiosname, domainsid=names.domainsid,
1809 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1811 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1812 # In future, this might be determined from some configuration
1813 kerberos_enctypes = str(ENC_ALL_TYPES)
1816 msg = ldb.Message(ldb.Dn(samdb,
1817 samdb.searchone("distinguishedName",
1818 expression="samAccountName=%s$" % names.netbiosname,
1819 scope=ldb.SCOPE_SUBTREE)))
1820 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1821 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1822 name="msDS-SupportedEncryptionTypes")
1824 except ldb.LdbError, (enum, estr):
1825 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1826 # It might be that this attribute does not exist in this schema
1829 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1830 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1831 dnspass=dnspass, os_level=dom_for_fun_level,
1832 targetdir=targetdir, fill_level=samdb_fill)
1834 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1835 attribute="objectGUID")
1836 assert isinstance(domainguid, str)
1838 lastProvisionUSNs = get_last_provision_usn(samdb)
1839 maxUSN = get_max_usn(samdb, str(names.rootdn))
1840 if lastProvisionUSNs is not None:
1841 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1843 set_provision_usn(samdb, 0, maxUSN, invocationid)
1845 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1846 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1847 { 'NTDSGUID' : names.ntdsguid })
1849 # fix any dangling GUIDs from the provision
1850 logger.info("Fixing provision GUIDs")
1851 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1853 samdb.transaction_start()
1855 # a small number of GUIDs are missing because of ordering issues in the
1857 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1858 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1859 scope=ldb.SCOPE_BASE,
1860 attrs=['defaultObjectCategory'])
1861 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1862 scope=ldb.SCOPE_ONELEVEL,
1863 attrs=['ipsecOwnersReference',
1864 'ipsecFilterReference',
1865 'ipsecISAKMPReference',
1866 'ipsecNegotiationPolicyReference',
1867 'ipsecNFAReference'])
1868 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
1869 attrs=['attributeId', 'governsId']) != 0:
1870 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
1872 samdb.transaction_cancel()
1875 samdb.transaction_commit()
1879 "ROLE_STANDALONE": "standalone server",
1880 "ROLE_DOMAIN_MEMBER": "member server",
1881 "ROLE_DOMAIN_BDC": "active directory domain controller",
1882 "ROLE_DOMAIN_PDC": "active directory domain controller",
1883 "dc": "active directory domain controller",
1884 "member": "member server",
1885 "domain controller": "active directory domain controller",
1886 "active directory domain controller": "active directory domain controller",
1887 "member server": "member server",
1888 "standalone": "standalone server",
1889 "standalone server": "standalone server",
1893 def sanitize_server_role(role):
1894 """Sanitize a server role name.
1896 :param role: Server role
1897 :raise ValueError: If the role can not be interpreted
1898 :return: Sanitized server role (one of "member server",
1899 "active directory domain controller", "standalone server")
1902 return _ROLES_MAP[role]
1904 raise ValueError(role)
1907 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1909 """Create AD entries for the fake ypserver.
1911 This is needed for being able to manipulate posix attrs via ADUC.
1913 samdb.transaction_start()
1915 logger.info("Setting up fake yp server settings")
1916 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1917 "DOMAINDN": domaindn,
1918 "NETBIOSNAME": netbiosname,
1919 "NISDOMAIN": nisdomain,
1922 samdb.transaction_cancel()
1925 samdb.transaction_commit()
1928 def provision(logger, session_info, smbconf=None,
1929 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1930 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1931 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1932 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1933 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1934 dns_backend=None, dns_forwarder=None, dnspass=None,
1935 invocationid=None, machinepass=None, ntdsguid=None,
1936 root=None, nobody=None, users=None, backup=None, aci=None,
1937 serverrole=None, dom_for_fun_level=None, backend_type=None,
1938 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1939 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1940 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
1941 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, ldap_backend_extra_port=None):
1944 :note: caution, this wipes all existing data!
1948 serverrole = sanitize_server_role(serverrole)
1950 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1952 if ldapadminpass is None:
1953 # Make a new, random password between Samba and it's LDAP server
1954 ldapadminpass = samba.generate_random_password(128, 255)
1956 if backend_type is None:
1957 backend_type = "ldb"
1959 if domainsid is None:
1960 domainsid = security.random_sid()
1962 root_uid = findnss_uid([root or "root"])
1963 nobody_uid = findnss_uid([nobody or "nobody"])
1964 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1965 root_gid = pwd.getpwuid(root_uid).pw_gid
1968 bind_gid = findnss_gid(["bind", "named"])
1972 if targetdir is not None:
1973 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1974 elif smbconf is None:
1975 smbconf = samba.param.default_path()
1976 if not os.path.exists(os.path.dirname(smbconf)):
1977 os.makedirs(os.path.dirname(smbconf))
1979 server_services = []
1982 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1984 if dns_backend != "SAMBA_INTERNAL":
1985 server_services.append("-dns")
1987 if dns_forwarder is not None:
1988 global_param["dns forwarder"] = [dns_forwarder]
1991 server_services.append("+smb")
1992 server_services.append("-s3fs")
1993 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1995 if len(server_services) > 0:
1996 global_param["server services"] = server_services
1998 # only install a new smb.conf if there isn't one there already
1999 if os.path.exists(smbconf):
2000 # if Samba Team members can't figure out the weird errors
2001 # loading an empty smb.conf gives, then we need to be smarter.
2002 # Pretend it just didn't exist --abartlet
2003 f = open(smbconf, 'r')
2005 data = f.read().lstrip()
2008 if data is None or data == "":
2009 make_smbconf(smbconf, hostname, domain, realm,
2010 targetdir, serverrole=serverrole,
2011 eadb=useeadb, use_ntvfs=use_ntvfs,
2012 lp=lp, global_param=global_param)
2014 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2015 serverrole=serverrole,
2016 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2019 lp = samba.param.LoadParm()
2021 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2022 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2023 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2024 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2025 paths = provision_paths_from_lp(lp, names.dnsdomain)
2027 paths.bind_gid = bind_gid
2028 paths.root_uid = root_uid;
2029 paths.root_gid = root_gid
2032 logger.info("Looking up IPv4 addresses")
2033 hostips = interface_ips_v4(lp)
2034 if len(hostips) > 0:
2036 if len(hostips) > 1:
2037 logger.warning("More than one IPv4 address found. Using %s",
2039 if hostip == "127.0.0.1":
2042 logger.warning("No IPv4 address will be assigned")
2045 logger.info("Looking up IPv6 addresses")
2046 hostips = interface_ips_v6(lp)
2048 hostip6 = hostips[0]
2049 if len(hostips) > 1:
2050 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2052 logger.warning("No IPv6 address will be assigned")
2054 names.hostip = hostip
2055 names.hostip6 = hostip6
2056 names.domainguid = domainguid
2057 names.domainsid = domainsid
2058 names.forestsid = domainsid
2060 if serverrole is None:
2061 serverrole = lp.get("server role")
2063 if not os.path.exists(paths.private_dir):
2064 os.mkdir(paths.private_dir)
2065 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
2066 os.makedirs(os.path.join(paths.private_dir, "tls"), 0700)
2067 if not os.path.exists(paths.state_dir):
2068 os.mkdir(paths.state_dir)
2070 if paths.sysvol and not os.path.exists(paths.sysvol):
2071 os.makedirs(paths.sysvol, 0775)
2073 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2075 schema = Schema(domainsid, invocationid=invocationid,
2076 schemadn=names.schemadn)
2078 if backend_type == "ldb":
2079 provision_backend = LDBBackend(backend_type, paths=paths,
2081 names=names, logger=logger)
2082 elif backend_type == "existing":
2083 # If support for this is ever added back, then the URI will need to be
2085 provision_backend = ExistingBackend(backend_type, paths=paths,
2087 names=names, logger=logger,
2088 ldap_backend_forced_uri=ldap_backend_forced_uri)
2089 elif backend_type == "fedora-ds":
2090 provision_backend = FDSBackend(backend_type, paths=paths,
2092 names=names, logger=logger, domainsid=domainsid,
2093 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2094 slapd_path=slapd_path,
2096 elif backend_type == "openldap":
2097 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2099 names=names, logger=logger, domainsid=domainsid,
2100 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2101 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2102 ldap_backend_extra_port=ldap_backend_extra_port,
2103 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2104 ldap_backend_forced_uri=ldap_backend_forced_uri)
2106 raise ValueError("Unknown LDAP backend type selected")
2108 provision_backend.init()
2109 provision_backend.start()
2111 # only install a new shares config db if there is none
2112 if not os.path.exists(paths.shareconf):
2113 logger.info("Setting up share.ldb")
2114 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2115 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2117 logger.info("Setting up secrets.ldb")
2118 secrets_ldb = setup_secretsdb(paths,
2119 session_info=session_info,
2120 backend_credentials=provision_backend.credentials, lp=lp)
2123 logger.info("Setting up the registry")
2124 setup_registry(paths.hklm, session_info, lp=lp)
2126 logger.info("Setting up the privileges database")
2127 setup_privileges(paths.privilege, session_info, lp=lp)
2129 logger.info("Setting up idmap db")
2130 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2132 setup_name_mappings(idmap, sid=str(domainsid),
2133 root_uid=root_uid, nobody_uid=nobody_uid,
2134 users_gid=users_gid, root_gid=root_gid)
2136 logger.info("Setting up SAM db")
2137 samdb = setup_samdb(paths.samdb, session_info,
2138 provision_backend, lp, names, logger=logger,
2139 serverrole=serverrole,
2140 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2142 if serverrole == "active directory domain controller":
2143 if paths.netlogon is None:
2144 raise MissingShareError("netlogon", paths.smbconf)
2146 if paths.sysvol is None:
2147 raise MissingShareError("sysvol", paths.smbconf)
2149 if not os.path.isdir(paths.netlogon):
2150 os.makedirs(paths.netlogon, 0755)
2152 if adminpass is None:
2153 adminpass = samba.generate_random_password(12, 32)
2154 adminpass_generated = True
2156 adminpass = unicode(adminpass, 'utf-8')
2157 adminpass_generated = False
2159 if samdb_fill == FILL_FULL:
2160 provision_fill(samdb, secrets_ldb, logger, names, paths,
2161 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2162 hostip=hostip, hostip6=hostip6,
2163 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2164 krbtgtpass=krbtgtpass,
2165 policyguid=policyguid, policyguid_dc=policyguid_dc,
2166 invocationid=invocationid, machinepass=machinepass,
2167 ntdsguid=ntdsguid, dns_backend=dns_backend,
2168 dnspass=dnspass, serverrole=serverrole,
2169 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2170 lp=lp, use_ntvfs=use_ntvfs,
2171 skip_sysvolacl=skip_sysvolacl)
2173 create_krb5_conf(paths.krb5conf,
2174 dnsdomain=names.dnsdomain, hostname=names.hostname,
2176 logger.info("A Kerberos configuration suitable for Samba AD has been "
2177 "generated at %s", paths.krb5conf)
2179 if serverrole == "active directory domain controller":
2180 create_dns_update_list(lp, logger, paths)
2182 backend_result = provision_backend.post_setup()
2183 provision_backend.shutdown()
2186 secrets_ldb.transaction_cancel()
2189 # Now commit the secrets.ldb to disk
2190 secrets_ldb.transaction_commit()
2192 # the commit creates the dns.keytab, now chown it
2193 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2194 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2196 os.chmod(dns_keytab_path, 0640)
2197 os.chown(dns_keytab_path, -1, paths.bind_gid)
2199 if not os.environ.has_key('SAMBA_SELFTEST'):
2200 logger.info("Failed to chown %s to bind gid %u",
2201 dns_keytab_path, paths.bind_gid)
2203 result = ProvisionResult()
2204 result.server_role = serverrole
2205 result.domaindn = domaindn
2206 result.paths = paths
2207 result.names = names
2209 result.samdb = samdb
2210 result.idmap = idmap
2211 result.domainsid = str(domainsid)
2213 if samdb_fill == FILL_FULL:
2214 result.adminpass_generated = adminpass_generated
2215 result.adminpass = adminpass
2217 result.adminpass_generated = False
2218 result.adminpass = None
2220 result.backend_result = backend_result
2223 provision_fake_ypserver(logger=logger, samdb=samdb,
2224 domaindn=names.domaindn, netbiosname=names.netbiosname,
2225 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2230 def provision_become_dc(smbconf=None, targetdir=None,
2231 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2232 serverdn=None, domain=None, hostname=None, domainsid=None,
2233 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2234 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2235 dns_backend=None, root=None, nobody=None, users=None,
2236 backup=None, serverrole=None, ldap_backend=None,
2237 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2239 logger = logging.getLogger("provision")
2240 samba.set_debug_level(debuglevel)
2242 res = provision(logger, system_session(),
2243 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2244 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2245 configdn=configdn, serverdn=serverdn, domain=domain,
2246 hostname=hostname, hostip=None, domainsid=domainsid,
2247 machinepass=machinepass,
2248 serverrole="active directory domain controller",
2249 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2250 use_ntvfs=use_ntvfs)
2251 res.lp.set("debuglevel", str(debuglevel))
2255 def create_krb5_conf(path, dnsdomain, hostname, realm):
2256 """Write out a file containing a valid krb5.conf file
2258 :param path: Path of the new krb5.conf file.
2259 :param dnsdomain: DNS Domain name
2260 :param hostname: Local hostname
2261 :param realm: Realm name
2263 setup_file(setup_path("krb5.conf"), path, {
2264 "DNSDOMAIN": dnsdomain,
2265 "HOSTNAME": hostname,
2270 class ProvisioningError(Exception):
2271 """A generic provision error."""
2273 def __init__(self, value):
2277 return "ProvisioningError: " + self.value
2280 class InvalidNetbiosName(Exception):
2281 """A specified name was not a valid NetBIOS name."""
2283 def __init__(self, name):
2284 super(InvalidNetbiosName, self).__init__(
2285 "The name '%r' is not a valid NetBIOS name" % name)
2288 class MissingShareError(ProvisioningError):
2290 def __init__(self, name, smbconf):
2291 super(MissingShareError, self).__init__(
2292 "Existing smb.conf does not have a [%s] share, but you are "
2293 "configuring a DC. Please remove %s or add the share manually." %