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
887 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
888 "BACKEND_TYPE": provision_backend.type,
889 "SERVER_ROLE": serverrole,
890 "REQUIRED_FEATURES": required_features
893 logger.info("Setting up sam.ldb rootDSE")
894 setup_samdb_rootdse(samdb, names)
896 samdb.transaction_cancel()
899 samdb.transaction_commit()
902 def secretsdb_self_join(secretsdb, domain,
903 netbiosname, machinepass, domainsid=None,
904 realm=None, dnsdomain=None,
906 key_version_number=1,
907 secure_channel_type=SEC_CHAN_WKSTA):
908 """Add domain join-specific bits to a secrets database.
910 :param secretsdb: Ldb Handle to the secrets database
911 :param machinepass: Machine password
913 attrs = ["whenChanged",
920 if realm is not None:
921 if dnsdomain is None:
922 dnsdomain = realm.lower()
923 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
926 shortname = netbiosname.lower()
928 # We don't need to set msg["flatname"] here, because rdn_name will handle
929 # it, and it causes problems for modifies anyway
930 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
931 msg["secureChannelType"] = [str(secure_channel_type)]
932 msg["objectClass"] = ["top", "primaryDomain"]
933 if dnsname is not None:
934 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
935 msg["realm"] = [realm]
936 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
937 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
938 msg["privateKeytab"] = ["secrets.keytab"]
940 msg["secret"] = [machinepass.encode('utf-8')]
941 msg["samAccountName"] = ["%s$" % netbiosname]
942 msg["secureChannelType"] = [str(secure_channel_type)]
943 if domainsid is not None:
944 msg["objectSid"] = [ndr_pack(domainsid)]
946 # This complex expression tries to ensure that we don't have more
947 # than one record for this SID, realm or netbios domain at a time,
948 # but we don't delete the old record that we are about to modify,
949 # because that would delete the keytab and previous password.
950 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
951 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
952 scope=ldb.SCOPE_ONELEVEL)
955 secretsdb.delete(del_msg.dn)
957 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
960 msg["priorSecret"] = [res[0]["secret"][0]]
962 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
967 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
972 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
978 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
979 secretsdb.modify(msg)
980 secretsdb.rename(res[0].dn, msg.dn)
982 spn = ['HOST/%s' % shortname]
983 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
984 # we are a domain controller then we add servicePrincipalName
985 # entries for the keytab code to update.
986 spn.extend(['HOST/%s' % dnsname])
987 msg["servicePrincipalName"] = spn
992 def setup_secretsdb(paths, session_info, backend_credentials, lp):
993 """Setup the secrets database.
995 :note: This function does not handle exceptions and transaction on purpose,
996 it's up to the caller to do this job.
998 :param path: Path to the secrets database.
999 :param session_info: Session info.
1000 :param credentials: Credentials
1001 :param lp: Loadparm context
1002 :return: LDB handle for the created secrets database
1004 if os.path.exists(paths.secrets):
1005 os.unlink(paths.secrets)
1007 keytab_path = os.path.join(paths.private_dir, paths.keytab)
1008 if os.path.exists(keytab_path):
1009 os.unlink(keytab_path)
1011 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1012 if os.path.exists(bind_dns_keytab_path):
1013 os.unlink(bind_dns_keytab_path)
1015 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1016 if os.path.exists(dns_keytab_path):
1017 os.unlink(dns_keytab_path)
1019 path = paths.secrets
1021 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1023 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1024 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1025 secrets_ldb.transaction_start()
1027 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1029 if (backend_credentials is not None and
1030 backend_credentials.authentication_requested()):
1031 if backend_credentials.get_bind_dn() is not None:
1032 setup_add_ldif(secrets_ldb,
1033 setup_path("secrets_simple_ldap.ldif"), {
1034 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1035 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1038 setup_add_ldif(secrets_ldb,
1039 setup_path("secrets_sasl_ldap.ldif"), {
1040 "LDAPADMINUSER": backend_credentials.get_username(),
1041 "LDAPADMINREALM": backend_credentials.get_realm(),
1042 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1045 secrets_ldb.transaction_cancel()
1050 def setup_privileges(path, session_info, lp):
1051 """Setup the privileges database.
1053 :param path: Path to the privileges database.
1054 :param session_info: Session info.
1055 :param credentials: Credentials
1056 :param lp: Loadparm context
1057 :return: LDB handle for the created secrets database
1059 if os.path.exists(path):
1061 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1062 privilege_ldb.erase()
1063 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1066 def setup_encrypted_secrets_key(path):
1067 """Setup the encrypted secrets key file.
1069 Any existing key file will be deleted and a new random key generated.
1071 :param path: Path to the secrets key file.
1074 if os.path.exists(path):
1077 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1078 mode = stat.S_IRUSR | stat.S_IWUSR
1080 umask_original = os.umask(0)
1082 fd = os.open(path, flags, mode)
1084 os.umask(umask_original)
1086 with os.fdopen(fd, 'w') as f:
1087 key = samba.generate_random_bytes(16)
1091 def setup_registry(path, session_info, lp):
1092 """Setup the registry.
1094 :param path: Path to the registry database
1095 :param session_info: Session information
1096 :param credentials: Credentials
1097 :param lp: Loadparm context
1099 reg = samba.registry.Registry()
1100 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1101 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1102 provision_reg = setup_path("provision.reg")
1103 assert os.path.exists(provision_reg)
1104 reg.diff_apply(provision_reg)
1107 def setup_idmapdb(path, session_info, lp):
1108 """Setup the idmap database.
1110 :param path: path to the idmap database
1111 :param session_info: Session information
1112 :param credentials: Credentials
1113 :param lp: Loadparm context
1115 if os.path.exists(path):
1118 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1120 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1124 def setup_samdb_rootdse(samdb, names):
1125 """Setup the SamDB rootdse.
1127 :param samdb: Sam Database handle
1129 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1130 "SCHEMADN": names.schemadn,
1131 "DOMAINDN": names.domaindn,
1132 "ROOTDN": names.rootdn,
1133 "CONFIGDN": names.configdn,
1134 "SERVERDN": names.serverdn,
1138 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1139 dns_backend, dnspass, domainsid, next_rid, invocationid,
1140 policyguid, policyguid_dc,
1141 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1142 """Join a host to its own domain."""
1143 assert isinstance(invocationid, str)
1144 if ntdsguid is not None:
1145 ntdsguid_line = "objectGUID: %s\n" %ntdsguid
1152 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1153 "CONFIGDN": names.configdn,
1154 "SCHEMADN": names.schemadn,
1155 "DOMAINDN": names.domaindn,
1156 "SERVERDN": names.serverdn,
1157 "INVOCATIONID": invocationid,
1158 "NETBIOSNAME": names.netbiosname,
1159 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1160 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1161 "DOMAINSID": str(domainsid),
1162 "DCRID": str(dc_rid),
1163 "SAMBA_VERSION_STRING": version,
1164 "NTDSGUID": ntdsguid_line,
1165 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1166 domainControllerFunctionality),
1167 "RIDALLOCATIONSTART": str(next_rid + 100),
1168 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1170 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1171 "POLICYGUID": policyguid,
1172 "POLICYGUID_DC": policyguid_dc,
1173 "DNSDOMAIN": names.dnsdomain,
1174 "DOMAINDN": names.domaindn})
1176 # If we are setting up a subdomain, then this has been replicated in, so we
1177 # don't need to add it
1178 if fill == FILL_FULL:
1179 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1180 "CONFIGDN": names.configdn,
1181 "SCHEMADN": names.schemadn,
1182 "DOMAINDN": names.domaindn,
1183 "SERVERDN": names.serverdn,
1184 "INVOCATIONID": invocationid,
1185 "NETBIOSNAME": names.netbiosname,
1186 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1187 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1188 "DOMAINSID": str(domainsid),
1189 "DCRID": str(dc_rid),
1190 "SAMBA_VERSION_STRING": version,
1191 "NTDSGUID": ntdsguid_line,
1192 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1193 domainControllerFunctionality)})
1195 # Setup fSMORoleOwner entries to point at the newly created DC entry
1196 setup_modify_ldif(samdb,
1197 setup_path("provision_self_join_modify_config.ldif"), {
1198 "CONFIGDN": names.configdn,
1199 "SCHEMADN": names.schemadn,
1200 "DEFAULTSITE": names.sitename,
1201 "NETBIOSNAME": names.netbiosname,
1202 "SERVERDN": names.serverdn,
1205 system_session_info = system_session()
1206 samdb.set_session_info(system_session_info)
1207 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1208 # modify a serverReference under cn=config when we are a subdomain, we must
1209 # be system due to ACLs
1210 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1211 "DOMAINDN": names.domaindn,
1212 "SERVERDN": names.serverdn,
1213 "NETBIOSNAME": names.netbiosname,
1216 samdb.set_session_info(admin_session_info)
1218 if dns_backend != "SAMBA_INTERNAL":
1219 # This is Samba4 specific and should be replaced by the correct
1220 # DNS AD-style setup
1221 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1222 "DNSDOMAIN": names.dnsdomain,
1223 "DOMAINDN": names.domaindn,
1224 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1225 "HOSTNAME": names.hostname,
1226 "DNSNAME": '%s.%s' % (
1227 names.netbiosname.lower(), names.dnsdomain.lower())
1231 def getpolicypath(sysvolpath, dnsdomain, guid):
1232 """Return the physical path of policy given its guid.
1234 :param sysvolpath: Path to the sysvol folder
1235 :param dnsdomain: DNS name of the AD domain
1236 :param guid: The GUID of the policy
1237 :return: A string with the complete path to the policy folder
1240 guid = "{%s}" % guid
1241 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1245 def create_gpo_struct(policy_path):
1246 if not os.path.exists(policy_path):
1247 os.makedirs(policy_path, 0o775)
1248 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1250 f.write("[General]\r\nVersion=0")
1253 p = os.path.join(policy_path, "MACHINE")
1254 if not os.path.exists(p):
1255 os.makedirs(p, 0o775)
1256 p = os.path.join(policy_path, "USER")
1257 if not os.path.exists(p):
1258 os.makedirs(p, 0o775)
1261 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1262 """Create the default GPO for a domain
1264 :param sysvolpath: Physical path for the sysvol folder
1265 :param dnsdomain: DNS domain name of the AD domain
1266 :param policyguid: GUID of the default domain policy
1267 :param policyguid_dc: GUID of the default domain controler policy
1269 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid)
1270 create_gpo_struct(policy_path)
1272 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc)
1273 create_gpo_struct(policy_path)
1276 def setup_samdb(path, session_info, provision_backend, lp, names,
1277 logger, fill, serverrole, schema, am_rodc=False,
1278 plaintext_secrets=False, backend_store=None):
1279 """Setup a complete SAM Database.
1281 :note: This will wipe the main SAM database file!
1284 # Also wipes the database
1285 setup_samdb_partitions(path, logger=logger, lp=lp,
1286 provision_backend=provision_backend, session_info=session_info,
1287 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1288 backend_store=backend_store)
1290 # Load the database, but don's load the global schema and don't connect
1292 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1293 credentials=provision_backend.credentials, lp=lp,
1294 global_schema=False, am_rodc=am_rodc)
1296 logger.info("Pre-loading the Samba 4 and AD schema")
1298 # Load the schema from the one we computed earlier
1299 samdb.set_schema(schema, write_indices_and_attributes=False)
1301 # Set the NTDS settings DN manually - in order to have it already around
1302 # before the provisioned tree exists and we connect
1303 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1305 # And now we can connect to the DB - the schema won't be loaded from the
1309 except ldb.LdbError as e2:
1310 (num, string_error) = e2.args
1311 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1312 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1316 # But we have to give it one more kick to have it use the schema
1317 # during provision - it needs, now that it is connected, to write
1318 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1319 samdb.set_schema(schema, write_indices_and_attributes=True)
1324 def fill_samdb(samdb, lp, names, logger, policyguid,
1325 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1326 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1327 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1328 backend_store=None):
1330 if next_rid is None:
1333 # Provision does not make much sense values larger than 1000000000
1334 # as the upper range of the rIDAvailablePool is 1073741823 and
1335 # we don't want to create a domain that cannot allocate rids.
1336 if next_rid < 1000 or next_rid > 1000000000:
1337 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1338 error += "the valid range is %u-%u. The default is %u." % (
1339 1000, 1000000000, 1000)
1340 raise ProvisioningError(error)
1342 # ATTENTION: Do NOT change these default values without discussion with the
1343 # team and/or release manager. They have a big impact on the whole program!
1344 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1346 if dom_for_fun_level is None:
1347 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1349 if dom_for_fun_level > domainControllerFunctionality:
1350 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!")
1352 domainFunctionality = dom_for_fun_level
1353 forestFunctionality = dom_for_fun_level
1355 # Set the NTDS settings DN manually - in order to have it already around
1356 # before the provisioned tree exists and we connect
1357 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1359 # Set the domain functionality levels onto the database.
1360 # Various module (the password_hash module in particular) need
1361 # to know what level of AD we are emulating.
1363 # These will be fixed into the database via the database
1364 # modifictions below, but we need them set from the start.
1365 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1366 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1367 samdb.set_opaque_integer("domainControllerFunctionality",
1368 domainControllerFunctionality)
1370 samdb.set_domain_sid(str(names.domainsid))
1371 samdb.set_invocation_id(invocationid)
1373 logger.info("Adding DomainDN: %s" % names.domaindn)
1375 # impersonate domain admin
1376 admin_session_info = admin_session(lp, str(names.domainsid))
1377 samdb.set_session_info(admin_session_info)
1378 if names.domainguid is not None:
1379 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1381 domainguid_line = ""
1383 descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1384 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1385 "DOMAINDN": names.domaindn,
1386 "DOMAINSID": str(names.domainsid),
1387 "DESCRIPTOR": descr,
1388 "DOMAINGUID": domainguid_line
1391 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1392 "DOMAINDN": names.domaindn,
1393 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1394 "NEXTRID": str(next_rid),
1395 "DEFAULTSITE": names.sitename,
1396 "CONFIGDN": names.configdn,
1397 "POLICYGUID": policyguid,
1398 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1399 "SAMBA_VERSION_STRING": version,
1400 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1403 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1404 if fill == FILL_FULL:
1405 logger.info("Adding configuration container")
1406 descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1407 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1408 "CONFIGDN": names.configdn,
1409 "DESCRIPTOR": descr,
1412 # The LDIF here was created when the Schema object was constructed
1413 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1414 logger.info("Setting up sam.ldb schema")
1415 samdb.add_ldif(schema.schema_dn_add,
1416 controls=["relax:0", ignore_checks_oid])
1417 samdb.modify_ldif(schema.schema_dn_modify,
1418 controls=[ignore_checks_oid])
1419 samdb.write_prefixes_from_schema()
1420 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1421 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1422 {"SCHEMADN": names.schemadn},
1423 controls=["relax:0", ignore_checks_oid])
1425 # Now register this container in the root of the forest
1426 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1427 msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
1430 samdb.invocation_id = invocationid
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 logger.info("Setting up sam.ldb configuration data")
1436 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1437 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1438 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1439 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1440 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1441 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1443 if "2008" in schema.base_schema:
1444 # exclude 2012-specific changes if we're using a 2008 schema
1449 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1450 "CONFIGDN": names.configdn,
1451 "NETBIOSNAME": names.netbiosname,
1452 "DEFAULTSITE": names.sitename,
1453 "DNSDOMAIN": names.dnsdomain,
1454 "DOMAIN": names.domain,
1455 "SCHEMADN": names.schemadn,
1456 "DOMAINDN": names.domaindn,
1457 "SERVERDN": names.serverdn,
1458 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1459 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1460 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1461 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1462 "SERVICES_DESCRIPTOR": protected1_descr,
1463 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1464 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1465 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1466 "PARTITIONS_DESCRIPTOR": partitions_descr,
1467 "SITES_DESCRIPTOR": sites_descr,
1470 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1471 "CONFIGDN": names.configdn,
1472 "INC2012" : incl_2012,
1475 logger.info("Setting up display specifiers")
1476 display_specifiers_ldif = read_ms_ldif(
1477 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1478 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1479 {"CONFIGDN": names.configdn})
1480 check_all_substituted(display_specifiers_ldif)
1481 samdb.add_ldif(display_specifiers_ldif)
1483 logger.info("Modifying display specifiers and extended rights")
1484 setup_modify_ldif(samdb,
1485 setup_path("provision_configuration_modify.ldif"), {
1486 "CONFIGDN": names.configdn,
1487 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1490 logger.info("Adding users container")
1491 users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1492 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1493 "DOMAINDN": names.domaindn,
1494 "USERS_DESCRIPTOR": users_desc
1496 logger.info("Modifying users container")
1497 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1498 "DOMAINDN": names.domaindn})
1499 logger.info("Adding computers container")
1500 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1501 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1502 "DOMAINDN": names.domaindn,
1503 "COMPUTERS_DESCRIPTOR": computers_desc
1505 logger.info("Modifying computers container")
1506 setup_modify_ldif(samdb,
1507 setup_path("provision_computers_modify.ldif"), {
1508 "DOMAINDN": names.domaindn})
1509 logger.info("Setting up sam.ldb data")
1510 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1511 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1512 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1513 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1514 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1515 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1516 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1517 "DOMAINDN": names.domaindn,
1518 "NETBIOSNAME": names.netbiosname,
1519 "DEFAULTSITE": names.sitename,
1520 "CONFIGDN": names.configdn,
1521 "SERVERDN": names.serverdn,
1522 "RIDAVAILABLESTART": str(next_rid + 600),
1523 "POLICYGUID_DC": policyguid_dc,
1524 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1525 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1526 "SYSTEM_DESCRIPTOR": system_desc,
1527 "BUILTIN_DESCRIPTOR": builtin_desc,
1528 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1531 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1532 if fill == FILL_FULL:
1533 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1534 setup_modify_ldif(samdb,
1535 setup_path("provision_configuration_references.ldif"), {
1536 "CONFIGDN": names.configdn,
1537 "SCHEMADN": names.schemadn})
1539 logger.info("Setting up well known security principals")
1540 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1541 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1542 "CONFIGDN": names.configdn,
1543 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1544 }, controls=["relax:0", "provision:0"])
1546 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1547 setup_modify_ldif(samdb,
1548 setup_path("provision_basedn_references.ldif"), {
1549 "DOMAINDN": names.domaindn,
1550 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1553 logger.info("Setting up sam.ldb users and groups")
1554 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1555 "DOMAINDN": names.domaindn,
1556 "DOMAINSID": str(names.domainsid),
1557 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1558 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1559 }, controls=["relax:0", "provision:0"])
1561 logger.info("Setting up self join")
1562 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1563 invocationid=invocationid,
1564 dns_backend=dns_backend,
1566 machinepass=machinepass,
1567 domainsid=names.domainsid,
1570 policyguid=policyguid,
1571 policyguid_dc=policyguid_dc,
1572 domainControllerFunctionality=domainControllerFunctionality,
1575 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1576 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1577 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1578 assert isinstance(names.ntdsguid, str)
1583 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1584 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)"
1585 SYSVOL_SERVICE = "sysvol"
1588 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1589 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1590 for root, dirs, files in os.walk(path, topdown=False):
1592 setntacl(lp, os.path.join(root, name), acl, domsid,
1593 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1595 setntacl(lp, os.path.join(root, name), acl, domsid,
1596 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1599 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1600 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1603 :param sysvol: Physical path for the sysvol folder
1604 :param dnsdomain: The DNS name of the domain
1605 :param domainsid: The SID of the domain
1606 :param domaindn: The DN of the domain (ie. DC=...)
1607 :param samdb: An LDB object on the SAM db
1608 :param lp: an LP object
1611 # Set ACL for GPO root folder
1612 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1613 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1614 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1616 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1617 attrs=["cn", "nTSecurityDescriptor"],
1618 expression="", scope=ldb.SCOPE_ONELEVEL)
1621 acl = ndr_unpack(security.descriptor,
1622 str(policy["nTSecurityDescriptor"])).as_sddl()
1623 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1624 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1625 str(domainsid), use_ntvfs,
1629 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1630 domaindn, lp, use_ntvfs):
1631 """Set the ACL for the sysvol share and the subfolders
1633 :param samdb: An LDB object on the SAM db
1634 :param netlogon: Physical path for the netlogon folder
1635 :param sysvol: Physical path for the sysvol folder
1636 :param uid: The UID of the "Administrator" user
1637 :param gid: The GID of the "Domain adminstrators" group
1638 :param domainsid: The SID of the domain
1639 :param dnsdomain: The DNS name of the domain
1640 :param domaindn: The DN of the domain (ie. DC=...)
1645 s3conf = s3param.get_context()
1646 s3conf.load(lp.configfile)
1648 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1651 smbd.set_simple_acl(file.name, 0o755, gid)
1653 if not smbd.have_posix_acls():
1654 # This clue is only strictly correct for RPM and
1655 # Debian-like Linux systems, but hopefully other users
1656 # will get enough clue from it.
1657 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1658 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1660 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1661 "Try the mounting the filesystem with the 'acl' option.")
1663 smbd.chown(file.name, uid, gid)
1665 raise ProvisioningError("Unable to chown a file on your filesystem. "
1666 "You may not be running provision as root.")
1670 # This will ensure that the smbd code we are running when setting ACLs
1671 # is initialised with the smb.conf
1672 s3conf = s3param.get_context()
1673 s3conf.load(lp.configfile)
1674 # ensure we are using the right samba_dsdb passdb backend, no matter what
1675 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1676 passdb.reload_static_pdb()
1678 # ensure that we init the samba_dsdb backend, so the domain sid is
1679 # marked in secrets.tdb
1680 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1682 # now ensure everything matches correctly, to avoid wierd issues
1683 if passdb.get_global_sam_sid() != domainsid:
1684 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))
1686 domain_info = s4_passdb.domain_info()
1687 if domain_info["dom_sid"] != domainsid:
1688 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))
1690 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1691 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()))
1696 os.chown(sysvol, -1, gid)
1702 # use admin sid dn as user dn, since admin should own most of the files,
1703 # the operation will be much faster
1704 userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1706 flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1707 auth.AUTH_SESSION_INFO_AUTHENTICATED |
1708 auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1710 session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1711 session_info_flags=flags)
1713 def _setntacl(path):
1714 """A helper to reuse args"""
1716 lp, path, SYSVOL_ACL, str(domainsid),
1717 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
1718 service=SYSVOL_SERVICE, session_info=session_info)
1720 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1722 for root, dirs, files in os.walk(sysvol, topdown=False):
1724 if use_ntvfs and canchown:
1725 os.chown(os.path.join(root, name), -1, gid)
1726 _setntacl(os.path.join(root, name))
1728 if use_ntvfs and canchown:
1729 os.chown(os.path.join(root, name), -1, gid)
1730 _setntacl(os.path.join(root, name))
1732 # Set acls on Policy folder and policies folders
1733 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1736 def acl_type(direct_db_access):
1737 if direct_db_access:
1743 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1744 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1745 fsacl_sddl = fsacl.as_sddl(domainsid)
1746 if fsacl_sddl != acl:
1747 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))
1749 for root, dirs, files in os.walk(path, topdown=False):
1751 fsacl = getntacl(lp, os.path.join(root, name),
1752 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1754 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1755 fsacl_sddl = fsacl.as_sddl(domainsid)
1756 if fsacl_sddl != acl:
1757 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))
1760 fsacl = getntacl(lp, os.path.join(root, name),
1761 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1763 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1764 fsacl_sddl = fsacl.as_sddl(domainsid)
1765 if fsacl_sddl != acl:
1766 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))
1769 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1771 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1774 :param sysvol: Physical path for the sysvol folder
1775 :param dnsdomain: The DNS name of the domain
1776 :param domainsid: The SID of the domain
1777 :param domaindn: The DN of the domain (ie. DC=...)
1778 :param samdb: An LDB object on the SAM db
1779 :param lp: an LP object
1782 # Set ACL for GPO root folder
1783 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1784 fsacl = getntacl(lp, root_policy_path,
1785 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1787 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1788 fsacl_sddl = fsacl.as_sddl(domainsid)
1789 if fsacl_sddl != POLICIES_ACL:
1790 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))
1791 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1792 attrs=["cn", "nTSecurityDescriptor"],
1793 expression="", scope=ldb.SCOPE_ONELEVEL)
1796 acl = ndr_unpack(security.descriptor,
1797 str(policy["nTSecurityDescriptor"])).as_sddl()
1798 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1799 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1800 domainsid, direct_db_access)
1803 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1805 """Set the ACL for the sysvol share and the subfolders
1807 :param samdb: An LDB object on the SAM db
1808 :param netlogon: Physical path for the netlogon folder
1809 :param sysvol: Physical path for the sysvol folder
1810 :param uid: The UID of the "Administrator" user
1811 :param gid: The GID of the "Domain adminstrators" group
1812 :param domainsid: The SID of the domain
1813 :param dnsdomain: The DNS name of the domain
1814 :param domaindn: The DN of the domain (ie. DC=...)
1817 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1818 s3conf = s3param.get_context()
1819 s3conf.load(lp.configfile)
1820 # ensure we are using the right samba_dsdb passdb backend, no matter what
1821 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1822 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1823 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1825 # now ensure everything matches correctly, to avoid wierd issues
1826 if passdb.get_global_sam_sid() != domainsid:
1827 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))
1829 domain_info = s4_passdb.domain_info()
1830 if domain_info["dom_sid"] != domainsid:
1831 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))
1833 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1834 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()))
1836 # Ensure we can read this directly, and via the smbd VFS
1837 for direct_db_access in [True, False]:
1838 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1839 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1840 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1842 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1843 fsacl_sddl = fsacl.as_sddl(domainsid)
1844 if fsacl_sddl != SYSVOL_ACL:
1845 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))
1847 # Check acls on Policy folder and policies folders
1848 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1852 def interface_ips_v4(lp):
1853 """return only IPv4 IPs"""
1854 ips = samba.interface_ips(lp, False)
1857 if i.find(':') == -1:
1862 def interface_ips_v6(lp):
1863 """return only IPv6 IPs"""
1864 ips = samba.interface_ips(lp, False)
1867 if i.find(':') != -1:
1872 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1874 targetdir=None, samdb_fill=FILL_FULL,
1875 hostip=None, hostip6=None,
1876 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1877 domainguid=None, policyguid=None, policyguid_dc=None,
1878 invocationid=None, machinepass=None, ntdsguid=None,
1879 dns_backend=None, dnspass=None,
1880 serverrole=None, dom_for_fun_level=None,
1881 am_rodc=False, lp=None, use_ntvfs=False,
1882 skip_sysvolacl=False, backend_store=None):
1883 # create/adapt the group policy GUIDs
1884 # Default GUID for default policy are described at
1885 # "How Core Group Policy Works"
1886 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1887 if policyguid is None:
1888 policyguid = DEFAULT_POLICY_GUID
1889 policyguid = policyguid.upper()
1890 if policyguid_dc is None:
1891 policyguid_dc = DEFAULT_DC_POLICY_GUID
1892 policyguid_dc = policyguid_dc.upper()
1894 if invocationid is None:
1895 invocationid = str(uuid.uuid4())
1897 if krbtgtpass is None:
1898 krbtgtpass = samba.generate_random_machine_password(128, 255)
1899 if machinepass is None:
1900 machinepass = samba.generate_random_machine_password(128, 255)
1902 dnspass = samba.generate_random_password(128, 255)
1904 samdb.transaction_start()
1906 samdb = fill_samdb(samdb, lp, names, logger=logger,
1908 policyguid=policyguid, policyguid_dc=policyguid_dc,
1909 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1910 invocationid=invocationid, machinepass=machinepass,
1911 dns_backend=dns_backend, dnspass=dnspass,
1912 ntdsguid=ntdsguid, serverrole=serverrole,
1913 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1914 next_rid=next_rid, dc_rid=dc_rid,
1915 backend_store=backend_store)
1917 # Set up group policies (domain policy and domain controller
1919 if serverrole == "active directory domain controller":
1920 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1923 samdb.transaction_cancel()
1926 samdb.transaction_commit()
1928 if serverrole == "active directory domain controller":
1929 # Continue setting up sysvol for GPO. This appears to require being
1930 # outside a transaction.
1931 if not skip_sysvolacl:
1932 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1933 paths.root_gid, names.domainsid, names.dnsdomain,
1934 names.domaindn, lp, use_ntvfs)
1936 logger.info("Setting acl on sysvol skipped")
1938 secretsdb_self_join(secrets_ldb, domain=names.domain,
1939 realm=names.realm, dnsdomain=names.dnsdomain,
1940 netbiosname=names.netbiosname, domainsid=names.domainsid,
1941 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1943 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1944 # In future, this might be determined from some configuration
1945 kerberos_enctypes = str(ENC_ALL_TYPES)
1948 msg = ldb.Message(ldb.Dn(samdb,
1949 samdb.searchone("distinguishedName",
1950 expression="samAccountName=%s$" % names.netbiosname,
1951 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1952 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1953 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1954 name="msDS-SupportedEncryptionTypes")
1956 except ldb.LdbError as e:
1957 (enum, estr) = e.args
1958 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1959 # It might be that this attribute does not exist in this schema
1962 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1963 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1964 dnspass=dnspass, os_level=dom_for_fun_level,
1965 targetdir=targetdir, fill_level=samdb_fill,
1966 backend_store=backend_store)
1968 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1969 attribute="objectGUID")
1970 assert isinstance(domainguid, str)
1972 lastProvisionUSNs = get_last_provision_usn(samdb)
1973 maxUSN = get_max_usn(samdb, str(names.rootdn))
1974 if lastProvisionUSNs is not None:
1975 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1977 set_provision_usn(samdb, 0, maxUSN, invocationid)
1979 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1980 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1981 {'NTDSGUID': names.ntdsguid})
1983 # fix any dangling GUIDs from the provision
1984 logger.info("Fixing provision GUIDs")
1985 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1987 samdb.transaction_start()
1989 # a small number of GUIDs are missing because of ordering issues in the
1991 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1992 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1993 scope=ldb.SCOPE_BASE,
1994 attrs=['defaultObjectCategory'])
1995 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1996 scope=ldb.SCOPE_ONELEVEL,
1997 attrs=['ipsecOwnersReference',
1998 'ipsecFilterReference',
1999 'ipsecISAKMPReference',
2000 'ipsecNegotiationPolicyReference',
2001 'ipsecNFAReference'])
2002 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
2003 attrs=['attributeId', 'governsId']) != 0:
2004 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2006 samdb.transaction_cancel()
2009 samdb.transaction_commit()
2013 "ROLE_STANDALONE": "standalone server",
2014 "ROLE_DOMAIN_MEMBER": "member server",
2015 "ROLE_DOMAIN_BDC": "active directory domain controller",
2016 "ROLE_DOMAIN_PDC": "active directory domain controller",
2017 "dc": "active directory domain controller",
2018 "member": "member server",
2019 "domain controller": "active directory domain controller",
2020 "active directory domain controller": "active directory domain controller",
2021 "member server": "member server",
2022 "standalone": "standalone server",
2023 "standalone server": "standalone server",
2027 def sanitize_server_role(role):
2028 """Sanitize a server role name.
2030 :param role: Server role
2031 :raise ValueError: If the role can not be interpreted
2032 :return: Sanitized server role (one of "member server",
2033 "active directory domain controller", "standalone server")
2036 return _ROLES_MAP[role]
2038 raise ValueError(role)
2041 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2043 """Create AD entries for the fake ypserver.
2045 This is needed for being able to manipulate posix attrs via ADUC.
2047 samdb.transaction_start()
2049 logger.info("Setting up fake yp server settings")
2050 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2051 "DOMAINDN": domaindn,
2052 "NETBIOSNAME": netbiosname,
2053 "NISDOMAIN": nisdomain,
2056 samdb.transaction_cancel()
2059 samdb.transaction_commit()
2062 def directory_create_or_exists(path, mode=0o755):
2063 if not os.path.exists(path):
2065 os.mkdir(path, mode)
2066 except OSError as e:
2067 if e.errno in [errno.EEXIST]:
2070 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2073 def determine_host_ip(logger, lp, hostip=None):
2075 logger.info("Looking up IPv4 addresses")
2076 hostips = interface_ips_v4(lp)
2077 if len(hostips) > 0:
2079 if len(hostips) > 1:
2080 logger.warning("More than one IPv4 address found. Using %s",
2082 if hostip == "127.0.0.1":
2085 logger.warning("No IPv4 address will be assigned")
2090 def determine_host_ip6(logger, lp, hostip6=None):
2092 logger.info("Looking up IPv6 addresses")
2093 hostips = interface_ips_v6(lp)
2095 hostip6 = hostips[0]
2096 if len(hostips) > 1:
2097 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2099 logger.warning("No IPv6 address will be assigned")
2104 def provision(logger, session_info, smbconf=None,
2105 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2106 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2107 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2108 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2109 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2110 dns_backend=None, dns_forwarder=None, dnspass=None,
2111 invocationid=None, machinepass=None, ntdsguid=None,
2112 root=None, nobody=None, users=None, backup=None, aci=None,
2113 serverrole=None, dom_for_fun_level=None, backend_type=None,
2114 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2115 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2116 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2117 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2118 ldap_backend_extra_port=None, base_schema=None,
2119 plaintext_secrets=False, backend_store=None):
2122 :note: caution, this wipes all existing data!
2126 serverrole = sanitize_server_role(serverrole)
2128 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2130 if ldapadminpass is None:
2131 # Make a new, random password between Samba and it's LDAP server
2132 ldapadminpass = samba.generate_random_password(128, 255)
2134 if backend_type is None:
2135 backend_type = "ldb"
2136 if backend_store is None:
2137 backend_store = get_default_backend_store()
2139 if domainsid is None:
2140 domainsid = security.random_sid()
2142 root_uid = findnss_uid([root or "root"])
2143 nobody_uid = findnss_uid([nobody or "nobody"])
2144 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2145 root_gid = pwd.getpwuid(root_uid).pw_gid
2148 bind_gid = findnss_gid(["bind", "named"])
2152 if targetdir is not None:
2153 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2154 elif smbconf is None:
2155 smbconf = samba.param.default_path()
2156 if not os.path.exists(os.path.dirname(smbconf)):
2157 os.makedirs(os.path.dirname(smbconf))
2159 server_services = []
2162 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2164 if dns_backend != "SAMBA_INTERNAL":
2165 server_services.append("-dns")
2167 if dns_forwarder is not None:
2168 global_param["dns forwarder"] = [dns_forwarder]
2171 server_services.append("+smb")
2172 server_services.append("-s3fs")
2173 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2175 if len(server_services) > 0:
2176 global_param["server services"] = server_services
2178 # only install a new smb.conf if there isn't one there already
2179 if os.path.exists(smbconf):
2180 # if Samba Team members can't figure out the weird errors
2181 # loading an empty smb.conf gives, then we need to be smarter.
2182 # Pretend it just didn't exist --abartlet
2183 f = open(smbconf, 'r')
2185 data = f.read().lstrip()
2188 if data is None or data == "":
2189 make_smbconf(smbconf, hostname, domain, realm,
2190 targetdir, serverrole=serverrole,
2191 eadb=useeadb, use_ntvfs=use_ntvfs,
2192 lp=lp, global_param=global_param)
2194 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2195 serverrole=serverrole,
2196 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2199 lp = samba.param.LoadParm()
2201 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2202 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2203 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2204 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2205 paths = provision_paths_from_lp(lp, names.dnsdomain)
2207 paths.bind_gid = bind_gid
2208 paths.root_uid = root_uid;
2209 paths.root_gid = root_gid
2211 hostip = determine_host_ip(logger, lp, hostip)
2212 hostip6 = determine_host_ip6(logger, lp, hostip6)
2213 names.hostip = hostip
2214 names.hostip6 = hostip6
2215 names.domainguid = domainguid
2216 names.domainsid = domainsid
2217 names.forestsid = domainsid
2219 if serverrole is None:
2220 serverrole = lp.get("server role")
2222 directory_create_or_exists(paths.private_dir, 0o700)
2223 directory_create_or_exists(paths.binddns_dir, 0o770)
2224 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2225 directory_create_or_exists(paths.state_dir)
2226 if not plaintext_secrets:
2227 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2229 if paths.sysvol and not os.path.exists(paths.sysvol):
2230 os.makedirs(paths.sysvol, 0o775)
2232 ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
2234 schema = Schema(domainsid, invocationid=invocationid,
2235 schemadn=names.schemadn, base_schema=base_schema)
2237 if backend_type == "ldb":
2238 provision_backend = LDBBackend(backend_type, paths=paths,
2240 names=names, logger=logger)
2241 elif backend_type == "existing":
2242 # If support for this is ever added back, then the URI will need to be
2244 provision_backend = ExistingBackend(backend_type, paths=paths,
2246 names=names, logger=logger,
2247 ldap_backend_forced_uri=ldap_backend_forced_uri)
2248 elif backend_type == "fedora-ds":
2249 provision_backend = FDSBackend(backend_type, paths=paths,
2251 names=names, logger=logger, domainsid=domainsid,
2252 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2253 slapd_path=slapd_path,
2255 elif backend_type == "openldap":
2256 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2258 names=names, logger=logger, domainsid=domainsid,
2259 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2260 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2261 ldap_backend_extra_port=ldap_backend_extra_port,
2262 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2263 ldap_backend_forced_uri=ldap_backend_forced_uri)
2265 raise ValueError("Unknown LDAP backend type selected")
2267 provision_backend.init()
2268 provision_backend.start()
2270 # only install a new shares config db if there is none
2271 if not os.path.exists(paths.shareconf):
2272 logger.info("Setting up share.ldb")
2273 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2274 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2276 logger.info("Setting up secrets.ldb")
2277 secrets_ldb = setup_secretsdb(paths,
2278 session_info=session_info,
2279 backend_credentials=provision_backend.credentials, lp=lp)
2282 logger.info("Setting up the registry")
2283 setup_registry(paths.hklm, session_info, lp=lp)
2285 logger.info("Setting up the privileges database")
2286 setup_privileges(paths.privilege, session_info, lp=lp)
2288 logger.info("Setting up idmap db")
2289 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2291 setup_name_mappings(idmap, sid=str(domainsid),
2292 root_uid=root_uid, nobody_uid=nobody_uid,
2293 users_gid=users_gid, root_gid=root_gid)
2295 logger.info("Setting up SAM db")
2296 samdb = setup_samdb(paths.samdb, session_info,
2297 provision_backend, lp, names, logger=logger,
2298 serverrole=serverrole,
2299 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2300 plaintext_secrets=plaintext_secrets,
2301 backend_store=backend_store)
2303 if serverrole == "active directory domain controller":
2304 if paths.netlogon is None:
2305 raise MissingShareError("netlogon", paths.smbconf)
2307 if paths.sysvol is None:
2308 raise MissingShareError("sysvol", paths.smbconf)
2310 if not os.path.isdir(paths.netlogon):
2311 os.makedirs(paths.netlogon, 0o755)
2313 if adminpass is None:
2314 adminpass = samba.generate_random_password(12, 32)
2315 adminpass_generated = True
2317 adminpass = unicode(adminpass, 'utf-8')
2318 adminpass_generated = False
2320 if samdb_fill == FILL_FULL:
2321 provision_fill(samdb, secrets_ldb, logger, names, paths,
2322 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2323 hostip=hostip, hostip6=hostip6,
2324 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2325 krbtgtpass=krbtgtpass,
2326 policyguid=policyguid, policyguid_dc=policyguid_dc,
2327 invocationid=invocationid, machinepass=machinepass,
2328 ntdsguid=ntdsguid, dns_backend=dns_backend,
2329 dnspass=dnspass, serverrole=serverrole,
2330 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2331 lp=lp, use_ntvfs=use_ntvfs,
2332 skip_sysvolacl=skip_sysvolacl,
2333 backend_store=backend_store)
2335 if not is_heimdal_built():
2336 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2337 logger.info("The Kerberos KDC configuration for Samba AD is "
2338 "located at %s", paths.kdcconf)
2340 create_krb5_conf(paths.krb5conf,
2341 dnsdomain=names.dnsdomain, hostname=names.hostname,
2343 logger.info("A Kerberos configuration suitable for Samba AD has been "
2344 "generated at %s", paths.krb5conf)
2345 logger.info("Merge the contents of this file with your system "
2346 "krb5.conf or replace it with this one. Do not create a "
2349 if serverrole == "active directory domain controller":
2350 create_dns_update_list(lp, logger, paths)
2352 backend_result = provision_backend.post_setup()
2353 provision_backend.shutdown()
2356 secrets_ldb.transaction_cancel()
2359 # Now commit the secrets.ldb to disk
2360 secrets_ldb.transaction_commit()
2362 # the commit creates the dns.keytab in the private directory
2363 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2364 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2366 if os.path.isfile(private_dns_keytab_path):
2367 if os.path.isfile(bind_dns_keytab_path):
2369 os.unlink(bind_dns_keytab_path)
2370 except OSError as e:
2371 logger.error("Failed to remove %s: %s" %
2372 (bind_dns_keytab_path, e.strerror))
2374 # link the dns.keytab to the bind-dns directory
2376 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2377 except OSError as e:
2378 logger.error("Failed to create link %s -> %s: %s" %
2379 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2381 # chown the dns.keytab in the bind-dns directory
2382 if paths.bind_gid is not None:
2384 os.chmod(paths.binddns_dir, 0o770)
2385 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2387 if 'SAMBA_SELFTEST' not in os.environ:
2388 logger.info("Failed to chown %s to bind gid %u",
2389 paths.binddns_dir, paths.bind_gid)
2392 os.chmod(bind_dns_keytab_path, 0o640)
2393 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2395 if 'SAMBA_SELFTEST' not in os.environ:
2396 logger.info("Failed to chown %s to bind gid %u",
2397 bind_dns_keytab_path, paths.bind_gid)
2399 result = ProvisionResult()
2400 result.server_role = serverrole
2401 result.domaindn = domaindn
2402 result.paths = paths
2403 result.names = names
2405 result.samdb = samdb
2406 result.idmap = idmap
2407 result.domainsid = str(domainsid)
2409 if samdb_fill == FILL_FULL:
2410 result.adminpass_generated = adminpass_generated
2411 result.adminpass = adminpass
2413 result.adminpass_generated = False
2414 result.adminpass = None
2416 result.backend_result = backend_result
2419 provision_fake_ypserver(logger=logger, samdb=samdb,
2420 domaindn=names.domaindn, netbiosname=names.netbiosname,
2421 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2426 def provision_become_dc(smbconf=None, targetdir=None,
2427 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2428 serverdn=None, domain=None, hostname=None, domainsid=None,
2429 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2430 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2431 dns_backend=None, root=None, nobody=None, users=None,
2432 backup=None, serverrole=None, ldap_backend=None,
2433 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2435 logger = logging.getLogger("provision")
2436 samba.set_debug_level(debuglevel)
2438 res = provision(logger, system_session(),
2439 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2440 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2441 configdn=configdn, serverdn=serverdn, domain=domain,
2442 hostname=hostname, hostip=None, domainsid=domainsid,
2443 machinepass=machinepass,
2444 serverrole="active directory domain controller",
2445 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2446 use_ntvfs=use_ntvfs)
2447 res.lp.set("debuglevel", str(debuglevel))
2451 def create_krb5_conf(path, dnsdomain, hostname, realm):
2452 """Write out a file containing a valid krb5.conf file
2454 :param path: Path of the new krb5.conf file.
2455 :param dnsdomain: DNS Domain name
2456 :param hostname: Local hostname
2457 :param realm: Realm name
2459 setup_file(setup_path("krb5.conf"), path, {
2460 "DNSDOMAIN": dnsdomain,
2461 "HOSTNAME": hostname,
2466 class ProvisioningError(Exception):
2467 """A generic provision error."""
2469 def __init__(self, value):
2473 return "ProvisioningError: " + self.value
2476 class InvalidNetbiosName(Exception):
2477 """A specified name was not a valid NetBIOS name."""
2479 def __init__(self, name):
2480 super(InvalidNetbiosName, self).__init__(
2481 "The name '%r' is not a valid NetBIOS name" % name)
2484 class MissingShareError(ProvisioningError):
2486 def __init__(self, name, smbconf):
2487 super(MissingShareError, self).__init__(
2488 "Existing smb.conf does not have a [%s] share, but you are "
2489 "configuring a DC. Please remove %s or add the share manually." %