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 samba.compat import urllib_quote
30 from base64 import b64encode
47 from samba.auth import system_session, admin_session
49 from samba import auth
50 from samba.samba3 import smbd, passdb
51 from samba.samba3 import param as s3param
52 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
56 check_all_substituted,
57 is_valid_netbios_char,
64 from samba.dcerpc import security, misc
65 from samba.dcerpc.misc import (
69 from samba.dsdb import (
70 DS_DOMAIN_FUNCTION_2003,
71 DS_DOMAIN_FUNCTION_2008_R2,
74 from samba.idmap import IDmapDB
75 from samba.ms_display_specifiers import read_ms_ldif
76 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
77 from samba.ndr import ndr_pack, ndr_unpack
78 from samba.provision.backend import (
84 from samba.descriptor import (
86 get_config_descriptor,
87 get_config_partitions_descriptor,
88 get_config_sites_descriptor,
89 get_config_ntds_quotas_descriptor,
90 get_config_delete_protected1_descriptor,
91 get_config_delete_protected1wd_descriptor,
92 get_config_delete_protected2_descriptor,
93 get_domain_descriptor,
94 get_domain_infrastructure_descriptor,
95 get_domain_builtin_descriptor,
96 get_domain_computers_descriptor,
97 get_domain_users_descriptor,
98 get_domain_controllers_descriptor,
99 get_domain_delete_protected1_descriptor,
100 get_domain_delete_protected2_descriptor,
101 get_dns_partition_descriptor,
102 get_dns_forest_microsoft_dns_descriptor,
103 get_dns_domain_microsoft_dns_descriptor,
104 get_managed_service_accounts_descriptor,
106 from samba.provision.common import (
115 from samba.provision.sambadns import (
118 create_dns_update_list
122 import samba.registry
123 from samba.schema import Schema
124 from samba.samdb import SamDB
125 from samba.dbchecker import dbcheck
126 from samba.provision.kerberos import create_kdc_conf
127 from samba.samdb import get_default_backend_store
129 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
130 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
131 DEFAULTSITE = "Default-First-Site-Name"
132 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
134 DEFAULT_MIN_PWD_LENGTH = 7
137 class ProvisionPaths(object):
140 self.shareconf = None
151 self.dns_keytab = None
154 self.private_dir = None
155 self.binddns_dir = None
156 self.state_dir = None
159 class ProvisionNames(object):
167 self.dnsforestdn = None
168 self.dnsdomaindn = None
169 self.ldapmanagerdn = None
170 self.dnsdomain = None
172 self.netbiosname = None
177 self.domainsid = None
178 self.forestsid = None
179 self.domainguid = None
183 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
185 """Get key provision parameters (realm, domain, ...) from a given provision
187 :param samdb: An LDB object connected to the sam.ldb file
188 :param secretsdb: An LDB object connected to the secrets.ldb file
189 :param idmapdb: An LDB object connected to the idmap.ldb file
190 :param paths: A list of path to provision object
191 :param smbconf: Path to the smb.conf file
192 :param lp: A LoadParm object
193 :return: A list of key provision parameters
195 names = ProvisionNames()
196 names.adminpass = None
198 # NT domain, kerberos realm, root dn, domain dn, domain dns name
199 names.domain = lp.get("workgroup").upper()
200 names.realm = lp.get("realm")
201 names.dnsdomain = names.realm.lower()
202 basedn = samba.dn_from_dns_name(names.dnsdomain)
203 names.realm = names.realm.upper()
205 # Get the netbiosname first (could be obtained from smb.conf in theory)
206 res = secretsdb.search(expression="(flatname=%s)" %
207 names.domain, base="CN=Primary Domains",
208 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
209 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$", "")
211 names.smbconf = smbconf
213 # That's a bit simplistic but it's ok as long as we have only 3
215 current = samdb.search(expression="(objectClass=*)",
216 base="", scope=ldb.SCOPE_BASE,
217 attrs=["defaultNamingContext", "schemaNamingContext",
218 "configurationNamingContext", "rootDomainNamingContext",
221 names.configdn = current[0]["configurationNamingContext"][0]
222 names.schemadn = current[0]["schemaNamingContext"][0]
223 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
224 current[0]["defaultNamingContext"][0].decode('utf8')))):
225 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
226 "is not the same ..." % (paths.samdb,
227 str(current[0]["defaultNamingContext"][0].decode('utf8')),
228 paths.smbconf, basedn)))
230 names.domaindn = current[0]["defaultNamingContext"][0]
231 names.rootdn = current[0]["rootDomainNamingContext"][0]
232 names.ncs = current[0]["namingContexts"]
233 names.dnsforestdn = None
234 names.dnsdomaindn = None
236 for i in range(0, len(names.ncs)):
239 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
240 if nc == dnsforestdn:
241 names.dnsforestdn = dnsforestdn
244 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
245 if nc == dnsdomaindn:
246 names.dnsdomaindn = dnsdomaindn
250 res3 = samdb.search(expression="(objectClass=site)",
251 base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
252 names.sitename = str(res3[0]["cn"])
254 # dns hostname and server dn
255 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
256 base="OU=Domain Controllers,%s" % basedn,
257 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
259 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
261 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
263 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
264 attrs=[], base=names.configdn)
265 names.serverdn = str(server_res[0].dn)
267 # invocation id/objectguid
268 res5 = samdb.search(expression="(objectClass=*)",
269 base="CN=NTDS Settings,%s" % str(names.serverdn),
270 scope=ldb.SCOPE_BASE,
271 attrs=["invocationID", "objectGUID"])
272 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
273 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
276 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
277 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
278 "objectSid", "msDS-Behavior-Version"])
279 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
280 names.domainsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
281 names.forestsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
282 if res6[0].get("msDS-Behavior-Version") is None or \
283 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
284 names.domainlevel = DS_DOMAIN_FUNCTION_2000
286 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
289 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
290 base="CN=Policies,CN=System," + basedn,
291 scope=ldb.SCOPE_ONELEVEL, attrs=["cn", "displayName"])
292 names.policyid = str(res7[0]["cn"]).replace("{", "").replace("}", "")
294 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
295 base="CN=Policies,CN=System," + basedn,
296 scope=ldb.SCOPE_ONELEVEL,
297 attrs=["cn", "displayName"])
299 names.policyid_dc = str(res8[0]["cn"]).replace("{", "").replace("}", "")
301 names.policyid_dc = None
303 res9 = idmapdb.search(expression="(cn=%s-%s)" %
304 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
305 attrs=["xidNumber", "type"])
307 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
308 if res9[0]["type"][0] == "ID_TYPE_BOTH":
309 names.root_gid = res9[0]["xidNumber"][0]
311 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
313 res10 = samdb.search(expression="(samaccountname=dns)",
314 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
315 controls=["search_options:1:2"])
317 has_legacy_dns_account = True
319 has_legacy_dns_account = False
321 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
322 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
323 controls=["search_options:1:2"])
325 has_dns_account = True
327 has_dns_account = False
329 if names.dnsdomaindn is not None:
331 names.dns_backend = 'BIND9_DLZ'
333 names.dns_backend = 'SAMBA_INTERNAL'
334 elif has_dns_account or has_legacy_dns_account:
335 names.dns_backend = 'BIND9_FLATFILE'
337 names.dns_backend = 'NONE'
339 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
340 names.name_map['DnsAdmins'] = str(dns_admins_sid)
345 def update_provision_usn(samdb, low, high, id, replace=False):
346 """Update the field provisionUSN in sam.ldb
348 This field is used to track range of USN modified by provision and
350 This value is used afterward by next provision to figure out if
351 the field have been modified since last provision.
353 :param samdb: An LDB object connect to sam.ldb
354 :param low: The lowest USN modified by this upgrade
355 :param high: The highest USN modified by this upgrade
356 :param id: The invocation id of the samba's dc
357 :param replace: A boolean indicating if the range should replace any
358 existing one or appended (default)
363 entry = samdb.search(base="@PROVISION",
364 scope=ldb.SCOPE_BASE,
365 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
366 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
367 if not re.search(';', e):
368 e = "%s;%s" % (e, id)
371 tab.append("%s-%s;%s" % (low, high, id))
372 delta = ldb.Message()
373 delta.dn = ldb.Dn(samdb, "@PROVISION")
374 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
375 ldb.MessageElement(tab,
376 ldb.FLAG_MOD_REPLACE,
377 LAST_PROVISION_USN_ATTRIBUTE)
378 entry = samdb.search(expression='provisionnerID=*',
379 base="@PROVISION", scope=ldb.SCOPE_BASE,
380 attrs=["provisionnerID"])
381 if len(entry) == 0 or len(entry[0]) == 0:
382 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
386 def set_provision_usn(samdb, low, high, id):
387 """Set the field provisionUSN in sam.ldb
388 This field is used to track range of USN modified by provision and
390 This value is used afterward by next provision to figure out if
391 the field have been modified since last provision.
393 :param samdb: An LDB object connect to sam.ldb
394 :param low: The lowest USN modified by this upgrade
395 :param high: The highest USN modified by this upgrade
396 :param id: The invocationId of the provision"""
399 tab.append("%s-%s;%s" % (low, high, id))
401 delta = ldb.Message()
402 delta.dn = ldb.Dn(samdb, "@PROVISION")
403 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
404 ldb.MessageElement(tab,
406 LAST_PROVISION_USN_ATTRIBUTE)
410 def get_max_usn(samdb, basedn):
411 """ This function return the biggest USN present in the provision
413 :param samdb: A LDB object pointing to the sam.ldb
414 :param basedn: A string containing the base DN of the provision
416 :return: The biggest USN in the provision"""
418 res = samdb.search(expression="objectClass=*", base=basedn,
419 scope=ldb.SCOPE_SUBTREE, attrs=["uSNChanged"],
420 controls=["search_options:1:2",
421 "server_sort:1:1:uSNChanged",
422 "paged_results:1:1"])
423 return res[0]["uSNChanged"]
426 def get_last_provision_usn(sam):
427 """Get USNs ranges modified by a provision or an upgradeprovision
429 :param sam: An LDB object pointing to the sam.ldb
430 :return: a dictionary which keys are invocation id and values are an array
431 of integer representing the different ranges
434 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
435 base="@PROVISION", scope=ldb.SCOPE_BASE,
436 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
437 except ldb.LdbError as e1:
438 (ecode, emsg) = e1.args
439 if ecode == ldb.ERR_NO_SUCH_OBJECT:
446 if entry[0].get("provisionnerID"):
447 for e in entry[0]["provisionnerID"]:
449 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
450 tab1 = str(r).split(';')
455 if (len(myids) > 0 and id not in myids):
457 tab2 = p.split(tab1[0])
458 if range.get(id) is None:
460 range[id].append(tab2[0])
461 range[id].append(tab2[1])
467 class ProvisionResult(object):
468 """Result of a provision.
470 :ivar server_role: The server role
471 :ivar paths: ProvisionPaths instance
472 :ivar domaindn: The domain dn, as string
476 self.server_role = None
483 self.domainsid = None
484 self.adminpass_generated = None
485 self.adminpass = None
486 self.backend_result = None
488 def report_logger(self, logger):
489 """Report this provision result to a logger."""
491 "Once the above files are installed, your Samba AD server will "
493 if self.adminpass_generated:
494 logger.info("Admin password: %s", self.adminpass)
495 logger.info("Server Role: %s", self.server_role)
496 logger.info("Hostname: %s", self.names.hostname)
497 logger.info("NetBIOS Domain: %s", self.names.domain)
498 logger.info("DNS Domain: %s", self.names.dnsdomain)
499 logger.info("DOMAIN SID: %s", self.domainsid)
501 if self.backend_result:
502 self.backend_result.report_logger(logger)
505 def check_install(lp, session_info, credentials):
506 """Check whether the current install seems ok.
508 :param lp: Loadparm context
509 :param session_info: Session information
510 :param credentials: Credentials
512 if lp.get("realm") == "":
513 raise Exception("Realm empty")
514 samdb = Ldb(lp.samdb_url(), session_info=session_info,
515 credentials=credentials, lp=lp)
516 if len(samdb.search("(cn=Administrator)")) != 1:
517 raise ProvisioningError("No administrator account found")
520 def findnss(nssfn, names):
521 """Find a user or group from a list of possibilities.
523 :param nssfn: NSS Function to try (should raise KeyError if not found)
524 :param names: Names to check.
525 :return: Value return by first names list.
532 raise KeyError("Unable to find user/group in %r" % names)
535 def findnss_uid(names):
536 return findnss(pwd.getpwnam, names)[2]
539 def findnss_gid(names):
540 return findnss(grp.getgrnam, names)[2]
543 def provision_paths_from_lp(lp, dnsdomain):
544 """Set the default paths for provisioning.
546 :param lp: Loadparm context.
547 :param dnsdomain: DNS Domain name
549 paths = ProvisionPaths()
550 paths.private_dir = lp.get("private dir")
551 paths.binddns_dir = lp.get("binddns dir")
552 paths.state_dir = lp.get("state directory")
554 # This is stored without path prefix for the "privateKeytab" attribute in
555 # "secrets_dns.ldif".
556 paths.dns_keytab = "dns.keytab"
557 paths.keytab = "secrets.keytab"
559 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
560 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
561 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
562 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
563 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
564 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
565 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
566 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
567 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
568 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
569 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
570 paths.encrypted_secrets_key_path = os.path.join(
572 "encrypted_secrets.key")
574 paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
575 paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
576 paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
577 paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
579 paths.hklm = "hklm.ldb"
580 paths.hkcr = "hkcr.ldb"
581 paths.hkcu = "hkcu.ldb"
582 paths.hku = "hku.ldb"
583 paths.hkpd = "hkpd.ldb"
584 paths.hkpt = "hkpt.ldb"
585 paths.sysvol = lp.get("path", "sysvol")
586 paths.netlogon = lp.get("path", "netlogon")
587 paths.smbconf = lp.configfile
591 def determine_netbios_name(hostname):
592 """Determine a netbios name from a hostname."""
593 # remove forbidden chars and force the length to be <16
594 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
595 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
598 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
599 serverrole=None, rootdn=None, domaindn=None, configdn=None,
600 schemadn=None, serverdn=None, sitename=None,
601 domain_names_forced=False):
602 """Guess configuration settings to use."""
605 hostname = socket.gethostname().split(".")[0]
607 netbiosname = lp.get("netbios name")
608 if netbiosname is None:
609 netbiosname = determine_netbios_name(hostname)
610 netbiosname = netbiosname.upper()
611 if not valid_netbios_name(netbiosname):
612 raise InvalidNetbiosName(netbiosname)
614 if dnsdomain is None:
615 dnsdomain = lp.get("realm")
616 if dnsdomain is None or dnsdomain == "":
617 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
619 dnsdomain = dnsdomain.lower()
621 if serverrole is None:
622 serverrole = lp.get("server role")
623 if serverrole is None:
624 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
626 serverrole = serverrole.lower()
628 realm = dnsdomain.upper()
630 if lp.get("realm") == "":
631 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
633 if lp.get("realm").upper() != realm:
634 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))
636 if lp.get("server role").lower() != serverrole:
637 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))
639 if serverrole == "active directory domain controller":
641 # This will, for better or worse, default to 'WORKGROUP'
642 domain = lp.get("workgroup")
643 domain = domain.upper()
645 if lp.get("workgroup").upper() != domain:
646 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))
649 domaindn = samba.dn_from_dns_name(dnsdomain)
651 if domain == netbiosname:
652 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
656 domaindn = "DC=" + netbiosname
658 if not valid_netbios_name(domain):
659 raise InvalidNetbiosName(domain)
661 if hostname.upper() == realm:
662 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
663 if netbiosname.upper() == realm:
664 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
665 if domain == realm and not domain_names_forced:
666 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
668 if serverrole != "active directory domain controller":
670 # This is the code path for a domain member
671 # where we provision the database as if we where
672 # on a domain controller, so we should not use
673 # the same dnsdomain as the domain controllers
674 # of our primary domain.
676 # This will be important if we start doing
677 # SID/name filtering and reject the local
678 # sid and names if they come from a domain
682 dnsdomain = netbiosname.lower()
688 configdn = "CN=Configuration," + rootdn
690 schemadn = "CN=Schema," + configdn
693 sitename = DEFAULTSITE
695 names = ProvisionNames()
696 names.rootdn = rootdn
697 names.domaindn = domaindn
698 names.configdn = configdn
699 names.schemadn = schemadn
700 names.ldapmanagerdn = "CN=Manager," + rootdn
701 names.dnsdomain = dnsdomain
702 names.domain = domain
704 names.netbiosname = netbiosname
705 names.hostname = hostname
706 names.sitename = sitename
707 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
708 netbiosname, sitename, configdn)
713 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
714 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
716 """Create a new smb.conf file based on a couple of basic settings.
718 assert smbconf is not None
721 hostname = socket.gethostname().split(".")[0]
723 netbiosname = determine_netbios_name(hostname)
725 if serverrole is None:
726 serverrole = "standalone server"
728 assert domain is not None
729 domain = domain.upper()
731 assert realm is not None
732 realm = realm.upper()
735 "netbios name": netbiosname,
738 "server role": serverrole,
742 lp = samba.param.LoadParm()
743 # Load non-existent file
744 if os.path.exists(smbconf):
747 if global_param is not None:
748 for ent in global_param:
749 if global_param[ent] is not None:
750 global_settings[ent] = " ".join(global_param[ent])
752 if targetdir is not None:
753 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
754 global_settings["lock dir"] = os.path.abspath(targetdir)
755 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
756 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
757 global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns"))
759 lp.set("lock dir", os.path.abspath(targetdir))
760 lp.set("state directory", global_settings["state directory"])
761 lp.set("cache directory", global_settings["cache directory"])
762 lp.set("binddns dir", global_settings["binddns dir"])
765 if use_ntvfs and not lp.get("posix:eadb"):
766 if targetdir is not None:
767 privdir = os.path.join(targetdir, "private")
769 privdir = lp.get("private dir")
770 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
771 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
772 if targetdir is not None:
773 statedir = os.path.join(targetdir, "state")
775 statedir = lp.get("state directory")
776 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
779 if serverrole == "active directory domain controller":
780 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
781 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
784 global_settings["passdb backend"] = "samba_dsdb"
786 f = open(smbconf, 'w')
788 f.write("[globals]\n")
789 for key, val in global_settings.items():
790 f.write("\t%s = %s\n" % (key, val))
793 for name, path in shares.items():
794 f.write("[%s]\n" % name)
795 f.write("\tpath = %s\n" % path)
796 f.write("\tread only = no\n")
800 # reload the smb.conf
803 # and dump it without any values that are the default
804 # this ensures that any smb.conf parameters that were set
805 # on the provision/join command line are set in the resulting smb.conf
806 lp.dump(False, smbconf)
809 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
810 users_gid, root_gid):
811 """setup reasonable name mappings for sam names to unix names.
813 :param samdb: SamDB object.
814 :param idmap: IDmap db object.
815 :param sid: The domain sid.
816 :param domaindn: The domain DN.
817 :param root_uid: uid of the UNIX root user.
818 :param nobody_uid: uid of the UNIX nobody user.
819 :param users_gid: gid of the UNIX users group.
820 :param root_gid: gid of the UNIX root group.
822 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
824 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
825 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
828 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
829 provision_backend, names, serverrole,
830 erase=False, plaintext_secrets=False,
832 """Setup the partitions for the SAM database.
834 Alternatively, provision() may call this, and then populate the database.
836 :note: This will wipe the Sam Database!
838 :note: This function always removes the local SAM LDB file. The erase
839 parameter controls whether to erase the existing data, which
840 may not be stored locally but in LDAP.
843 assert session_info is not None
845 # We use options=["modules:"] to stop the modules loading - we
846 # just want to wipe and re-initialise the database, not start it up
849 os.unlink(samdb_path)
853 samdb = Ldb(url=samdb_path, session_info=session_info,
854 lp=lp, options=["modules:"])
856 ldap_backend_line = "# No LDAP backend"
857 if provision_backend.type != "ldb":
858 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
860 required_features = None
861 if not plaintext_secrets:
862 required_features = "requiredFeatures: encryptedSecrets"
864 if backend_store is None:
865 backend_store = get_default_backend_store()
866 backend_store_line = "backendStore: %s" % backend_store
868 if backend_store == "mdb":
869 if required_features is not None:
870 required_features += "\n"
872 required_features = ""
873 required_features += "requiredFeatures: lmdbLevelOne"
875 if required_features is None:
876 required_features = "# No required features"
878 samdb.transaction_start()
880 logger.info("Setting up sam.ldb partitions and settings")
881 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
882 "LDAP_BACKEND_LINE": ldap_backend_line,
883 "BACKEND_STORE": backend_store_line
886 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
887 "BACKEND_TYPE": provision_backend.type,
888 "SERVER_ROLE": serverrole,
889 "REQUIRED_FEATURES": required_features
892 logger.info("Setting up sam.ldb rootDSE")
893 setup_samdb_rootdse(samdb, names)
895 samdb.transaction_cancel()
898 samdb.transaction_commit()
901 def secretsdb_self_join(secretsdb, domain,
902 netbiosname, machinepass, domainsid=None,
903 realm=None, dnsdomain=None,
905 key_version_number=1,
906 secure_channel_type=SEC_CHAN_WKSTA):
907 """Add domain join-specific bits to a secrets database.
909 :param secretsdb: Ldb Handle to the secrets database
910 :param machinepass: Machine password
912 attrs = ["whenChanged",
919 if realm is not None:
920 if dnsdomain is None:
921 dnsdomain = realm.lower()
922 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
925 shortname = netbiosname.lower()
927 # We don't need to set msg["flatname"] here, because rdn_name will handle
928 # it, and it causes problems for modifies anyway
929 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
930 msg["secureChannelType"] = [str(secure_channel_type)]
931 msg["objectClass"] = ["top", "primaryDomain"]
932 if dnsname is not None:
933 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
934 msg["realm"] = [realm]
935 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
936 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
937 msg["privateKeytab"] = ["secrets.keytab"]
939 msg["secret"] = [machinepass.encode('utf-8')]
940 msg["samAccountName"] = ["%s$" % netbiosname]
941 msg["secureChannelType"] = [str(secure_channel_type)]
942 if domainsid is not None:
943 msg["objectSid"] = [ndr_pack(domainsid)]
945 # This complex expression tries to ensure that we don't have more
946 # than one record for this SID, realm or netbios domain at a time,
947 # but we don't delete the old record that we are about to modify,
948 # because that would delete the keytab and previous password.
949 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
950 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
951 scope=ldb.SCOPE_ONELEVEL)
954 secretsdb.delete(del_msg.dn)
956 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
959 msg["priorSecret"] = [res[0]["secret"][0]]
961 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
966 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
971 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
977 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
978 secretsdb.modify(msg)
979 secretsdb.rename(res[0].dn, msg.dn)
981 spn = ['HOST/%s' % shortname]
982 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
983 # we are a domain controller then we add servicePrincipalName
984 # entries for the keytab code to update.
985 spn.extend(['HOST/%s' % dnsname])
986 msg["servicePrincipalName"] = spn
991 def setup_secretsdb(paths, session_info, backend_credentials, lp):
992 """Setup the secrets database.
994 :note: This function does not handle exceptions and transaction on purpose,
995 it's up to the caller to do this job.
997 :param path: Path to the secrets database.
998 :param session_info: Session info.
999 :param credentials: Credentials
1000 :param lp: Loadparm context
1001 :return: LDB handle for the created secrets database
1003 if os.path.exists(paths.secrets):
1004 os.unlink(paths.secrets)
1006 keytab_path = os.path.join(paths.private_dir, paths.keytab)
1007 if os.path.exists(keytab_path):
1008 os.unlink(keytab_path)
1010 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1011 if os.path.exists(bind_dns_keytab_path):
1012 os.unlink(bind_dns_keytab_path)
1014 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1015 if os.path.exists(dns_keytab_path):
1016 os.unlink(dns_keytab_path)
1018 path = paths.secrets
1020 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1022 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1023 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1024 secrets_ldb.transaction_start()
1026 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1028 if (backend_credentials is not None and
1029 backend_credentials.authentication_requested()):
1030 if backend_credentials.get_bind_dn() is not None:
1031 setup_add_ldif(secrets_ldb,
1032 setup_path("secrets_simple_ldap.ldif"), {
1033 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1034 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1037 setup_add_ldif(secrets_ldb,
1038 setup_path("secrets_sasl_ldap.ldif"), {
1039 "LDAPADMINUSER": backend_credentials.get_username(),
1040 "LDAPADMINREALM": backend_credentials.get_realm(),
1041 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1044 secrets_ldb.transaction_cancel()
1049 def setup_privileges(path, session_info, lp):
1050 """Setup the privileges database.
1052 :param path: Path to the privileges database.
1053 :param session_info: Session info.
1054 :param credentials: Credentials
1055 :param lp: Loadparm context
1056 :return: LDB handle for the created secrets database
1058 if os.path.exists(path):
1060 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1061 privilege_ldb.erase()
1062 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1065 def setup_encrypted_secrets_key(path):
1066 """Setup the encrypted secrets key file.
1068 Any existing key file will be deleted and a new random key generated.
1070 :param path: Path to the secrets key file.
1073 if os.path.exists(path):
1076 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1077 mode = stat.S_IRUSR | stat.S_IWUSR
1079 umask_original = os.umask(0)
1081 fd = os.open(path, flags, mode)
1083 os.umask(umask_original)
1085 with os.fdopen(fd, 'w') as f:
1086 key = samba.generate_random_bytes(16)
1090 def setup_registry(path, session_info, lp):
1091 """Setup the registry.
1093 :param path: Path to the registry database
1094 :param session_info: Session information
1095 :param credentials: Credentials
1096 :param lp: Loadparm context
1098 reg = samba.registry.Registry()
1099 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1100 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1101 provision_reg = setup_path("provision.reg")
1102 assert os.path.exists(provision_reg)
1103 reg.diff_apply(provision_reg)
1106 def setup_idmapdb(path, session_info, lp):
1107 """Setup the idmap database.
1109 :param path: path to the idmap database
1110 :param session_info: Session information
1111 :param credentials: Credentials
1112 :param lp: Loadparm context
1114 if os.path.exists(path):
1117 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1119 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1123 def setup_samdb_rootdse(samdb, names):
1124 """Setup the SamDB rootdse.
1126 :param samdb: Sam Database handle
1128 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1129 "SCHEMADN": names.schemadn,
1130 "DOMAINDN": names.domaindn,
1131 "ROOTDN": names.rootdn,
1132 "CONFIGDN": names.configdn,
1133 "SERVERDN": names.serverdn,
1137 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1138 dns_backend, dnspass, domainsid, next_rid, invocationid,
1139 policyguid, policyguid_dc,
1140 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1141 """Join a host to its own domain."""
1142 assert isinstance(invocationid, str)
1143 if ntdsguid is not None:
1144 ntdsguid_line = "objectGUID: %s\n" %ntdsguid
1151 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1152 "CONFIGDN": names.configdn,
1153 "SCHEMADN": names.schemadn,
1154 "DOMAINDN": names.domaindn,
1155 "SERVERDN": names.serverdn,
1156 "INVOCATIONID": invocationid,
1157 "NETBIOSNAME": names.netbiosname,
1158 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1159 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1160 "DOMAINSID": str(domainsid),
1161 "DCRID": str(dc_rid),
1162 "SAMBA_VERSION_STRING": version,
1163 "NTDSGUID": ntdsguid_line,
1164 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1165 domainControllerFunctionality),
1166 "RIDALLOCATIONSTART": str(next_rid + 100),
1167 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1169 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1170 "POLICYGUID": policyguid,
1171 "POLICYGUID_DC": policyguid_dc,
1172 "DNSDOMAIN": names.dnsdomain,
1173 "DOMAINDN": names.domaindn})
1175 # If we are setting up a subdomain, then this has been replicated in, so we
1176 # don't need to add it
1177 if fill == FILL_FULL:
1178 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1179 "CONFIGDN": names.configdn,
1180 "SCHEMADN": names.schemadn,
1181 "DOMAINDN": names.domaindn,
1182 "SERVERDN": names.serverdn,
1183 "INVOCATIONID": invocationid,
1184 "NETBIOSNAME": names.netbiosname,
1185 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1186 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1187 "DOMAINSID": str(domainsid),
1188 "DCRID": str(dc_rid),
1189 "SAMBA_VERSION_STRING": version,
1190 "NTDSGUID": ntdsguid_line,
1191 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1192 domainControllerFunctionality)})
1194 # Setup fSMORoleOwner entries to point at the newly created DC entry
1195 setup_modify_ldif(samdb,
1196 setup_path("provision_self_join_modify_config.ldif"), {
1197 "CONFIGDN": names.configdn,
1198 "SCHEMADN": names.schemadn,
1199 "DEFAULTSITE": names.sitename,
1200 "NETBIOSNAME": names.netbiosname,
1201 "SERVERDN": names.serverdn,
1204 system_session_info = system_session()
1205 samdb.set_session_info(system_session_info)
1206 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1207 # modify a serverReference under cn=config when we are a subdomain, we must
1208 # be system due to ACLs
1209 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1210 "DOMAINDN": names.domaindn,
1211 "SERVERDN": names.serverdn,
1212 "NETBIOSNAME": names.netbiosname,
1215 samdb.set_session_info(admin_session_info)
1217 if dns_backend != "SAMBA_INTERNAL":
1218 # This is Samba4 specific and should be replaced by the correct
1219 # DNS AD-style setup
1220 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1221 "DNSDOMAIN": names.dnsdomain,
1222 "DOMAINDN": names.domaindn,
1223 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1224 "HOSTNAME": names.hostname,
1225 "DNSNAME": '%s.%s' % (
1226 names.netbiosname.lower(), names.dnsdomain.lower())
1230 def getpolicypath(sysvolpath, dnsdomain, guid):
1231 """Return the physical path of policy given its guid.
1233 :param sysvolpath: Path to the sysvol folder
1234 :param dnsdomain: DNS name of the AD domain
1235 :param guid: The GUID of the policy
1236 :return: A string with the complete path to the policy folder
1239 guid = "{%s}" % guid
1240 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1244 def create_gpo_struct(policy_path):
1245 if not os.path.exists(policy_path):
1246 os.makedirs(policy_path, 0o775)
1247 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1249 f.write("[General]\r\nVersion=0")
1252 p = os.path.join(policy_path, "MACHINE")
1253 if not os.path.exists(p):
1254 os.makedirs(p, 0o775)
1255 p = os.path.join(policy_path, "USER")
1256 if not os.path.exists(p):
1257 os.makedirs(p, 0o775)
1260 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1261 """Create the default GPO for a domain
1263 :param sysvolpath: Physical path for the sysvol folder
1264 :param dnsdomain: DNS domain name of the AD domain
1265 :param policyguid: GUID of the default domain policy
1266 :param policyguid_dc: GUID of the default domain controler policy
1268 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid)
1269 create_gpo_struct(policy_path)
1271 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc)
1272 create_gpo_struct(policy_path)
1275 def setup_samdb(path, session_info, provision_backend, lp, names,
1276 logger, fill, serverrole, schema, am_rodc=False,
1277 plaintext_secrets=False, backend_store=None):
1278 """Setup a complete SAM Database.
1280 :note: This will wipe the main SAM database file!
1283 # Also wipes the database
1284 setup_samdb_partitions(path, logger=logger, lp=lp,
1285 provision_backend=provision_backend, session_info=session_info,
1286 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1287 backend_store=backend_store)
1289 # Load the database, but don's load the global schema and don't connect
1291 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1292 credentials=provision_backend.credentials, lp=lp,
1293 global_schema=False, am_rodc=am_rodc)
1295 logger.info("Pre-loading the Samba 4 and AD schema")
1297 # Load the schema from the one we computed earlier
1298 samdb.set_schema(schema, write_indices_and_attributes=False)
1300 # Set the NTDS settings DN manually - in order to have it already around
1301 # before the provisioned tree exists and we connect
1302 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1304 # And now we can connect to the DB - the schema won't be loaded from the
1308 except ldb.LdbError as e2:
1309 (num, string_error) = e2.args
1310 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1311 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1315 # But we have to give it one more kick to have it use the schema
1316 # during provision - it needs, now that it is connected, to write
1317 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1318 samdb.set_schema(schema, write_indices_and_attributes=True)
1323 def fill_samdb(samdb, lp, names, logger, policyguid,
1324 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1325 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1326 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1327 backend_store=None):
1329 if next_rid is None:
1332 # Provision does not make much sense values larger than 1000000000
1333 # as the upper range of the rIDAvailablePool is 1073741823 and
1334 # we don't want to create a domain that cannot allocate rids.
1335 if next_rid < 1000 or next_rid > 1000000000:
1336 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1337 error += "the valid range is %u-%u. The default is %u." % (
1338 1000, 1000000000, 1000)
1339 raise ProvisioningError(error)
1341 # ATTENTION: Do NOT change these default values without discussion with the
1342 # team and/or release manager. They have a big impact on the whole program!
1343 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1345 if dom_for_fun_level is None:
1346 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1348 if dom_for_fun_level > domainControllerFunctionality:
1349 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!")
1351 domainFunctionality = dom_for_fun_level
1352 forestFunctionality = dom_for_fun_level
1354 # Set the NTDS settings DN manually - in order to have it already around
1355 # before the provisioned tree exists and we connect
1356 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1358 # Set the domain functionality levels onto the database.
1359 # Various module (the password_hash module in particular) need
1360 # to know what level of AD we are emulating.
1362 # These will be fixed into the database via the database
1363 # modifictions below, but we need them set from the start.
1364 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1365 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1366 samdb.set_opaque_integer("domainControllerFunctionality",
1367 domainControllerFunctionality)
1369 samdb.set_domain_sid(str(names.domainsid))
1370 samdb.set_invocation_id(invocationid)
1372 logger.info("Adding DomainDN: %s" % names.domaindn)
1374 # impersonate domain admin
1375 admin_session_info = admin_session(lp, str(names.domainsid))
1376 samdb.set_session_info(admin_session_info)
1377 if names.domainguid is not None:
1378 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1380 domainguid_line = ""
1382 descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1383 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1384 "DOMAINDN": names.domaindn,
1385 "DOMAINSID": str(names.domainsid),
1386 "DESCRIPTOR": descr,
1387 "DOMAINGUID": domainguid_line
1390 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1391 "DOMAINDN": names.domaindn,
1392 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1393 "NEXTRID": str(next_rid),
1394 "DEFAULTSITE": names.sitename,
1395 "CONFIGDN": names.configdn,
1396 "POLICYGUID": policyguid,
1397 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1398 "SAMBA_VERSION_STRING": version,
1399 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1402 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1403 if fill == FILL_FULL:
1404 logger.info("Adding configuration container")
1405 descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1406 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1407 "CONFIGDN": names.configdn,
1408 "DESCRIPTOR": descr,
1411 # The LDIF here was created when the Schema object was constructed
1412 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1413 logger.info("Setting up sam.ldb schema")
1414 samdb.add_ldif(schema.schema_dn_add,
1415 controls=["relax:0", ignore_checks_oid])
1416 samdb.modify_ldif(schema.schema_dn_modify,
1417 controls=[ignore_checks_oid])
1418 samdb.write_prefixes_from_schema()
1419 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1420 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1421 {"SCHEMADN": names.schemadn},
1422 controls=["relax:0", ignore_checks_oid])
1424 # Now register this container in the root of the forest
1425 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1426 msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
1429 samdb.invocation_id = invocationid
1431 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1432 if fill == FILL_FULL:
1433 logger.info("Setting up sam.ldb configuration data")
1435 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1436 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1437 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1438 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1439 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1440 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1442 if "2008" in schema.base_schema:
1443 # exclude 2012-specific changes if we're using a 2008 schema
1448 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1449 "CONFIGDN": names.configdn,
1450 "NETBIOSNAME": names.netbiosname,
1451 "DEFAULTSITE": names.sitename,
1452 "DNSDOMAIN": names.dnsdomain,
1453 "DOMAIN": names.domain,
1454 "SCHEMADN": names.schemadn,
1455 "DOMAINDN": names.domaindn,
1456 "SERVERDN": names.serverdn,
1457 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1458 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1459 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1460 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1461 "SERVICES_DESCRIPTOR": protected1_descr,
1462 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1463 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1464 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1465 "PARTITIONS_DESCRIPTOR": partitions_descr,
1466 "SITES_DESCRIPTOR": sites_descr,
1469 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1470 "CONFIGDN": names.configdn,
1471 "INC2012" : incl_2012,
1474 logger.info("Setting up display specifiers")
1475 display_specifiers_ldif = read_ms_ldif(
1476 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1477 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1478 {"CONFIGDN": names.configdn})
1479 check_all_substituted(display_specifiers_ldif)
1480 samdb.add_ldif(display_specifiers_ldif)
1482 logger.info("Modifying display specifiers and extended rights")
1483 setup_modify_ldif(samdb,
1484 setup_path("provision_configuration_modify.ldif"), {
1485 "CONFIGDN": names.configdn,
1486 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1489 logger.info("Adding users container")
1490 users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1491 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1492 "DOMAINDN": names.domaindn,
1493 "USERS_DESCRIPTOR": users_desc
1495 logger.info("Modifying users container")
1496 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1497 "DOMAINDN": names.domaindn})
1498 logger.info("Adding computers container")
1499 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1500 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1501 "DOMAINDN": names.domaindn,
1502 "COMPUTERS_DESCRIPTOR": computers_desc
1504 logger.info("Modifying computers container")
1505 setup_modify_ldif(samdb,
1506 setup_path("provision_computers_modify.ldif"), {
1507 "DOMAINDN": names.domaindn})
1508 logger.info("Setting up sam.ldb data")
1509 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1510 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1511 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1512 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1513 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1514 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1515 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1516 "DOMAINDN": names.domaindn,
1517 "NETBIOSNAME": names.netbiosname,
1518 "DEFAULTSITE": names.sitename,
1519 "CONFIGDN": names.configdn,
1520 "SERVERDN": names.serverdn,
1521 "RIDAVAILABLESTART": str(next_rid + 600),
1522 "POLICYGUID_DC": policyguid_dc,
1523 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1524 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1525 "SYSTEM_DESCRIPTOR": system_desc,
1526 "BUILTIN_DESCRIPTOR": builtin_desc,
1527 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1530 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1531 if fill == FILL_FULL:
1532 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1533 setup_modify_ldif(samdb,
1534 setup_path("provision_configuration_references.ldif"), {
1535 "CONFIGDN": names.configdn,
1536 "SCHEMADN": names.schemadn})
1538 logger.info("Setting up well known security principals")
1539 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1540 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1541 "CONFIGDN": names.configdn,
1542 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1543 }, controls=["relax:0", "provision:0"])
1545 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1546 setup_modify_ldif(samdb,
1547 setup_path("provision_basedn_references.ldif"), {
1548 "DOMAINDN": names.domaindn,
1549 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1552 logger.info("Setting up sam.ldb users and groups")
1553 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1554 "DOMAINDN": names.domaindn,
1555 "DOMAINSID": str(names.domainsid),
1556 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1557 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1558 }, controls=["relax:0", "provision:0"])
1560 logger.info("Setting up self join")
1561 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1562 invocationid=invocationid,
1563 dns_backend=dns_backend,
1565 machinepass=machinepass,
1566 domainsid=names.domainsid,
1569 policyguid=policyguid,
1570 policyguid_dc=policyguid_dc,
1571 domainControllerFunctionality=domainControllerFunctionality,
1574 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1575 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1576 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1577 assert isinstance(names.ntdsguid, str)
1582 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1583 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)"
1584 SYSVOL_SERVICE = "sysvol"
1587 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1588 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1589 for root, dirs, files in os.walk(path, topdown=False):
1591 setntacl(lp, os.path.join(root, name), acl, domsid,
1592 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1594 setntacl(lp, os.path.join(root, name), acl, domsid,
1595 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1598 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1599 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1602 :param sysvol: Physical path for the sysvol folder
1603 :param dnsdomain: The DNS name of the domain
1604 :param domainsid: The SID of the domain
1605 :param domaindn: The DN of the domain (ie. DC=...)
1606 :param samdb: An LDB object on the SAM db
1607 :param lp: an LP object
1610 # Set ACL for GPO root folder
1611 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1612 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1613 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1615 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1616 attrs=["cn", "nTSecurityDescriptor"],
1617 expression="", scope=ldb.SCOPE_ONELEVEL)
1620 acl = ndr_unpack(security.descriptor,
1621 str(policy["nTSecurityDescriptor"])).as_sddl()
1622 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1623 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1624 str(domainsid), use_ntvfs,
1628 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1629 domaindn, lp, use_ntvfs):
1630 """Set the ACL for the sysvol share and the subfolders
1632 :param samdb: An LDB object on the SAM db
1633 :param netlogon: Physical path for the netlogon folder
1634 :param sysvol: Physical path for the sysvol folder
1635 :param uid: The UID of the "Administrator" user
1636 :param gid: The GID of the "Domain adminstrators" group
1637 :param domainsid: The SID of the domain
1638 :param dnsdomain: The DNS name of the domain
1639 :param domaindn: The DN of the domain (ie. DC=...)
1644 s3conf = s3param.get_context()
1645 s3conf.load(lp.configfile)
1647 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1650 smbd.set_simple_acl(file.name, 0o755, gid)
1652 if not smbd.have_posix_acls():
1653 # This clue is only strictly correct for RPM and
1654 # Debian-like Linux systems, but hopefully other users
1655 # will get enough clue from it.
1656 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1657 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1659 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1660 "Try the mounting the filesystem with the 'acl' option.")
1662 smbd.chown(file.name, uid, gid)
1664 raise ProvisioningError("Unable to chown a file on your filesystem. "
1665 "You may not be running provision as root.")
1669 # This will ensure that the smbd code we are running when setting ACLs
1670 # is initialised with the smb.conf
1671 s3conf = s3param.get_context()
1672 s3conf.load(lp.configfile)
1673 # ensure we are using the right samba_dsdb passdb backend, no matter what
1674 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1675 passdb.reload_static_pdb()
1677 # ensure that we init the samba_dsdb backend, so the domain sid is
1678 # marked in secrets.tdb
1679 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1681 # now ensure everything matches correctly, to avoid wierd issues
1682 if passdb.get_global_sam_sid() != domainsid:
1683 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))
1685 domain_info = s4_passdb.domain_info()
1686 if domain_info["dom_sid"] != domainsid:
1687 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))
1689 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1690 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()))
1694 os.chown(sysvol, -1, gid)
1700 # use admin sid dn as user dn, since admin should own most of the files,
1701 # the operation will be much faster
1702 userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1704 flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1705 auth.AUTH_SESSION_INFO_AUTHENTICATED |
1706 auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1708 session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1709 session_info_flags=flags)
1711 def _setntacl(path):
1712 """A helper to reuse args"""
1714 lp, path, SYSVOL_ACL, str(domainsid),
1715 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
1716 service=SYSVOL_SERVICE, session_info=session_info)
1718 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1720 for root, dirs, files in os.walk(sysvol, topdown=False):
1722 if use_ntvfs and canchown:
1723 os.chown(os.path.join(root, name), -1, gid)
1724 _setntacl(os.path.join(root, name))
1726 if use_ntvfs and canchown:
1727 os.chown(os.path.join(root, name), -1, gid)
1728 _setntacl(os.path.join(root, name))
1730 # Set acls on Policy folder and policies folders
1731 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1734 def acl_type(direct_db_access):
1735 if direct_db_access:
1741 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1742 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1743 fsacl_sddl = fsacl.as_sddl(domainsid)
1744 if fsacl_sddl != acl:
1745 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))
1747 for root, dirs, files in os.walk(path, topdown=False):
1749 fsacl = getntacl(lp, os.path.join(root, name),
1750 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1752 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1753 fsacl_sddl = fsacl.as_sddl(domainsid)
1754 if fsacl_sddl != acl:
1755 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))
1758 fsacl = getntacl(lp, os.path.join(root, name),
1759 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1761 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1762 fsacl_sddl = fsacl.as_sddl(domainsid)
1763 if fsacl_sddl != acl:
1764 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))
1767 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1769 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1772 :param sysvol: Physical path for the sysvol folder
1773 :param dnsdomain: The DNS name of the domain
1774 :param domainsid: The SID of the domain
1775 :param domaindn: The DN of the domain (ie. DC=...)
1776 :param samdb: An LDB object on the SAM db
1777 :param lp: an LP object
1780 # Set ACL for GPO root folder
1781 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1782 fsacl = getntacl(lp, root_policy_path,
1783 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1785 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1786 fsacl_sddl = fsacl.as_sddl(domainsid)
1787 if fsacl_sddl != POLICIES_ACL:
1788 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))
1789 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1790 attrs=["cn", "nTSecurityDescriptor"],
1791 expression="", scope=ldb.SCOPE_ONELEVEL)
1794 acl = ndr_unpack(security.descriptor,
1795 str(policy["nTSecurityDescriptor"])).as_sddl()
1796 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1797 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1798 domainsid, direct_db_access)
1801 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1803 """Set the ACL for the sysvol share and the subfolders
1805 :param samdb: An LDB object on the SAM db
1806 :param netlogon: Physical path for the netlogon folder
1807 :param sysvol: Physical path for the sysvol folder
1808 :param uid: The UID of the "Administrator" user
1809 :param gid: The GID of the "Domain adminstrators" group
1810 :param domainsid: The SID of the domain
1811 :param dnsdomain: The DNS name of the domain
1812 :param domaindn: The DN of the domain (ie. DC=...)
1815 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1816 s3conf = s3param.get_context()
1817 s3conf.load(lp.configfile)
1818 # ensure we are using the right samba_dsdb passdb backend, no matter what
1819 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1820 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1821 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1823 # now ensure everything matches correctly, to avoid wierd issues
1824 if passdb.get_global_sam_sid() != domainsid:
1825 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))
1827 domain_info = s4_passdb.domain_info()
1828 if domain_info["dom_sid"] != domainsid:
1829 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))
1831 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1832 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()))
1834 # Ensure we can read this directly, and via the smbd VFS
1835 for direct_db_access in [True, False]:
1836 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1837 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1838 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1840 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1841 fsacl_sddl = fsacl.as_sddl(domainsid)
1842 if fsacl_sddl != SYSVOL_ACL:
1843 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))
1845 # Check acls on Policy folder and policies folders
1846 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1850 def interface_ips_v4(lp):
1851 """return only IPv4 IPs"""
1852 ips = samba.interface_ips(lp, False)
1855 if i.find(':') == -1:
1860 def interface_ips_v6(lp):
1861 """return only IPv6 IPs"""
1862 ips = samba.interface_ips(lp, False)
1865 if i.find(':') != -1:
1870 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1872 targetdir=None, samdb_fill=FILL_FULL,
1873 hostip=None, hostip6=None,
1874 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1875 domainguid=None, policyguid=None, policyguid_dc=None,
1876 invocationid=None, machinepass=None, ntdsguid=None,
1877 dns_backend=None, dnspass=None,
1878 serverrole=None, dom_for_fun_level=None,
1879 am_rodc=False, lp=None, use_ntvfs=False,
1880 skip_sysvolacl=False, backend_store=None):
1881 # create/adapt the group policy GUIDs
1882 # Default GUID for default policy are described at
1883 # "How Core Group Policy Works"
1884 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1885 if policyguid is None:
1886 policyguid = DEFAULT_POLICY_GUID
1887 policyguid = policyguid.upper()
1888 if policyguid_dc is None:
1889 policyguid_dc = DEFAULT_DC_POLICY_GUID
1890 policyguid_dc = policyguid_dc.upper()
1892 if invocationid is None:
1893 invocationid = str(uuid.uuid4())
1895 if krbtgtpass is None:
1896 krbtgtpass = samba.generate_random_machine_password(128, 255)
1897 if machinepass is None:
1898 machinepass = samba.generate_random_machine_password(128, 255)
1900 dnspass = samba.generate_random_password(128, 255)
1902 samdb.transaction_start()
1904 samdb = fill_samdb(samdb, lp, names, logger=logger,
1906 policyguid=policyguid, policyguid_dc=policyguid_dc,
1907 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1908 invocationid=invocationid, machinepass=machinepass,
1909 dns_backend=dns_backend, dnspass=dnspass,
1910 ntdsguid=ntdsguid, serverrole=serverrole,
1911 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1912 next_rid=next_rid, dc_rid=dc_rid,
1913 backend_store=backend_store)
1915 # Set up group policies (domain policy and domain controller
1917 if serverrole == "active directory domain controller":
1918 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1921 samdb.transaction_cancel()
1924 samdb.transaction_commit()
1926 if serverrole == "active directory domain controller":
1927 # Continue setting up sysvol for GPO. This appears to require being
1928 # outside a transaction.
1929 if not skip_sysvolacl:
1930 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1931 paths.root_gid, names.domainsid, names.dnsdomain,
1932 names.domaindn, lp, use_ntvfs)
1934 logger.info("Setting acl on sysvol skipped")
1936 secretsdb_self_join(secrets_ldb, domain=names.domain,
1937 realm=names.realm, dnsdomain=names.dnsdomain,
1938 netbiosname=names.netbiosname, domainsid=names.domainsid,
1939 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1941 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1942 # In future, this might be determined from some configuration
1943 kerberos_enctypes = str(ENC_ALL_TYPES)
1946 msg = ldb.Message(ldb.Dn(samdb,
1947 samdb.searchone("distinguishedName",
1948 expression="samAccountName=%s$" % names.netbiosname,
1949 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1950 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1951 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1952 name="msDS-SupportedEncryptionTypes")
1954 except ldb.LdbError as e:
1955 (enum, estr) = e.args
1956 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1957 # It might be that this attribute does not exist in this schema
1960 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1961 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1962 dnspass=dnspass, os_level=dom_for_fun_level,
1963 targetdir=targetdir, fill_level=samdb_fill,
1964 backend_store=backend_store)
1966 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1967 attribute="objectGUID")
1968 assert isinstance(domainguid, str)
1970 lastProvisionUSNs = get_last_provision_usn(samdb)
1971 maxUSN = get_max_usn(samdb, str(names.rootdn))
1972 if lastProvisionUSNs is not None:
1973 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1975 set_provision_usn(samdb, 0, maxUSN, invocationid)
1977 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1978 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1979 {'NTDSGUID': names.ntdsguid})
1981 # fix any dangling GUIDs from the provision
1982 logger.info("Fixing provision GUIDs")
1983 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1985 samdb.transaction_start()
1987 # a small number of GUIDs are missing because of ordering issues in the
1989 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1990 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1991 scope=ldb.SCOPE_BASE,
1992 attrs=['defaultObjectCategory'])
1993 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1994 scope=ldb.SCOPE_ONELEVEL,
1995 attrs=['ipsecOwnersReference',
1996 'ipsecFilterReference',
1997 'ipsecISAKMPReference',
1998 'ipsecNegotiationPolicyReference',
1999 'ipsecNFAReference'])
2000 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
2001 attrs=['attributeId', 'governsId']) != 0:
2002 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2004 samdb.transaction_cancel()
2007 samdb.transaction_commit()
2011 "ROLE_STANDALONE": "standalone server",
2012 "ROLE_DOMAIN_MEMBER": "member server",
2013 "ROLE_DOMAIN_BDC": "active directory domain controller",
2014 "ROLE_DOMAIN_PDC": "active directory domain controller",
2015 "dc": "active directory domain controller",
2016 "member": "member server",
2017 "domain controller": "active directory domain controller",
2018 "active directory domain controller": "active directory domain controller",
2019 "member server": "member server",
2020 "standalone": "standalone server",
2021 "standalone server": "standalone server",
2025 def sanitize_server_role(role):
2026 """Sanitize a server role name.
2028 :param role: Server role
2029 :raise ValueError: If the role can not be interpreted
2030 :return: Sanitized server role (one of "member server",
2031 "active directory domain controller", "standalone server")
2034 return _ROLES_MAP[role]
2036 raise ValueError(role)
2039 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2041 """Create AD entries for the fake ypserver.
2043 This is needed for being able to manipulate posix attrs via ADUC.
2045 samdb.transaction_start()
2047 logger.info("Setting up fake yp server settings")
2048 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2049 "DOMAINDN": domaindn,
2050 "NETBIOSNAME": netbiosname,
2051 "NISDOMAIN": nisdomain,
2054 samdb.transaction_cancel()
2057 samdb.transaction_commit()
2060 def directory_create_or_exists(path, mode=0o755):
2061 if not os.path.exists(path):
2063 os.mkdir(path, mode)
2064 except OSError as e:
2065 if e.errno in [errno.EEXIST]:
2068 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2071 def determine_host_ip(logger, lp, hostip=None):
2073 logger.info("Looking up IPv4 addresses")
2074 hostips = interface_ips_v4(lp)
2075 if len(hostips) > 0:
2077 if len(hostips) > 1:
2078 logger.warning("More than one IPv4 address found. Using %s",
2080 if hostip == "127.0.0.1":
2083 logger.warning("No IPv4 address will be assigned")
2088 def determine_host_ip6(logger, lp, hostip6=None):
2090 logger.info("Looking up IPv6 addresses")
2091 hostips = interface_ips_v6(lp)
2093 hostip6 = hostips[0]
2094 if len(hostips) > 1:
2095 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2097 logger.warning("No IPv6 address will be assigned")
2102 def provision(logger, session_info, smbconf=None,
2103 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2104 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2105 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2106 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2107 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2108 dns_backend=None, dns_forwarder=None, dnspass=None,
2109 invocationid=None, machinepass=None, ntdsguid=None,
2110 root=None, nobody=None, users=None, backup=None, aci=None,
2111 serverrole=None, dom_for_fun_level=None, backend_type=None,
2112 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2113 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2114 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2115 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2116 ldap_backend_extra_port=None, base_schema=None,
2117 plaintext_secrets=False, backend_store=None):
2120 :note: caution, this wipes all existing data!
2124 serverrole = sanitize_server_role(serverrole)
2126 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2128 if ldapadminpass is None:
2129 # Make a new, random password between Samba and it's LDAP server
2130 ldapadminpass = samba.generate_random_password(128, 255)
2132 if backend_type is None:
2133 backend_type = "ldb"
2134 if backend_store is None:
2135 backend_store = get_default_backend_store()
2137 if domainsid is None:
2138 domainsid = security.random_sid()
2140 root_uid = findnss_uid([root or "root"])
2141 nobody_uid = findnss_uid([nobody or "nobody"])
2142 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2143 root_gid = pwd.getpwuid(root_uid).pw_gid
2146 bind_gid = findnss_gid(["bind", "named"])
2150 if targetdir is not None:
2151 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2152 elif smbconf is None:
2153 smbconf = samba.param.default_path()
2154 if not os.path.exists(os.path.dirname(smbconf)):
2155 os.makedirs(os.path.dirname(smbconf))
2157 server_services = []
2160 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2162 if dns_backend != "SAMBA_INTERNAL":
2163 server_services.append("-dns")
2165 if dns_forwarder is not None:
2166 global_param["dns forwarder"] = [dns_forwarder]
2169 server_services.append("+smb")
2170 server_services.append("-s3fs")
2171 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2173 if len(server_services) > 0:
2174 global_param["server services"] = server_services
2176 # only install a new smb.conf if there isn't one there already
2177 if os.path.exists(smbconf):
2178 # if Samba Team members can't figure out the weird errors
2179 # loading an empty smb.conf gives, then we need to be smarter.
2180 # Pretend it just didn't exist --abartlet
2181 f = open(smbconf, 'r')
2183 data = f.read().lstrip()
2186 if data is None or data == "":
2187 make_smbconf(smbconf, hostname, domain, realm,
2188 targetdir, serverrole=serverrole,
2189 eadb=useeadb, use_ntvfs=use_ntvfs,
2190 lp=lp, global_param=global_param)
2192 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2193 serverrole=serverrole,
2194 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2197 lp = samba.param.LoadParm()
2199 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2200 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2201 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2202 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2203 paths = provision_paths_from_lp(lp, names.dnsdomain)
2205 paths.bind_gid = bind_gid
2206 paths.root_uid = root_uid;
2207 paths.root_gid = root_gid
2209 hostip = determine_host_ip(logger, lp, hostip)
2210 hostip6 = determine_host_ip6(logger, lp, hostip6)
2211 names.hostip = hostip
2212 names.hostip6 = hostip6
2213 names.domainguid = domainguid
2214 names.domainsid = domainsid
2215 names.forestsid = domainsid
2217 if serverrole is None:
2218 serverrole = lp.get("server role")
2220 directory_create_or_exists(paths.private_dir, 0o700)
2221 directory_create_or_exists(paths.binddns_dir, 0o770)
2222 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2223 directory_create_or_exists(paths.state_dir)
2224 if not plaintext_secrets:
2225 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2227 if paths.sysvol and not os.path.exists(paths.sysvol):
2228 os.makedirs(paths.sysvol, 0o775)
2230 ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
2232 schema = Schema(domainsid, invocationid=invocationid,
2233 schemadn=names.schemadn, base_schema=base_schema)
2235 if backend_type == "ldb":
2236 provision_backend = LDBBackend(backend_type, paths=paths,
2238 names=names, logger=logger)
2239 elif backend_type == "existing":
2240 # If support for this is ever added back, then the URI will need to be
2242 provision_backend = ExistingBackend(backend_type, paths=paths,
2244 names=names, logger=logger,
2245 ldap_backend_forced_uri=ldap_backend_forced_uri)
2246 elif backend_type == "fedora-ds":
2247 provision_backend = FDSBackend(backend_type, paths=paths,
2249 names=names, logger=logger, domainsid=domainsid,
2250 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2251 slapd_path=slapd_path,
2253 elif backend_type == "openldap":
2254 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2256 names=names, logger=logger, domainsid=domainsid,
2257 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2258 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2259 ldap_backend_extra_port=ldap_backend_extra_port,
2260 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2261 ldap_backend_forced_uri=ldap_backend_forced_uri)
2263 raise ValueError("Unknown LDAP backend type selected")
2265 provision_backend.init()
2266 provision_backend.start()
2268 # only install a new shares config db if there is none
2269 if not os.path.exists(paths.shareconf):
2270 logger.info("Setting up share.ldb")
2271 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2272 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2274 logger.info("Setting up secrets.ldb")
2275 secrets_ldb = setup_secretsdb(paths,
2276 session_info=session_info,
2277 backend_credentials=provision_backend.credentials, lp=lp)
2280 logger.info("Setting up the registry")
2281 setup_registry(paths.hklm, session_info, lp=lp)
2283 logger.info("Setting up the privileges database")
2284 setup_privileges(paths.privilege, session_info, lp=lp)
2286 logger.info("Setting up idmap db")
2287 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2289 setup_name_mappings(idmap, sid=str(domainsid),
2290 root_uid=root_uid, nobody_uid=nobody_uid,
2291 users_gid=users_gid, root_gid=root_gid)
2293 logger.info("Setting up SAM db")
2294 samdb = setup_samdb(paths.samdb, session_info,
2295 provision_backend, lp, names, logger=logger,
2296 serverrole=serverrole,
2297 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2298 plaintext_secrets=plaintext_secrets,
2299 backend_store=backend_store)
2301 if serverrole == "active directory domain controller":
2302 if paths.netlogon is None:
2303 raise MissingShareError("netlogon", paths.smbconf)
2305 if paths.sysvol is None:
2306 raise MissingShareError("sysvol", paths.smbconf)
2308 if not os.path.isdir(paths.netlogon):
2309 os.makedirs(paths.netlogon, 0o755)
2311 if adminpass is None:
2312 adminpass = samba.generate_random_password(12, 32)
2313 adminpass_generated = True
2315 adminpass = unicode(adminpass, 'utf-8')
2316 adminpass_generated = False
2318 if samdb_fill == FILL_FULL:
2319 provision_fill(samdb, secrets_ldb, logger, names, paths,
2320 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2321 hostip=hostip, hostip6=hostip6,
2322 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2323 krbtgtpass=krbtgtpass,
2324 policyguid=policyguid, policyguid_dc=policyguid_dc,
2325 invocationid=invocationid, machinepass=machinepass,
2326 ntdsguid=ntdsguid, dns_backend=dns_backend,
2327 dnspass=dnspass, serverrole=serverrole,
2328 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2329 lp=lp, use_ntvfs=use_ntvfs,
2330 skip_sysvolacl=skip_sysvolacl,
2331 backend_store=backend_store)
2333 if not is_heimdal_built():
2334 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2335 logger.info("The Kerberos KDC configuration for Samba AD is "
2336 "located at %s", paths.kdcconf)
2338 create_krb5_conf(paths.krb5conf,
2339 dnsdomain=names.dnsdomain, hostname=names.hostname,
2341 logger.info("A Kerberos configuration suitable for Samba AD has been "
2342 "generated at %s", paths.krb5conf)
2343 logger.info("Merge the contents of this file with your system "
2344 "krb5.conf or replace it with this one. Do not create a "
2347 if serverrole == "active directory domain controller":
2348 create_dns_update_list(lp, logger, paths)
2350 backend_result = provision_backend.post_setup()
2351 provision_backend.shutdown()
2354 secrets_ldb.transaction_cancel()
2357 # Now commit the secrets.ldb to disk
2358 secrets_ldb.transaction_commit()
2360 # the commit creates the dns.keytab in the private directory
2361 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2362 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2364 if os.path.isfile(private_dns_keytab_path):
2365 if os.path.isfile(bind_dns_keytab_path):
2367 os.unlink(bind_dns_keytab_path)
2368 except OSError as e:
2369 logger.error("Failed to remove %s: %s" %
2370 (bind_dns_keytab_path, e.strerror))
2372 # link the dns.keytab to the bind-dns directory
2374 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2375 except OSError as e:
2376 logger.error("Failed to create link %s -> %s: %s" %
2377 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2379 # chown the dns.keytab in the bind-dns directory
2380 if paths.bind_gid is not None:
2382 os.chmod(paths.binddns_dir, 0o770)
2383 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2385 if 'SAMBA_SELFTEST' not in os.environ:
2386 logger.info("Failed to chown %s to bind gid %u",
2387 paths.binddns_dir, paths.bind_gid)
2390 os.chmod(bind_dns_keytab_path, 0o640)
2391 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2393 if 'SAMBA_SELFTEST' not in os.environ:
2394 logger.info("Failed to chown %s to bind gid %u",
2395 bind_dns_keytab_path, paths.bind_gid)
2397 result = ProvisionResult()
2398 result.server_role = serverrole
2399 result.domaindn = domaindn
2400 result.paths = paths
2401 result.names = names
2403 result.samdb = samdb
2404 result.idmap = idmap
2405 result.domainsid = str(domainsid)
2407 if samdb_fill == FILL_FULL:
2408 result.adminpass_generated = adminpass_generated
2409 result.adminpass = adminpass
2411 result.adminpass_generated = False
2412 result.adminpass = None
2414 result.backend_result = backend_result
2417 provision_fake_ypserver(logger=logger, samdb=samdb,
2418 domaindn=names.domaindn, netbiosname=names.netbiosname,
2419 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2424 def provision_become_dc(smbconf=None, targetdir=None,
2425 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2426 serverdn=None, domain=None, hostname=None, domainsid=None,
2427 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2428 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2429 dns_backend=None, root=None, nobody=None, users=None,
2430 backup=None, serverrole=None, ldap_backend=None,
2431 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2433 logger = logging.getLogger("provision")
2434 samba.set_debug_level(debuglevel)
2436 res = provision(logger, system_session(),
2437 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2438 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2439 configdn=configdn, serverdn=serverdn, domain=domain,
2440 hostname=hostname, hostip=None, domainsid=domainsid,
2441 machinepass=machinepass,
2442 serverrole="active directory domain controller",
2443 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2444 use_ntvfs=use_ntvfs)
2445 res.lp.set("debuglevel", str(debuglevel))
2449 def create_krb5_conf(path, dnsdomain, hostname, realm):
2450 """Write out a file containing a valid krb5.conf file
2452 :param path: Path of the new krb5.conf file.
2453 :param dnsdomain: DNS Domain name
2454 :param hostname: Local hostname
2455 :param realm: Realm name
2457 setup_file(setup_path("krb5.conf"), path, {
2458 "DNSDOMAIN": dnsdomain,
2459 "HOSTNAME": hostname,
2464 class ProvisioningError(Exception):
2465 """A generic provision error."""
2467 def __init__(self, value):
2471 return "ProvisioningError: " + self.value
2474 class InvalidNetbiosName(Exception):
2475 """A specified name was not a valid NetBIOS name."""
2477 def __init__(self, name):
2478 super(InvalidNetbiosName, self).__init__(
2479 "The name '%r' is not a valid NetBIOS name" % name)
2482 class MissingShareError(ProvisioningError):
2484 def __init__(self, name, smbconf):
2485 super(MissingShareError, self).__init__(
2486 "Existing smb.conf does not have a [%s] share, but you are "
2487 "configuring a DC. Please remove %s or add the share manually." %