1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba AD 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
46 from samba.auth import system_session, admin_session
48 from samba.samba3 import smbd, passdb
49 from samba.samba3 import param as s3param
50 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
54 check_all_substituted,
55 is_valid_netbios_char,
62 from samba.dcerpc import security, misc
63 from samba.dcerpc.misc import (
67 from samba.dsdb import (
68 DS_DOMAIN_FUNCTION_2003,
69 DS_DOMAIN_FUNCTION_2008_R2,
72 from samba.idmap import IDmapDB
73 from samba.ms_display_specifiers import read_ms_ldif
74 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
75 from samba.ndr import ndr_pack, ndr_unpack
76 from samba.provision.backend import (
82 from samba.descriptor import (
84 get_config_descriptor,
85 get_config_partitions_descriptor,
86 get_config_sites_descriptor,
87 get_config_ntds_quotas_descriptor,
88 get_config_delete_protected1_descriptor,
89 get_config_delete_protected1wd_descriptor,
90 get_config_delete_protected2_descriptor,
91 get_domain_descriptor,
92 get_domain_infrastructure_descriptor,
93 get_domain_builtin_descriptor,
94 get_domain_computers_descriptor,
95 get_domain_users_descriptor,
96 get_domain_controllers_descriptor,
97 get_domain_delete_protected1_descriptor,
98 get_domain_delete_protected2_descriptor,
99 get_dns_partition_descriptor,
100 get_dns_forest_microsoft_dns_descriptor,
101 get_dns_domain_microsoft_dns_descriptor,
103 from samba.provision.common import (
112 from samba.provision.sambadns import (
115 create_dns_update_list
119 import samba.registry
120 from samba.schema import Schema
121 from samba.samdb import SamDB
122 from samba.dbchecker import dbcheck
123 from samba.provision.kerberos import create_kdc_conf
125 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
126 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
127 DEFAULTSITE = "Default-First-Site-Name"
128 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
131 class ProvisionPaths(object):
134 self.shareconf = None
145 self.dns_keytab = None
148 self.private_dir = None
149 self.binddns_dir = None
150 self.state_dir = None
153 class ProvisionNames(object):
161 self.dnsforestdn = None
162 self.dnsdomaindn = None
163 self.ldapmanagerdn = None
164 self.dnsdomain = None
166 self.netbiosname = None
171 self.domainsid = None
172 self.forestsid = None
173 self.domainguid = None
177 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
179 """Get key provision parameters (realm, domain, ...) from a given provision
181 :param samdb: An LDB object connected to the sam.ldb file
182 :param secretsdb: An LDB object connected to the secrets.ldb file
183 :param idmapdb: An LDB object connected to the idmap.ldb file
184 :param paths: A list of path to provision object
185 :param smbconf: Path to the smb.conf file
186 :param lp: A LoadParm object
187 :return: A list of key provision parameters
189 names = ProvisionNames()
190 names.adminpass = None
192 # NT domain, kerberos realm, root dn, domain dn, domain dns name
193 names.domain = string.upper(lp.get("workgroup"))
194 names.realm = lp.get("realm")
195 names.dnsdomain = names.realm.lower()
196 basedn = samba.dn_from_dns_name(names.dnsdomain)
197 names.realm = string.upper(names.realm)
199 # Get the netbiosname first (could be obtained from smb.conf in theory)
200 res = secretsdb.search(expression="(flatname=%s)" %
201 names.domain,base="CN=Primary Domains",
202 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
203 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
205 names.smbconf = smbconf
207 # That's a bit simplistic but it's ok as long as we have only 3
209 current = samdb.search(expression="(objectClass=*)",
210 base="", scope=ldb.SCOPE_BASE,
211 attrs=["defaultNamingContext", "schemaNamingContext",
212 "configurationNamingContext","rootDomainNamingContext",
215 names.configdn = current[0]["configurationNamingContext"][0]
216 names.schemadn = current[0]["schemaNamingContext"][0]
217 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
218 current[0]["defaultNamingContext"][0]))):
219 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
220 "is not the same ..." % (paths.samdb,
221 str(current[0]["defaultNamingContext"][0]),
222 paths.smbconf, basedn)))
224 names.domaindn=current[0]["defaultNamingContext"][0]
225 names.rootdn=current[0]["rootDomainNamingContext"][0]
226 names.ncs=current[0]["namingContexts"]
227 names.dnsforestdn = None
228 names.dnsdomaindn = None
230 for i in range(0, len(names.ncs)):
233 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
234 if nc == dnsforestdn:
235 names.dnsforestdn = dnsforestdn
238 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
239 if nc == dnsdomaindn:
240 names.dnsdomaindn = dnsdomaindn
244 res3 = samdb.search(expression="(objectClass=site)",
245 base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
246 names.sitename = str(res3[0]["cn"])
248 # dns hostname and server dn
249 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
250 base="OU=Domain Controllers,%s" % basedn,
251 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
253 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
255 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
257 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
258 attrs=[], base=names.configdn)
259 names.serverdn = str(server_res[0].dn)
261 # invocation id/objectguid
262 res5 = samdb.search(expression="(objectClass=*)",
263 base="CN=NTDS Settings,%s" % str(names.serverdn),
264 scope=ldb.SCOPE_BASE,
265 attrs=["invocationID", "objectGUID"])
266 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
267 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
270 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
271 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
272 "objectSid","msDS-Behavior-Version" ])
273 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
274 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
275 names.forestsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
276 if res6[0].get("msDS-Behavior-Version") is None or \
277 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
278 names.domainlevel = DS_DOMAIN_FUNCTION_2000
280 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
283 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
284 base="CN=Policies,CN=System," + basedn,
285 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
286 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
288 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
289 base="CN=Policies,CN=System," + basedn,
290 scope=ldb.SCOPE_ONELEVEL,
291 attrs=["cn","displayName"])
293 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
295 names.policyid_dc = None
297 res9 = idmapdb.search(expression="(cn=%s-%s)" %
298 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
299 attrs=["xidNumber", "type"])
301 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
302 if res9[0]["type"][0] == "ID_TYPE_BOTH":
303 names.root_gid = res9[0]["xidNumber"][0]
305 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
307 res10 = samdb.search(expression="(samaccountname=dns)",
308 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
309 controls=["search_options:1:2"])
311 has_legacy_dns_account = True
313 has_legacy_dns_account = False
315 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
316 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
317 controls=["search_options:1:2"])
319 has_dns_account = True
321 has_dns_account = False
323 if names.dnsdomaindn is not None:
325 names.dns_backend = 'BIND9_DLZ'
327 names.dns_backend = 'SAMBA_INTERNAL'
328 elif has_dns_account or has_legacy_dns_account:
329 names.dns_backend = 'BIND9_FLATFILE'
331 names.dns_backend = 'NONE'
333 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
334 names.name_map['DnsAdmins'] = str(dns_admins_sid)
339 def update_provision_usn(samdb, low, high, id, replace=False):
340 """Update the field provisionUSN in sam.ldb
342 This field is used to track range of USN modified by provision and
344 This value is used afterward by next provision to figure out if
345 the field have been modified since last provision.
347 :param samdb: An LDB object connect to sam.ldb
348 :param low: The lowest USN modified by this upgrade
349 :param high: The highest USN modified by this upgrade
350 :param id: The invocation id of the samba's dc
351 :param replace: A boolean indicating if the range should replace any
352 existing one or appended (default)
357 entry = samdb.search(base="@PROVISION",
358 scope=ldb.SCOPE_BASE,
359 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
360 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
361 if not re.search(';', e):
362 e = "%s;%s" % (e, id)
365 tab.append("%s-%s;%s" % (low, high, id))
366 delta = ldb.Message()
367 delta.dn = ldb.Dn(samdb, "@PROVISION")
368 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
369 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
370 entry = samdb.search(expression='provisionnerID=*',
371 base="@PROVISION", scope=ldb.SCOPE_BASE,
372 attrs=["provisionnerID"])
373 if len(entry) == 0 or len(entry[0]) == 0:
374 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
378 def set_provision_usn(samdb, low, high, id):
379 """Set the field provisionUSN in sam.ldb
380 This field is used to track range of USN modified by provision and
382 This value is used afterward by next provision to figure out if
383 the field have been modified since last provision.
385 :param samdb: An LDB object connect to sam.ldb
386 :param low: The lowest USN modified by this upgrade
387 :param high: The highest USN modified by this upgrade
388 :param id: The invocationId of the provision"""
391 tab.append("%s-%s;%s" % (low, high, id))
393 delta = ldb.Message()
394 delta.dn = ldb.Dn(samdb, "@PROVISION")
395 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
396 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
400 def get_max_usn(samdb,basedn):
401 """ This function return the biggest USN present in the provision
403 :param samdb: A LDB object pointing to the sam.ldb
404 :param basedn: A string containing the base DN of the provision
406 :return: The biggest USN in the provision"""
408 res = samdb.search(expression="objectClass=*",base=basedn,
409 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
410 controls=["search_options:1:2",
411 "server_sort:1:1:uSNChanged",
412 "paged_results:1:1"])
413 return res[0]["uSNChanged"]
416 def get_last_provision_usn(sam):
417 """Get USNs ranges modified by a provision or an upgradeprovision
419 :param sam: An LDB object pointing to the sam.ldb
420 :return: a dictionary which keys are invocation id and values are an array
421 of integer representing the different ranges
424 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
425 base="@PROVISION", scope=ldb.SCOPE_BASE,
426 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
427 except ldb.LdbError, (ecode, emsg):
428 if ecode == ldb.ERR_NO_SUCH_OBJECT:
435 if entry[0].get("provisionnerID"):
436 for e in entry[0]["provisionnerID"]:
438 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
439 tab1 = str(r).split(';')
444 if (len(myids) > 0 and id not in myids):
446 tab2 = p.split(tab1[0])
447 if range.get(id) is None:
449 range[id].append(tab2[0])
450 range[id].append(tab2[1])
456 class ProvisionResult(object):
457 """Result of a provision.
459 :ivar server_role: The server role
460 :ivar paths: ProvisionPaths instance
461 :ivar domaindn: The domain dn, as string
465 self.server_role = None
472 self.domainsid = None
473 self.adminpass_generated = None
474 self.adminpass = None
475 self.backend_result = None
477 def report_logger(self, logger):
478 """Report this provision result to a logger."""
480 "Once the above files are installed, your Samba AD server will "
482 if self.adminpass_generated:
483 logger.info("Admin password: %s", self.adminpass)
484 logger.info("Server Role: %s", self.server_role)
485 logger.info("Hostname: %s", self.names.hostname)
486 logger.info("NetBIOS Domain: %s", self.names.domain)
487 logger.info("DNS Domain: %s", self.names.dnsdomain)
488 logger.info("DOMAIN SID: %s", self.domainsid)
490 if self.backend_result:
491 self.backend_result.report_logger(logger)
494 def check_install(lp, session_info, credentials):
495 """Check whether the current install seems ok.
497 :param lp: Loadparm context
498 :param session_info: Session information
499 :param credentials: Credentials
501 if lp.get("realm") == "":
502 raise Exception("Realm empty")
503 samdb = Ldb(lp.samdb_url(), session_info=session_info,
504 credentials=credentials, lp=lp)
505 if len(samdb.search("(cn=Administrator)")) != 1:
506 raise ProvisioningError("No administrator account found")
509 def findnss(nssfn, names):
510 """Find a user or group from a list of possibilities.
512 :param nssfn: NSS Function to try (should raise KeyError if not found)
513 :param names: Names to check.
514 :return: Value return by first names list.
521 raise KeyError("Unable to find user/group in %r" % names)
524 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
525 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
528 def provision_paths_from_lp(lp, dnsdomain):
529 """Set the default paths for provisioning.
531 :param lp: Loadparm context.
532 :param dnsdomain: DNS Domain name
534 paths = ProvisionPaths()
535 paths.private_dir = lp.get("private dir")
536 paths.binddns_dir = lp.get("binddns dir")
537 paths.state_dir = lp.get("state directory")
539 # This is stored without path prefix for the "privateKeytab" attribute in
540 # "secrets_dns.ldif".
541 paths.dns_keytab = "dns.keytab"
542 paths.keytab = "secrets.keytab"
544 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
545 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
546 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
547 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
548 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
549 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
550 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
551 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
552 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
553 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
554 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
556 paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
557 paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
558 paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
559 paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
561 paths.hklm = "hklm.ldb"
562 paths.hkcr = "hkcr.ldb"
563 paths.hkcu = "hkcu.ldb"
564 paths.hku = "hku.ldb"
565 paths.hkpd = "hkpd.ldb"
566 paths.hkpt = "hkpt.ldb"
567 paths.sysvol = lp.get("path", "sysvol")
568 paths.netlogon = lp.get("path", "netlogon")
569 paths.smbconf = lp.configfile
573 def determine_netbios_name(hostname):
574 """Determine a netbios name from a hostname."""
575 # remove forbidden chars and force the length to be <16
576 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
577 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
580 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
581 serverrole=None, rootdn=None, domaindn=None, configdn=None,
582 schemadn=None, serverdn=None, sitename=None,
583 domain_names_forced=False):
584 """Guess configuration settings to use."""
587 hostname = socket.gethostname().split(".")[0]
589 netbiosname = lp.get("netbios name")
590 if netbiosname is None:
591 netbiosname = determine_netbios_name(hostname)
592 netbiosname = netbiosname.upper()
593 if not valid_netbios_name(netbiosname):
594 raise InvalidNetbiosName(netbiosname)
596 if dnsdomain is None:
597 dnsdomain = lp.get("realm")
598 if dnsdomain is None or dnsdomain == "":
599 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
601 dnsdomain = dnsdomain.lower()
603 if serverrole is None:
604 serverrole = lp.get("server role")
605 if serverrole is None:
606 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
608 serverrole = serverrole.lower()
610 realm = dnsdomain.upper()
612 if lp.get("realm") == "":
613 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
615 if lp.get("realm").upper() != realm:
616 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))
618 if lp.get("server role").lower() != serverrole:
619 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))
621 if serverrole == "active directory domain controller":
623 # This will, for better or worse, default to 'WORKGROUP'
624 domain = lp.get("workgroup")
625 domain = domain.upper()
627 if lp.get("workgroup").upper() != domain:
628 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))
631 domaindn = samba.dn_from_dns_name(dnsdomain)
633 if domain == netbiosname:
634 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
638 domaindn = "DC=" + netbiosname
640 if not valid_netbios_name(domain):
641 raise InvalidNetbiosName(domain)
643 if hostname.upper() == realm:
644 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
645 if netbiosname.upper() == realm:
646 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
647 if domain == realm and not domain_names_forced:
648 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
654 configdn = "CN=Configuration," + rootdn
656 schemadn = "CN=Schema," + configdn
659 sitename = DEFAULTSITE
661 names = ProvisionNames()
662 names.rootdn = rootdn
663 names.domaindn = domaindn
664 names.configdn = configdn
665 names.schemadn = schemadn
666 names.ldapmanagerdn = "CN=Manager," + rootdn
667 names.dnsdomain = dnsdomain
668 names.domain = domain
670 names.netbiosname = netbiosname
671 names.hostname = hostname
672 names.sitename = sitename
673 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
674 netbiosname, sitename, configdn)
678 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
679 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
681 """Create a new smb.conf file based on a couple of basic settings.
683 assert smbconf is not None
686 hostname = socket.gethostname().split(".")[0]
688 netbiosname = determine_netbios_name(hostname)
690 if serverrole is None:
691 serverrole = "standalone server"
693 assert domain is not None
694 domain = domain.upper()
696 assert realm is not None
697 realm = realm.upper()
700 "netbios name": netbiosname,
703 "server role": serverrole,
707 lp = samba.param.LoadParm()
708 #Load non-existent file
709 if os.path.exists(smbconf):
712 if global_param is not None:
713 for ent in global_param:
714 if global_param[ent] is not None:
715 global_settings[ent] = " ".join(global_param[ent])
717 if targetdir is not None:
718 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
719 global_settings["lock dir"] = os.path.abspath(targetdir)
720 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
721 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
723 lp.set("lock dir", os.path.abspath(targetdir))
724 lp.set("state directory", global_settings["state directory"])
725 lp.set("cache directory", global_settings["cache directory"])
728 if use_ntvfs and not lp.get("posix:eadb"):
729 if targetdir is not None:
730 privdir = os.path.join(targetdir, "private")
732 privdir = lp.get("private dir")
733 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
734 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
735 if targetdir is not None:
736 statedir = os.path.join(targetdir, "state")
738 statedir = lp.get("state directory")
739 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
742 if serverrole == "active directory domain controller":
743 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
744 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
747 global_settings["passdb backend"] = "samba_dsdb"
749 f = open(smbconf, 'w')
751 f.write("[globals]\n")
752 for key, val in global_settings.iteritems():
753 f.write("\t%s = %s\n" % (key, val))
756 for name, path in shares.iteritems():
757 f.write("[%s]\n" % name)
758 f.write("\tpath = %s\n" % path)
759 f.write("\tread only = no\n")
763 # reload the smb.conf
766 # and dump it without any values that are the default
767 # this ensures that any smb.conf parameters that were set
768 # on the provision/join command line are set in the resulting smb.conf
769 lp.dump(False, smbconf)
772 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
773 users_gid, root_gid):
774 """setup reasonable name mappings for sam names to unix names.
776 :param samdb: SamDB object.
777 :param idmap: IDmap db object.
778 :param sid: The domain sid.
779 :param domaindn: The domain DN.
780 :param root_uid: uid of the UNIX root user.
781 :param nobody_uid: uid of the UNIX nobody user.
782 :param users_gid: gid of the UNIX users group.
783 :param root_gid: gid of the UNIX root group.
785 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
787 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
788 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
791 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
792 provision_backend, names, serverrole,
794 """Setup the partitions for the SAM database.
796 Alternatively, provision() may call this, and then populate the database.
798 :note: This will wipe the Sam Database!
800 :note: This function always removes the local SAM LDB file. The erase
801 parameter controls whether to erase the existing data, which
802 may not be stored locally but in LDAP.
805 assert session_info is not None
807 # We use options=["modules:"] to stop the modules loading - we
808 # just want to wipe and re-initialise the database, not start it up
811 os.unlink(samdb_path)
815 samdb = Ldb(url=samdb_path, session_info=session_info,
816 lp=lp, options=["modules:"])
818 ldap_backend_line = "# No LDAP backend"
819 if provision_backend.type != "ldb":
820 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
822 samdb.transaction_start()
824 logger.info("Setting up sam.ldb partitions and settings")
825 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
826 "LDAP_BACKEND_LINE": ldap_backend_line
830 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
831 "BACKEND_TYPE": provision_backend.type,
832 "SERVER_ROLE": serverrole
835 logger.info("Setting up sam.ldb rootDSE")
836 setup_samdb_rootdse(samdb, names)
838 samdb.transaction_cancel()
841 samdb.transaction_commit()
844 def secretsdb_self_join(secretsdb, domain,
845 netbiosname, machinepass, domainsid=None,
846 realm=None, dnsdomain=None,
848 key_version_number=1,
849 secure_channel_type=SEC_CHAN_WKSTA):
850 """Add domain join-specific bits to a secrets database.
852 :param secretsdb: Ldb Handle to the secrets database
853 :param machinepass: Machine password
855 attrs = ["whenChanged",
862 if realm is not None:
863 if dnsdomain is None:
864 dnsdomain = realm.lower()
865 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
868 shortname = netbiosname.lower()
870 # We don't need to set msg["flatname"] here, because rdn_name will handle
871 # it, and it causes problems for modifies anyway
872 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
873 msg["secureChannelType"] = [str(secure_channel_type)]
874 msg["objectClass"] = ["top", "primaryDomain"]
875 if dnsname is not None:
876 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
877 msg["realm"] = [realm]
878 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
879 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
880 msg["privateKeytab"] = ["secrets.keytab"]
882 msg["secret"] = [machinepass.encode('utf-8')]
883 msg["samAccountName"] = ["%s$" % netbiosname]
884 msg["secureChannelType"] = [str(secure_channel_type)]
885 if domainsid is not None:
886 msg["objectSid"] = [ndr_pack(domainsid)]
888 # This complex expression tries to ensure that we don't have more
889 # than one record for this SID, realm or netbios domain at a time,
890 # but we don't delete the old record that we are about to modify,
891 # because that would delete the keytab and previous password.
892 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
893 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
894 scope=ldb.SCOPE_ONELEVEL)
897 secretsdb.delete(del_msg.dn)
899 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
902 msg["priorSecret"] = [res[0]["secret"][0]]
904 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
909 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
914 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
920 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
921 secretsdb.modify(msg)
922 secretsdb.rename(res[0].dn, msg.dn)
924 spn = [ 'HOST/%s' % shortname ]
925 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
926 # we are a domain controller then we add servicePrincipalName
927 # entries for the keytab code to update.
928 spn.extend([ 'HOST/%s' % dnsname ])
929 msg["servicePrincipalName"] = spn
934 def setup_secretsdb(paths, session_info, backend_credentials, lp):
935 """Setup the secrets database.
937 :note: This function does not handle exceptions and transaction on purpose,
938 it's up to the caller to do this job.
940 :param path: Path to the secrets database.
941 :param session_info: Session info.
942 :param credentials: Credentials
943 :param lp: Loadparm context
944 :return: LDB handle for the created secrets database
946 if os.path.exists(paths.secrets):
947 os.unlink(paths.secrets)
949 keytab_path = os.path.join(paths.private_dir, paths.keytab)
950 if os.path.exists(keytab_path):
951 os.unlink(keytab_path)
953 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
954 if os.path.exists(bind_dns_keytab_path):
955 os.unlink(bind_dns_keytab_path)
957 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
958 if os.path.exists(dns_keytab_path):
959 os.unlink(dns_keytab_path)
963 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
965 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
966 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
967 secrets_ldb.transaction_start()
969 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
971 if (backend_credentials is not None and
972 backend_credentials.authentication_requested()):
973 if backend_credentials.get_bind_dn() is not None:
974 setup_add_ldif(secrets_ldb,
975 setup_path("secrets_simple_ldap.ldif"), {
976 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
977 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
980 setup_add_ldif(secrets_ldb,
981 setup_path("secrets_sasl_ldap.ldif"), {
982 "LDAPADMINUSER": backend_credentials.get_username(),
983 "LDAPADMINREALM": backend_credentials.get_realm(),
984 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
987 secrets_ldb.transaction_cancel()
992 def setup_privileges(path, session_info, lp):
993 """Setup the privileges database.
995 :param path: Path to the privileges database.
996 :param session_info: Session info.
997 :param credentials: Credentials
998 :param lp: Loadparm context
999 :return: LDB handle for the created secrets database
1001 if os.path.exists(path):
1003 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1004 privilege_ldb.erase()
1005 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1008 def setup_registry(path, session_info, lp):
1009 """Setup the registry.
1011 :param path: Path to the registry database
1012 :param session_info: Session information
1013 :param credentials: Credentials
1014 :param lp: Loadparm context
1016 reg = samba.registry.Registry()
1017 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1018 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1019 provision_reg = setup_path("provision.reg")
1020 assert os.path.exists(provision_reg)
1021 reg.diff_apply(provision_reg)
1024 def setup_idmapdb(path, session_info, lp):
1025 """Setup the idmap database.
1027 :param path: path to the idmap database
1028 :param session_info: Session information
1029 :param credentials: Credentials
1030 :param lp: Loadparm context
1032 if os.path.exists(path):
1035 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1037 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1041 def setup_samdb_rootdse(samdb, names):
1042 """Setup the SamDB rootdse.
1044 :param samdb: Sam Database handle
1046 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1047 "SCHEMADN": names.schemadn,
1048 "DOMAINDN": names.domaindn,
1049 "ROOTDN" : names.rootdn,
1050 "CONFIGDN": names.configdn,
1051 "SERVERDN": names.serverdn,
1055 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1056 dns_backend, dnspass, domainsid, next_rid, invocationid,
1057 policyguid, policyguid_dc,
1058 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1059 """Join a host to its own domain."""
1060 assert isinstance(invocationid, str)
1061 if ntdsguid is not None:
1062 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1069 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1070 "CONFIGDN": names.configdn,
1071 "SCHEMADN": names.schemadn,
1072 "DOMAINDN": names.domaindn,
1073 "SERVERDN": names.serverdn,
1074 "INVOCATIONID": invocationid,
1075 "NETBIOSNAME": names.netbiosname,
1076 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1077 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1078 "DOMAINSID": str(domainsid),
1079 "DCRID": str(dc_rid),
1080 "SAMBA_VERSION_STRING": version,
1081 "NTDSGUID": ntdsguid_line,
1082 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1083 domainControllerFunctionality),
1084 "RIDALLOCATIONSTART": str(next_rid + 100),
1085 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1087 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1088 "POLICYGUID": policyguid,
1089 "POLICYGUID_DC": policyguid_dc,
1090 "DNSDOMAIN": names.dnsdomain,
1091 "DOMAINDN": names.domaindn})
1093 # If we are setting up a subdomain, then this has been replicated in, so we
1094 # don't need to add it
1095 if fill == FILL_FULL:
1096 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1097 "CONFIGDN": names.configdn,
1098 "SCHEMADN": names.schemadn,
1099 "DOMAINDN": names.domaindn,
1100 "SERVERDN": names.serverdn,
1101 "INVOCATIONID": invocationid,
1102 "NETBIOSNAME": names.netbiosname,
1103 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1104 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1105 "DOMAINSID": str(domainsid),
1106 "DCRID": str(dc_rid),
1107 "SAMBA_VERSION_STRING": version,
1108 "NTDSGUID": ntdsguid_line,
1109 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1110 domainControllerFunctionality)})
1112 # Setup fSMORoleOwner entries to point at the newly created DC entry
1113 setup_modify_ldif(samdb,
1114 setup_path("provision_self_join_modify_config.ldif"), {
1115 "CONFIGDN": names.configdn,
1116 "SCHEMADN": names.schemadn,
1117 "DEFAULTSITE": names.sitename,
1118 "NETBIOSNAME": names.netbiosname,
1119 "SERVERDN": names.serverdn,
1122 system_session_info = system_session()
1123 samdb.set_session_info(system_session_info)
1124 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1125 # modify a serverReference under cn=config when we are a subdomain, we must
1126 # be system due to ACLs
1127 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1128 "DOMAINDN": names.domaindn,
1129 "SERVERDN": names.serverdn,
1130 "NETBIOSNAME": names.netbiosname,
1133 samdb.set_session_info(admin_session_info)
1135 if dns_backend != "SAMBA_INTERNAL":
1136 # This is Samba4 specific and should be replaced by the correct
1137 # DNS AD-style setup
1138 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1139 "DNSDOMAIN": names.dnsdomain,
1140 "DOMAINDN": names.domaindn,
1141 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1142 "HOSTNAME" : names.hostname,
1143 "DNSNAME" : '%s.%s' % (
1144 names.netbiosname.lower(), names.dnsdomain.lower())
1148 def getpolicypath(sysvolpath, dnsdomain, guid):
1149 """Return the physical path of policy given its guid.
1151 :param sysvolpath: Path to the sysvol folder
1152 :param dnsdomain: DNS name of the AD domain
1153 :param guid: The GUID of the policy
1154 :return: A string with the complete path to the policy folder
1157 guid = "{%s}" % guid
1158 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1162 def create_gpo_struct(policy_path):
1163 if not os.path.exists(policy_path):
1164 os.makedirs(policy_path, 0775)
1165 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1167 f.write("[General]\r\nVersion=0")
1170 p = os.path.join(policy_path, "MACHINE")
1171 if not os.path.exists(p):
1172 os.makedirs(p, 0775)
1173 p = os.path.join(policy_path, "USER")
1174 if not os.path.exists(p):
1175 os.makedirs(p, 0775)
1178 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1179 """Create the default GPO for a domain
1181 :param sysvolpath: Physical path for the sysvol folder
1182 :param dnsdomain: DNS domain name of the AD domain
1183 :param policyguid: GUID of the default domain policy
1184 :param policyguid_dc: GUID of the default domain controler policy
1186 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1187 create_gpo_struct(policy_path)
1189 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1190 create_gpo_struct(policy_path)
1193 def setup_samdb(path, session_info, provision_backend, lp, names,
1194 logger, fill, serverrole, schema, am_rodc=False):
1195 """Setup a complete SAM Database.
1197 :note: This will wipe the main SAM database file!
1200 # Also wipes the database
1201 setup_samdb_partitions(path, logger=logger, lp=lp,
1202 provision_backend=provision_backend, session_info=session_info,
1203 names=names, serverrole=serverrole)
1205 # Load the database, but don's load the global schema and don't connect
1207 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1208 credentials=provision_backend.credentials, lp=lp,
1209 global_schema=False, am_rodc=am_rodc)
1211 logger.info("Pre-loading the Samba 4 and AD schema")
1213 # Load the schema from the one we computed earlier
1214 samdb.set_schema(schema, write_indices_and_attributes=False)
1216 # Set the NTDS settings DN manually - in order to have it already around
1217 # before the provisioned tree exists and we connect
1218 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1220 # And now we can connect to the DB - the schema won't be loaded from the
1224 except ldb.LdbError, (num, string_error):
1225 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1226 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1230 # But we have to give it one more kick to have it use the schema
1231 # during provision - it needs, now that it is connected, to write
1232 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1233 samdb.set_schema(schema, write_indices_and_attributes=True)
1238 def fill_samdb(samdb, lp, names, logger, policyguid,
1239 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1240 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1241 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1243 if next_rid is None:
1246 # Provision does not make much sense values larger than 1000000000
1247 # as the upper range of the rIDAvailablePool is 1073741823 and
1248 # we don't want to create a domain that cannot allocate rids.
1249 if next_rid < 1000 or next_rid > 1000000000:
1250 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1251 error += "the valid range is %u-%u. The default is %u." % (
1252 1000, 1000000000, 1000)
1253 raise ProvisioningError(error)
1255 # ATTENTION: Do NOT change these default values without discussion with the
1256 # team and/or release manager. They have a big impact on the whole program!
1257 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1259 if dom_for_fun_level is None:
1260 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1262 if dom_for_fun_level > domainControllerFunctionality:
1263 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!")
1265 domainFunctionality = dom_for_fun_level
1266 forestFunctionality = dom_for_fun_level
1268 # Set the NTDS settings DN manually - in order to have it already around
1269 # before the provisioned tree exists and we connect
1270 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1272 # Set the domain functionality levels onto the database.
1273 # Various module (the password_hash module in particular) need
1274 # to know what level of AD we are emulating.
1276 # These will be fixed into the database via the database
1277 # modifictions below, but we need them set from the start.
1278 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1279 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1280 samdb.set_opaque_integer("domainControllerFunctionality",
1281 domainControllerFunctionality)
1283 samdb.set_domain_sid(str(names.domainsid))
1284 samdb.set_invocation_id(invocationid)
1286 logger.info("Adding DomainDN: %s" % names.domaindn)
1288 # impersonate domain admin
1289 admin_session_info = admin_session(lp, str(names.domainsid))
1290 samdb.set_session_info(admin_session_info)
1291 if names.domainguid is not None:
1292 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1294 domainguid_line = ""
1296 descr = b64encode(get_domain_descriptor(names.domainsid))
1297 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1298 "DOMAINDN": names.domaindn,
1299 "DOMAINSID": str(names.domainsid),
1300 "DESCRIPTOR": descr,
1301 "DOMAINGUID": domainguid_line
1304 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1305 "DOMAINDN": names.domaindn,
1306 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1307 "NEXTRID": str(next_rid),
1308 "DEFAULTSITE": names.sitename,
1309 "CONFIGDN": names.configdn,
1310 "POLICYGUID": policyguid,
1311 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1312 "SAMBA_VERSION_STRING": version
1315 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1316 if fill == FILL_FULL:
1317 logger.info("Adding configuration container")
1318 descr = b64encode(get_config_descriptor(names.domainsid))
1319 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1320 "CONFIGDN": names.configdn,
1321 "DESCRIPTOR": descr,
1324 # The LDIF here was created when the Schema object was constructed
1325 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1326 logger.info("Setting up sam.ldb schema")
1327 samdb.add_ldif(schema.schema_dn_add,
1328 controls=["relax:0", ignore_checks_oid])
1329 samdb.modify_ldif(schema.schema_dn_modify,
1330 controls=[ignore_checks_oid])
1331 samdb.write_prefixes_from_schema()
1332 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1333 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1334 {"SCHEMADN": names.schemadn},
1335 controls=["relax:0", ignore_checks_oid])
1337 # Now register this container in the root of the forest
1338 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1339 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1342 samdb.invocation_id = invocationid
1344 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1345 if fill == FILL_FULL:
1346 logger.info("Setting up sam.ldb configuration data")
1348 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid))
1349 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid))
1350 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid))
1351 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid))
1352 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1353 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid))
1355 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1356 "CONFIGDN": names.configdn,
1357 "NETBIOSNAME": names.netbiosname,
1358 "DEFAULTSITE": names.sitename,
1359 "DNSDOMAIN": names.dnsdomain,
1360 "DOMAIN": names.domain,
1361 "SCHEMADN": names.schemadn,
1362 "DOMAINDN": names.domaindn,
1363 "SERVERDN": names.serverdn,
1364 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1365 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1366 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1367 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1368 "SERVICES_DESCRIPTOR": protected1_descr,
1369 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1370 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1371 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1372 "PARTITIONS_DESCRIPTOR": partitions_descr,
1373 "SITES_DESCRIPTOR": sites_descr,
1376 logger.info("Setting up display specifiers")
1377 display_specifiers_ldif = read_ms_ldif(
1378 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1379 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1380 {"CONFIGDN": names.configdn})
1381 check_all_substituted(display_specifiers_ldif)
1382 samdb.add_ldif(display_specifiers_ldif)
1384 logger.info("Modifying display specifiers")
1385 setup_modify_ldif(samdb,
1386 setup_path("provision_configuration_modify.ldif"), {
1387 "CONFIGDN": names.configdn,
1388 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1391 logger.info("Adding users container")
1392 users_desc = b64encode(get_domain_users_descriptor(names.domainsid))
1393 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1394 "DOMAINDN": names.domaindn,
1395 "USERS_DESCRIPTOR": users_desc
1397 logger.info("Modifying users container")
1398 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1399 "DOMAINDN": names.domaindn})
1400 logger.info("Adding computers container")
1401 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid))
1402 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1403 "DOMAINDN": names.domaindn,
1404 "COMPUTERS_DESCRIPTOR": computers_desc
1406 logger.info("Modifying computers container")
1407 setup_modify_ldif(samdb,
1408 setup_path("provision_computers_modify.ldif"), {
1409 "DOMAINDN": names.domaindn})
1410 logger.info("Setting up sam.ldb data")
1411 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid))
1412 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid))
1413 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid))
1414 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid))
1415 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid))
1416 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1417 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1418 "DOMAINDN": names.domaindn,
1419 "NETBIOSNAME": names.netbiosname,
1420 "DEFAULTSITE": names.sitename,
1421 "CONFIGDN": names.configdn,
1422 "SERVERDN": names.serverdn,
1423 "RIDAVAILABLESTART": str(next_rid + 600),
1424 "POLICYGUID_DC": policyguid_dc,
1425 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1426 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1427 "SYSTEM_DESCRIPTOR": system_desc,
1428 "BUILTIN_DESCRIPTOR": builtin_desc,
1429 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1432 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1433 if fill == FILL_FULL:
1434 setup_modify_ldif(samdb,
1435 setup_path("provision_configuration_references.ldif"), {
1436 "CONFIGDN": names.configdn,
1437 "SCHEMADN": names.schemadn})
1439 logger.info("Setting up well known security principals")
1440 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1441 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1442 "CONFIGDN": names.configdn,
1443 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1446 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1447 setup_modify_ldif(samdb,
1448 setup_path("provision_basedn_references.ldif"),
1449 {"DOMAINDN": names.domaindn})
1451 logger.info("Setting up sam.ldb users and groups")
1452 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1453 "DOMAINDN": names.domaindn,
1454 "DOMAINSID": str(names.domainsid),
1455 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1456 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1459 logger.info("Setting up self join")
1460 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1461 invocationid=invocationid,
1462 dns_backend=dns_backend,
1464 machinepass=machinepass,
1465 domainsid=names.domainsid,
1468 policyguid=policyguid,
1469 policyguid_dc=policyguid_dc,
1470 domainControllerFunctionality=domainControllerFunctionality,
1473 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1474 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1475 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1476 assert isinstance(names.ntdsguid, str)
1481 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1482 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)"
1483 SYSVOL_SERVICE="sysvol"
1485 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1486 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1487 for root, dirs, files in os.walk(path, topdown=False):
1489 setntacl(lp, os.path.join(root, name), acl, domsid,
1490 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1492 setntacl(lp, os.path.join(root, name), acl, domsid,
1493 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1496 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1497 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1500 :param sysvol: Physical path for the sysvol folder
1501 :param dnsdomain: The DNS name of the domain
1502 :param domainsid: The SID of the domain
1503 :param domaindn: The DN of the domain (ie. DC=...)
1504 :param samdb: An LDB object on the SAM db
1505 :param lp: an LP object
1508 # Set ACL for GPO root folder
1509 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1510 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1511 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1513 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1514 attrs=["cn", "nTSecurityDescriptor"],
1515 expression="", scope=ldb.SCOPE_ONELEVEL)
1518 acl = ndr_unpack(security.descriptor,
1519 str(policy["nTSecurityDescriptor"])).as_sddl()
1520 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1521 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1522 str(domainsid), use_ntvfs,
1526 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1527 domaindn, lp, use_ntvfs):
1528 """Set the ACL for the sysvol share and the subfolders
1530 :param samdb: An LDB object on the SAM db
1531 :param netlogon: Physical path for the netlogon folder
1532 :param sysvol: Physical path for the sysvol folder
1533 :param uid: The UID of the "Administrator" user
1534 :param gid: The GID of the "Domain adminstrators" group
1535 :param domainsid: The SID of the domain
1536 :param dnsdomain: The DNS name of the domain
1537 :param domaindn: The DN of the domain (ie. DC=...)
1542 s3conf = s3param.get_context()
1543 s3conf.load(lp.configfile)
1545 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1548 smbd.set_simple_acl(file.name, 0755, gid)
1550 if not smbd.have_posix_acls():
1551 # This clue is only strictly correct for RPM and
1552 # Debian-like Linux systems, but hopefully other users
1553 # will get enough clue from it.
1554 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1555 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1557 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1558 "Try the mounting the filesystem with the 'acl' option.")
1560 smbd.chown(file.name, uid, gid)
1562 raise ProvisioningError("Unable to chown a file on your filesystem. "
1563 "You may not be running provision as root.")
1567 # This will ensure that the smbd code we are running when setting ACLs
1568 # is initialised with the smb.conf
1569 s3conf = s3param.get_context()
1570 s3conf.load(lp.configfile)
1571 # ensure we are using the right samba_dsdb passdb backend, no matter what
1572 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1573 passdb.reload_static_pdb()
1575 # ensure that we init the samba_dsdb backend, so the domain sid is
1576 # marked in secrets.tdb
1577 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1579 # now ensure everything matches correctly, to avoid wierd issues
1580 if passdb.get_global_sam_sid() != domainsid:
1581 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))
1583 domain_info = s4_passdb.domain_info()
1584 if domain_info["dom_sid"] != domainsid:
1585 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))
1587 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1588 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()))
1593 os.chown(sysvol, -1, gid)
1599 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1600 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1601 skip_invalid_chown=True, passdb=s4_passdb,
1602 service=SYSVOL_SERVICE)
1603 for root, dirs, files in os.walk(sysvol, topdown=False):
1605 if use_ntvfs and canchown:
1606 os.chown(os.path.join(root, name), -1, gid)
1607 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1608 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1609 passdb=s4_passdb, service=SYSVOL_SERVICE)
1611 if use_ntvfs and canchown:
1612 os.chown(os.path.join(root, name), -1, gid)
1613 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1614 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1615 passdb=s4_passdb, service=SYSVOL_SERVICE)
1617 # Set acls on Policy folder and policies folders
1618 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1620 def acl_type(direct_db_access):
1621 if direct_db_access:
1626 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1627 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1628 fsacl_sddl = fsacl.as_sddl(domainsid)
1629 if fsacl_sddl != acl:
1630 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))
1632 for root, dirs, files in os.walk(path, topdown=False):
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 file %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 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))
1643 fsacl = getntacl(lp, os.path.join(root, name),
1644 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1646 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1647 fsacl_sddl = fsacl.as_sddl(domainsid)
1648 if fsacl_sddl != acl:
1649 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))
1652 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1654 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1657 :param sysvol: Physical path for the sysvol folder
1658 :param dnsdomain: The DNS name of the domain
1659 :param domainsid: The SID of the domain
1660 :param domaindn: The DN of the domain (ie. DC=...)
1661 :param samdb: An LDB object on the SAM db
1662 :param lp: an LP object
1665 # Set ACL for GPO root folder
1666 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1667 fsacl = getntacl(lp, root_policy_path,
1668 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1670 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1671 fsacl_sddl = fsacl.as_sddl(domainsid)
1672 if fsacl_sddl != POLICIES_ACL:
1673 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))
1674 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1675 attrs=["cn", "nTSecurityDescriptor"],
1676 expression="", scope=ldb.SCOPE_ONELEVEL)
1679 acl = ndr_unpack(security.descriptor,
1680 str(policy["nTSecurityDescriptor"])).as_sddl()
1681 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1682 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1683 domainsid, direct_db_access)
1686 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1688 """Set the ACL for the sysvol share and the subfolders
1690 :param samdb: An LDB object on the SAM db
1691 :param netlogon: Physical path for the netlogon folder
1692 :param sysvol: Physical path for the sysvol folder
1693 :param uid: The UID of the "Administrator" user
1694 :param gid: The GID of the "Domain adminstrators" group
1695 :param domainsid: The SID of the domain
1696 :param dnsdomain: The DNS name of the domain
1697 :param domaindn: The DN of the domain (ie. DC=...)
1700 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1701 s3conf = s3param.get_context()
1702 s3conf.load(lp.configfile)
1703 # ensure we are using the right samba_dsdb passdb backend, no matter what
1704 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1705 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1706 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1708 # now ensure everything matches correctly, to avoid wierd issues
1709 if passdb.get_global_sam_sid() != domainsid:
1710 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))
1712 domain_info = s4_passdb.domain_info()
1713 if domain_info["dom_sid"] != domainsid:
1714 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))
1716 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1717 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()))
1719 # Ensure we can read this directly, and via the smbd VFS
1720 for direct_db_access in [True, False]:
1721 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1722 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1723 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1725 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1726 fsacl_sddl = fsacl.as_sddl(domainsid)
1727 if fsacl_sddl != SYSVOL_ACL:
1728 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))
1730 # Check acls on Policy folder and policies folders
1731 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1735 def interface_ips_v4(lp):
1736 """return only IPv4 IPs"""
1737 ips = samba.interface_ips(lp, False)
1740 if i.find(':') == -1:
1745 def interface_ips_v6(lp):
1746 """return only IPv6 IPs"""
1747 ips = samba.interface_ips(lp, False)
1750 if i.find(':') != -1:
1755 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1757 targetdir=None, samdb_fill=FILL_FULL,
1758 hostip=None, hostip6=None,
1759 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1760 domainguid=None, policyguid=None, policyguid_dc=None,
1761 invocationid=None, machinepass=None, ntdsguid=None,
1762 dns_backend=None, dnspass=None,
1763 serverrole=None, dom_for_fun_level=None,
1764 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1765 # create/adapt the group policy GUIDs
1766 # Default GUID for default policy are described at
1767 # "How Core Group Policy Works"
1768 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1769 if policyguid is None:
1770 policyguid = DEFAULT_POLICY_GUID
1771 policyguid = policyguid.upper()
1772 if policyguid_dc is None:
1773 policyguid_dc = DEFAULT_DC_POLICY_GUID
1774 policyguid_dc = policyguid_dc.upper()
1776 if invocationid is None:
1777 invocationid = str(uuid.uuid4())
1779 if krbtgtpass is None:
1780 krbtgtpass = samba.generate_random_machine_password(128, 255)
1781 if machinepass is None:
1782 machinepass = samba.generate_random_machine_password(128, 255)
1784 dnspass = samba.generate_random_password(128, 255)
1786 samdb.transaction_start()
1788 samdb = fill_samdb(samdb, lp, names, logger=logger,
1790 policyguid=policyguid, policyguid_dc=policyguid_dc,
1791 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1792 invocationid=invocationid, machinepass=machinepass,
1793 dns_backend=dns_backend, dnspass=dnspass,
1794 ntdsguid=ntdsguid, serverrole=serverrole,
1795 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1796 next_rid=next_rid, dc_rid=dc_rid)
1798 # Set up group policies (domain policy and domain controller
1800 if serverrole == "active directory domain controller":
1801 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1804 samdb.transaction_cancel()
1807 samdb.transaction_commit()
1809 if serverrole == "active directory domain controller":
1810 # Continue setting up sysvol for GPO. This appears to require being
1811 # outside a transaction.
1812 if not skip_sysvolacl:
1813 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1814 paths.root_gid, names.domainsid, names.dnsdomain,
1815 names.domaindn, lp, use_ntvfs)
1817 logger.info("Setting acl on sysvol skipped")
1819 secretsdb_self_join(secrets_ldb, domain=names.domain,
1820 realm=names.realm, dnsdomain=names.dnsdomain,
1821 netbiosname=names.netbiosname, domainsid=names.domainsid,
1822 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1824 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1825 # In future, this might be determined from some configuration
1826 kerberos_enctypes = str(ENC_ALL_TYPES)
1829 msg = ldb.Message(ldb.Dn(samdb,
1830 samdb.searchone("distinguishedName",
1831 expression="samAccountName=%s$" % names.netbiosname,
1832 scope=ldb.SCOPE_SUBTREE)))
1833 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1834 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1835 name="msDS-SupportedEncryptionTypes")
1837 except ldb.LdbError, (enum, estr):
1838 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1839 # It might be that this attribute does not exist in this schema
1842 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1843 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1844 dnspass=dnspass, os_level=dom_for_fun_level,
1845 targetdir=targetdir, fill_level=samdb_fill)
1847 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1848 attribute="objectGUID")
1849 assert isinstance(domainguid, str)
1851 lastProvisionUSNs = get_last_provision_usn(samdb)
1852 maxUSN = get_max_usn(samdb, str(names.rootdn))
1853 if lastProvisionUSNs is not None:
1854 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1856 set_provision_usn(samdb, 0, maxUSN, invocationid)
1858 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1859 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1860 { 'NTDSGUID' : names.ntdsguid })
1862 # fix any dangling GUIDs from the provision
1863 logger.info("Fixing provision GUIDs")
1864 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1866 samdb.transaction_start()
1868 # a small number of GUIDs are missing because of ordering issues in the
1870 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1871 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1872 scope=ldb.SCOPE_BASE,
1873 attrs=['defaultObjectCategory'])
1874 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1875 scope=ldb.SCOPE_ONELEVEL,
1876 attrs=['ipsecOwnersReference',
1877 'ipsecFilterReference',
1878 'ipsecISAKMPReference',
1879 'ipsecNegotiationPolicyReference',
1880 'ipsecNFAReference'])
1881 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
1882 attrs=['attributeId', 'governsId']) != 0:
1883 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
1885 samdb.transaction_cancel()
1888 samdb.transaction_commit()
1892 "ROLE_STANDALONE": "standalone server",
1893 "ROLE_DOMAIN_MEMBER": "member server",
1894 "ROLE_DOMAIN_BDC": "active directory domain controller",
1895 "ROLE_DOMAIN_PDC": "active directory domain controller",
1896 "dc": "active directory domain controller",
1897 "member": "member server",
1898 "domain controller": "active directory domain controller",
1899 "active directory domain controller": "active directory domain controller",
1900 "member server": "member server",
1901 "standalone": "standalone server",
1902 "standalone server": "standalone server",
1906 def sanitize_server_role(role):
1907 """Sanitize a server role name.
1909 :param role: Server role
1910 :raise ValueError: If the role can not be interpreted
1911 :return: Sanitized server role (one of "member server",
1912 "active directory domain controller", "standalone server")
1915 return _ROLES_MAP[role]
1917 raise ValueError(role)
1920 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1922 """Create AD entries for the fake ypserver.
1924 This is needed for being able to manipulate posix attrs via ADUC.
1926 samdb.transaction_start()
1928 logger.info("Setting up fake yp server settings")
1929 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1930 "DOMAINDN": domaindn,
1931 "NETBIOSNAME": netbiosname,
1932 "NISDOMAIN": nisdomain,
1935 samdb.transaction_cancel()
1938 samdb.transaction_commit()
1940 def directory_create_or_exists(path, mode=0o755):
1941 if not os.path.exists(path):
1943 os.mkdir(path, mode)
1944 except OSError as e:
1945 if e.errno in [errno.EEXIST]:
1948 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
1950 def provision(logger, session_info, smbconf=None,
1951 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1952 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1953 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1954 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1955 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1956 dns_backend=None, dns_forwarder=None, dnspass=None,
1957 invocationid=None, machinepass=None, ntdsguid=None,
1958 root=None, nobody=None, users=None, backup=None, aci=None,
1959 serverrole=None, dom_for_fun_level=None, backend_type=None,
1960 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1961 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1962 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
1963 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, ldap_backend_extra_port=None):
1966 :note: caution, this wipes all existing data!
1970 serverrole = sanitize_server_role(serverrole)
1972 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1974 if ldapadminpass is None:
1975 # Make a new, random password between Samba and it's LDAP server
1976 ldapadminpass = samba.generate_random_password(128, 255)
1978 if backend_type is None:
1979 backend_type = "ldb"
1981 if domainsid is None:
1982 domainsid = security.random_sid()
1984 root_uid = findnss_uid([root or "root"])
1985 nobody_uid = findnss_uid([nobody or "nobody"])
1986 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1987 root_gid = pwd.getpwuid(root_uid).pw_gid
1990 bind_gid = findnss_gid(["bind", "named"])
1994 if targetdir is not None:
1995 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1996 elif smbconf is None:
1997 smbconf = samba.param.default_path()
1998 if not os.path.exists(os.path.dirname(smbconf)):
1999 os.makedirs(os.path.dirname(smbconf))
2001 server_services = []
2004 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2006 if dns_backend != "SAMBA_INTERNAL":
2007 server_services.append("-dns")
2009 if dns_forwarder is not None:
2010 global_param["dns forwarder"] = [dns_forwarder]
2013 server_services.append("+smb")
2014 server_services.append("-s3fs")
2015 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2017 if len(server_services) > 0:
2018 global_param["server services"] = server_services
2020 # only install a new smb.conf if there isn't one there already
2021 if os.path.exists(smbconf):
2022 # if Samba Team members can't figure out the weird errors
2023 # loading an empty smb.conf gives, then we need to be smarter.
2024 # Pretend it just didn't exist --abartlet
2025 f = open(smbconf, 'r')
2027 data = f.read().lstrip()
2030 if data is None or data == "":
2031 make_smbconf(smbconf, hostname, domain, realm,
2032 targetdir, serverrole=serverrole,
2033 eadb=useeadb, use_ntvfs=use_ntvfs,
2034 lp=lp, global_param=global_param)
2036 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2037 serverrole=serverrole,
2038 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2041 lp = samba.param.LoadParm()
2043 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2044 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2045 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2046 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2047 paths = provision_paths_from_lp(lp, names.dnsdomain)
2049 paths.bind_gid = bind_gid
2050 paths.root_uid = root_uid;
2051 paths.root_gid = root_gid
2054 logger.info("Looking up IPv4 addresses")
2055 hostips = interface_ips_v4(lp)
2056 if len(hostips) > 0:
2058 if len(hostips) > 1:
2059 logger.warning("More than one IPv4 address found. Using %s",
2061 if hostip == "127.0.0.1":
2064 logger.warning("No IPv4 address will be assigned")
2067 logger.info("Looking up IPv6 addresses")
2068 hostips = interface_ips_v6(lp)
2070 hostip6 = hostips[0]
2071 if len(hostips) > 1:
2072 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2074 logger.warning("No IPv6 address will be assigned")
2076 names.hostip = hostip
2077 names.hostip6 = hostip6
2078 names.domainguid = domainguid
2079 names.domainsid = domainsid
2080 names.forestsid = domainsid
2082 if serverrole is None:
2083 serverrole = lp.get("server role")
2085 directory_create_or_exists(paths.private_dir, 0o700)
2086 directory_create_or_exists(paths.binddns_dir, 0o770)
2087 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2088 directory_create_or_exists(paths.state_dir)
2090 if paths.sysvol and not os.path.exists(paths.sysvol):
2091 os.makedirs(paths.sysvol, 0775)
2093 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2095 schema = Schema(domainsid, invocationid=invocationid,
2096 schemadn=names.schemadn)
2098 if backend_type == "ldb":
2099 provision_backend = LDBBackend(backend_type, paths=paths,
2101 names=names, logger=logger)
2102 elif backend_type == "existing":
2103 # If support for this is ever added back, then the URI will need to be
2105 provision_backend = ExistingBackend(backend_type, paths=paths,
2107 names=names, logger=logger,
2108 ldap_backend_forced_uri=ldap_backend_forced_uri)
2109 elif backend_type == "fedora-ds":
2110 provision_backend = FDSBackend(backend_type, paths=paths,
2112 names=names, logger=logger, domainsid=domainsid,
2113 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2114 slapd_path=slapd_path,
2116 elif backend_type == "openldap":
2117 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2119 names=names, logger=logger, domainsid=domainsid,
2120 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2121 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2122 ldap_backend_extra_port=ldap_backend_extra_port,
2123 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2124 ldap_backend_forced_uri=ldap_backend_forced_uri)
2126 raise ValueError("Unknown LDAP backend type selected")
2128 provision_backend.init()
2129 provision_backend.start()
2131 # only install a new shares config db if there is none
2132 if not os.path.exists(paths.shareconf):
2133 logger.info("Setting up share.ldb")
2134 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2135 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2137 logger.info("Setting up secrets.ldb")
2138 secrets_ldb = setup_secretsdb(paths,
2139 session_info=session_info,
2140 backend_credentials=provision_backend.credentials, lp=lp)
2143 logger.info("Setting up the registry")
2144 setup_registry(paths.hklm, session_info, lp=lp)
2146 logger.info("Setting up the privileges database")
2147 setup_privileges(paths.privilege, session_info, lp=lp)
2149 logger.info("Setting up idmap db")
2150 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2152 setup_name_mappings(idmap, sid=str(domainsid),
2153 root_uid=root_uid, nobody_uid=nobody_uid,
2154 users_gid=users_gid, root_gid=root_gid)
2156 logger.info("Setting up SAM db")
2157 samdb = setup_samdb(paths.samdb, session_info,
2158 provision_backend, lp, names, logger=logger,
2159 serverrole=serverrole,
2160 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2162 if serverrole == "active directory domain controller":
2163 if paths.netlogon is None:
2164 raise MissingShareError("netlogon", paths.smbconf)
2166 if paths.sysvol is None:
2167 raise MissingShareError("sysvol", paths.smbconf)
2169 if not os.path.isdir(paths.netlogon):
2170 os.makedirs(paths.netlogon, 0755)
2172 if adminpass is None:
2173 adminpass = samba.generate_random_password(12, 32)
2174 adminpass_generated = True
2176 adminpass = unicode(adminpass, 'utf-8')
2177 adminpass_generated = False
2179 if samdb_fill == FILL_FULL:
2180 provision_fill(samdb, secrets_ldb, logger, names, paths,
2181 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2182 hostip=hostip, hostip6=hostip6,
2183 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2184 krbtgtpass=krbtgtpass,
2185 policyguid=policyguid, policyguid_dc=policyguid_dc,
2186 invocationid=invocationid, machinepass=machinepass,
2187 ntdsguid=ntdsguid, dns_backend=dns_backend,
2188 dnspass=dnspass, serverrole=serverrole,
2189 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2190 lp=lp, use_ntvfs=use_ntvfs,
2191 skip_sysvolacl=skip_sysvolacl)
2193 if not is_heimdal_built():
2194 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2195 logger.info("The Kerberos KDC configuration for Samba AD is "
2196 "located at %s", paths.kdcconf)
2198 create_krb5_conf(paths.krb5conf,
2199 dnsdomain=names.dnsdomain, hostname=names.hostname,
2201 logger.info("A Kerberos configuration suitable for Samba AD has been "
2202 "generated at %s", paths.krb5conf)
2204 if serverrole == "active directory domain controller":
2205 create_dns_update_list(lp, logger, paths)
2207 backend_result = provision_backend.post_setup()
2208 provision_backend.shutdown()
2211 secrets_ldb.transaction_cancel()
2214 # Now commit the secrets.ldb to disk
2215 secrets_ldb.transaction_commit()
2217 # the commit creates the dns.keytab in the private directory
2218 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2219 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2221 if os.path.isfile(private_dns_keytab_path):
2222 if os.path.isfile(bind_dns_keytab_path):
2224 os.unlink(bind_dns_keytab_path)
2225 except OSError as e:
2226 logger.error("Failed to remove %s: %s" %
2227 (bind_dns_keytab_path, e.strerror))
2229 # link the dns.keytab to the bind-dns directory
2231 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2232 except OSError as e:
2233 logger.error("Failed to create link %s -> %s: %s" %
2234 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2236 # chown the dns.keytab in the bind-dns directory
2237 if paths.bind_gid is not None:
2239 os.chmod(bind_dns_keytab_path, 0640)
2240 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2242 if not os.environ.has_key('SAMBA_SELFTEST'):
2243 logger.info("Failed to chown %s to bind gid %u",
2244 bind_dns_keytab_path, paths.bind_gid)
2246 result = ProvisionResult()
2247 result.server_role = serverrole
2248 result.domaindn = domaindn
2249 result.paths = paths
2250 result.names = names
2252 result.samdb = samdb
2253 result.idmap = idmap
2254 result.domainsid = str(domainsid)
2256 if samdb_fill == FILL_FULL:
2257 result.adminpass_generated = adminpass_generated
2258 result.adminpass = adminpass
2260 result.adminpass_generated = False
2261 result.adminpass = None
2263 result.backend_result = backend_result
2266 provision_fake_ypserver(logger=logger, samdb=samdb,
2267 domaindn=names.domaindn, netbiosname=names.netbiosname,
2268 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2273 def provision_become_dc(smbconf=None, targetdir=None,
2274 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2275 serverdn=None, domain=None, hostname=None, domainsid=None,
2276 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2277 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2278 dns_backend=None, root=None, nobody=None, users=None,
2279 backup=None, serverrole=None, ldap_backend=None,
2280 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2282 logger = logging.getLogger("provision")
2283 samba.set_debug_level(debuglevel)
2285 res = provision(logger, system_session(),
2286 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2287 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2288 configdn=configdn, serverdn=serverdn, domain=domain,
2289 hostname=hostname, hostip=None, domainsid=domainsid,
2290 machinepass=machinepass,
2291 serverrole="active directory domain controller",
2292 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2293 use_ntvfs=use_ntvfs)
2294 res.lp.set("debuglevel", str(debuglevel))
2298 def create_krb5_conf(path, dnsdomain, hostname, realm):
2299 """Write out a file containing a valid krb5.conf file
2301 :param path: Path of the new krb5.conf file.
2302 :param dnsdomain: DNS Domain name
2303 :param hostname: Local hostname
2304 :param realm: Realm name
2306 setup_file(setup_path("krb5.conf"), path, {
2307 "DNSDOMAIN": dnsdomain,
2308 "HOSTNAME": hostname,
2313 class ProvisioningError(Exception):
2314 """A generic provision error."""
2316 def __init__(self, value):
2320 return "ProvisioningError: " + self.value
2323 class InvalidNetbiosName(Exception):
2324 """A specified name was not a valid NetBIOS name."""
2326 def __init__(self, name):
2327 super(InvalidNetbiosName, self).__init__(
2328 "The name '%r' is not a valid NetBIOS name" % name)
2331 class MissingShareError(ProvisioningError):
2333 def __init__(self, name, smbconf):
2334 super(MissingShareError, self).__init__(
2335 "Existing smb.conf does not have a [%s] share, but you are "
2336 "configuring a DC. Please remove %s or add the share manually." %